From fe8d53d9b84ed72a1ab43ca386f0eb6d3b7b7ee1 Mon Sep 17 00:00:00 2001 From: PC Date: Sun, 8 Feb 2026 18:20:08 +0800 Subject: [PATCH 1/6] fix: fixed eslint issues --- actions/(announcements)/resend.ts | 4 +- actions/(calendar)/calendar.ts | 21 +++-- actions/(events)/event.ts | 92 +++++++++++-------- actions/(events)/google.ts | 13 ++- app/api/webhooks/resend/route.ts | 16 ++-- app/dashboard/components/NewTeamBtn.tsx | 2 +- .../components/team-table/ActionsMenu.tsx | 1 - app/dashboard/layout.tsx | 8 +- app/dashboard/page.tsx | 4 +- .../components/AnnouncementCard.tsx | 4 +- .../components/AnnouncementsClient.tsx | 4 +- .../components/CreateAnnouncementDialog.tsx | 2 +- .../(actions)/useCalendarActions.ts | 1 + .../(calendar)/components/CalendarToolbar.tsx | 14 +-- .../(calendar)/components/EventModal.tsx | 1 + .../components/ImportMembersModal.tsx | 7 +- .../(members)/components/MemberTable.tsx | 2 +- components/ui/AlertDialog.tsx | 2 - components/ui/DataTable.tsx | 2 +- lint-final.json | 1 + lint-results-updated.json | 1 + lint-results.json | 1 + public/fonts.ts | 2 +- .../functions/store-google-refresh/index.ts | 18 ++-- utils/supabase/proxy.ts | 10 +- 25 files changed, 121 insertions(+), 112 deletions(-) create mode 100644 lint-final.json create mode 100644 lint-results-updated.json create mode 100644 lint-results.json diff --git a/actions/(announcements)/resend.ts b/actions/(announcements)/resend.ts index a889b3c..8e246d9 100644 --- a/actions/(announcements)/resend.ts +++ b/actions/(announcements)/resend.ts @@ -51,7 +51,7 @@ export async function sendAnnouncementEmail({ }: SendEmailParams): Promise { // Validate all emails first const emails = recipients.map((r) => r.email); - const { valid, invalid } = validateEmails(emails); + const { invalid } = validateEmails(emails); if (invalid.length > 0) { throw new Error(`Invalid email addresses: ${invalid.join(", ")}`); @@ -106,7 +106,7 @@ export async function sendAnnouncementEmail({ /** * Query Resend API for batch status (for reconciliation) */ -export async function getBatchStatus(batchId: string) { +export async function getBatchStatus() { try { // Note: Resend doesn't have a direct batch status endpoint yet // This is a placeholder for future implementation diff --git a/actions/(calendar)/calendar.ts b/actions/(calendar)/calendar.ts index 84b04f2..1095e9f 100644 --- a/actions/(calendar)/calendar.ts +++ b/actions/(calendar)/calendar.ts @@ -35,11 +35,12 @@ export async function createTeamCalendar(teamName: string) { }); return { success: true, calendarId: response.data.id }; - } catch (error: any) { - console.error(`Error creating calendar: ${teamName}`, error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error(`Error creating calendar: ${teamName}`, message); return { success: false, - error: error?.message || `Failed to create calendar: ${teamName}`, + error: message || `Failed to create calendar: ${teamName}`, }; } } @@ -72,11 +73,12 @@ export async function updateTeamCalendar(teamId: string, teamName: string) { }); return { success: true, calendarId: response.data.id }; - } catch (error: any) { - console.error(`Error updating calendar: ${calendarId}`, error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error(`Error updating calendar: ${calendarId}`, message); return { success: false, - error: error?.message || `Failed to update calendar: ${calendarId}`, + error: message || `Failed to update calendar: ${calendarId}`, }; } } @@ -103,11 +105,12 @@ export async function deleteTeamCalendar(teamId: string) { }); return { success: true, response: response.data }; - } catch (error: any) { - console.error(`Error deleting calendar: ${calendarId}`, error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error(`Error deleting calendar: ${calendarId}`, message); return { success: false, - error: error?.message || `Failed to delete calendar: ${calendarId}`, + error: message || `Failed to delete calendar: ${calendarId}`, }; } } diff --git a/actions/(events)/event.ts b/actions/(events)/event.ts index 430761b..ee4ba55 100644 --- a/actions/(events)/event.ts +++ b/actions/(events)/event.ts @@ -1,6 +1,5 @@ "use server"; import { getCalendarClient } from "./google"; -import { CalendarEventSchema } from "@/lib/validations"; import { createCalendarEvent_DB, updateCalendarEvent_DB, @@ -34,11 +33,12 @@ export async function createCalendarEvent( throw new Error("User not authenticated"); } leader_id = idHelper; - } catch (error: any) { - console.error(`Error creating calendar: ${teamId}`, error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error(`Error creating calendar: ${teamId}`, message); return { success: false, - error: error?.message || `Failed to create calendar: ${teamId}`, + error: message || `Failed to create calendar: ${teamId}`, }; } @@ -99,8 +99,10 @@ export async function createCalendarEvent( eventDetails, calendarEventId, ); - } catch (dbError: any) { - console.error("DB Error. Rolling back Google Event...", dbError.message); + } catch (dbError: unknown) { + const dbMessage = + dbError instanceof Error ? dbError.message : String(dbError); + console.error("DB Error. Rolling back Google Event...", dbMessage); // Delete event from google calendar try { @@ -115,7 +117,7 @@ export async function createCalendarEvent( // TODO: Send alert to admin / deadlock queue } - throw new Error(`System Error: Failed to save event. ${dbError.message}`); + throw new Error(`System Error: Failed to save event. ${dbMessage}`); } return { @@ -123,11 +125,12 @@ export async function createCalendarEvent( eventId: response.data.id, eventLink: response.data.htmlLink, }; - } catch (error: any) { - console.error("Error creating event:", error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error("Error creating event:", message); return { success: false, - error: error?.message || "Failed to create event", + error: message || "Failed to create event", }; } } @@ -142,11 +145,12 @@ export async function getTeamEvents(teamId: string) { throw new Error("User not authenticated"); } leader_id = idHelper; - } catch (error: any) { - console.error(`Error in getTeamEvents: ${teamId}`, error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error(`Error in getTeamEvents: ${teamId}`, message); return { success: false, - error: error?.message || "Authentication failed", + error: message || "Authentication failed", }; } @@ -168,11 +172,12 @@ export async function getTeamEvents(teamId: string) { success: true, events: response.data.items, }; - } catch (error: any) { - console.error("Error getting events:", error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error("Error getting events:", message); return { success: false, - error: error?.message || "Failed to get events", + error: message || "Failed to get events", }; } } @@ -217,11 +222,12 @@ export async function updateCalendarEvent( throw new Error("User not authenticated"); } leader_id = idHelper; - } catch (error: any) { - console.error(`Error creating calendar: ${teamId}`, error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error(`Error creating calendar: ${teamId}`, message); return { success: false, - error: error?.message || `Failed to create calendar: ${teamId}`, + error: message || `Failed to create calendar: ${teamId}`, }; } @@ -280,15 +286,17 @@ export async function updateCalendarEvent( // Save changes to DB try { await updateCalendarEvent_DB(eventId, eventDetails); - } catch (dbError: any) { + } catch (dbError: unknown) { + const dbMessage = + dbError instanceof Error ? dbError.message : String(dbError); console.error( "❌ DB Consistency Failure during Update. Google Event updated, but DB failed.", - dbError.message, + dbMessage, ); // NOTE: Rolling back an update is complex (needs old data). // For now, we log the inconsistency. throw new Error( - `System Error: Failed to update event in DB. ${dbError.message}`, + `System Error: Failed to update event in DB. ${dbMessage}`, ); } @@ -297,11 +305,12 @@ export async function updateCalendarEvent( eventId: response.data.id, eventLink: response.data.htmlLink, }; - } catch (error: any) { - console.error("Error updating event:", error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error("Error updating event:", message); return { success: false, - error: error?.message || "Failed to update event", + error: message || "Failed to update event", }; } } @@ -315,11 +324,12 @@ export async function deleteCalendarEvent(teamId: string, eventId: string) { throw new Error("User not authenticated"); } leader_id = idHelper; - } catch (error: any) { - console.error(`Error in deleteCalendarEvent: ${teamId}`, error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error(`Error in deleteCalendarEvent: ${teamId}`, message); return { success: false, - error: error?.message || "Authentication failed", + error: message || "Authentication failed", }; } @@ -339,14 +349,13 @@ export async function deleteCalendarEvent(teamId: string, eventId: string) { where: { id: eventId }, select: { google_event_id: true }, }); - } catch (dbError: any) { - console.error( - "Database query failed in deleteCalendarEvent:", - dbError.message, - ); + } catch (dbError: unknown) { + const dbMessage = + dbError instanceof Error ? dbError.message : String(dbError); + console.error("Database query failed in deleteCalendarEvent:", dbMessage); return { success: false, - error: `Database error: ${dbError.message}`, + error: `Database error: ${dbMessage}`, }; } @@ -365,26 +374,29 @@ export async function deleteCalendarEvent(teamId: string, eventId: string) { }); try { await deleteCalendarEvent_DB(eventId); - } catch (dbError: any) { + } catch (dbError: unknown) { + const dbMessage = + dbError instanceof Error ? dbError.message : String(dbError); console.error( "❌ DB Consistency Failure during Delete. Google Event deleted, but DB failed (Zombie Data).", - dbError.message, + dbMessage, ); // NOTE: Rolling back a delete means RE-CREATING the event (new ID). // For now, we log the inconsistency. throw new Error( - `System Error: Failed to delete event in DB. ${dbError.message}`, + `System Error: Failed to delete event in DB. ${dbMessage}`, ); } return { success: true, }; - } catch (error: any) { - console.error("Error deleting event:", error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error("Error deleting event:", message); return { success: false, - error: error?.message || "Failed to delete event", + error: message || "Failed to delete event", }; } } diff --git a/actions/(events)/google.ts b/actions/(events)/google.ts index d7f4189..e64c219 100644 --- a/actions/(events)/google.ts +++ b/actions/(events)/google.ts @@ -44,13 +44,18 @@ export async function checkCalendarPermissions() { const { client } = await getCalendarClient(); await client.calendarList.list({ maxResults: 1 }); return { hasValidToken: true, needsReauth: false }; - } catch (error: any) { - console.error("Permission check failed:", error?.message); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : String(error); + console.error("Permission check failed:", message); // If any auth error, we need re-auth + const errorCode = (error as { code?: number })?.code; + const errorResponse = ( + error as { response?: { data?: { error?: string } } } + )?.response?.data?.error; const isAuthError = - [400, 401, 403].includes(error?.code) || - error?.response?.data?.error === "invalid_request"; + (errorCode !== undefined && [400, 401, 403].includes(errorCode)) || + errorResponse === "invalid_request"; if (isAuthError) { const user = await getUser(); diff --git a/app/api/webhooks/resend/route.ts b/app/api/webhooks/resend/route.ts index 71ab612..81c7bba 100644 --- a/app/api/webhooks/resend/route.ts +++ b/app/api/webhooks/resend/route.ts @@ -39,7 +39,7 @@ export async function POST(request: NextRequest) { // Verify webhook signature const wh = new Webhook(webhookSecret); - let payload: any; + let payload: unknown; try { payload = wh.verify(body, { @@ -53,7 +53,10 @@ export async function POST(request: NextRequest) { } // Parse verified payload - const { type, data } = payload; + const { type, data } = payload as { + type: string; + data: Record; + }; console.log("Received webhook:", type, data); @@ -61,13 +64,14 @@ export async function POST(request: NextRequest) { // Note: Resend's webhook structure may vary, adjust as needed const batchId = data?.batch_id || data?.id; - if (!batchId) { - console.warn("No batch ID in webhook payload"); + // Type guard: ensure batchId is a string + if (!batchId || typeof batchId !== "string") { + console.warn("Invalid or missing batch ID in webhook payload"); return NextResponse.json({ received: true }, { status: 200 }); } // Find announcement by batch ID - // Handle both single ID and JSON array of IDs + // Handle both exact match and JSON array containing the ID const announcements = await prisma.announcements.findMany({ where: { OR: [ @@ -102,7 +106,7 @@ export async function POST(request: NextRequest) { async function processWebhookEvent( announcementId: bigint, eventType: string, - eventData: any, + eventData: Record, ) { try { const announcement = await prisma.announcements.findUnique({ diff --git a/app/dashboard/components/NewTeamBtn.tsx b/app/dashboard/components/NewTeamBtn.tsx index a6fbae4..9250f36 100644 --- a/app/dashboard/components/NewTeamBtn.tsx +++ b/app/dashboard/components/NewTeamBtn.tsx @@ -14,7 +14,7 @@ import { Button } from "@/components/ui/Button"; import { Plus, Users } from "lucide-react"; import { createTeam } from "@/actions/teams"; import toast from "react-hot-toast"; -import { inter, instrumentSerif } from "@/app/fonts"; +import { instrumentSerif } from "@/app/fonts"; function NewTeamBtn() { const [teamName, setTeamName] = useState(""); diff --git a/app/dashboard/components/team-table/ActionsMenu.tsx b/app/dashboard/components/team-table/ActionsMenu.tsx index 3306ff4..273acf6 100644 --- a/app/dashboard/components/team-table/ActionsMenu.tsx +++ b/app/dashboard/components/team-table/ActionsMenu.tsx @@ -4,7 +4,6 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, - DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/DropdownMenu"; diff --git a/app/dashboard/layout.tsx b/app/dashboard/layout.tsx index db16efd..1491822 100644 --- a/app/dashboard/layout.tsx +++ b/app/dashboard/layout.tsx @@ -1,13 +1,7 @@ -import SignOutBtn from "./components/SignOutBtn"; export default function DashboardLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { - return ( -
- {/* */} - {children} -
- ); + return
{children}
; } diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 7717f3c..405e56f 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -1,4 +1,4 @@ -import { getUser, requireAuth } from "@/actions/auth"; +import { requireAuth } from "@/actions/auth"; import { getTeams } from "@/actions/teams"; import WelcomeMsg from "./components/WelcomeMsg"; import { inter } from "@/app/fonts"; @@ -18,7 +18,7 @@ export default async function Page() { const teams = await getTeams(); // Google auth info is in user.user_metadata - const { full_name, email, avatar_url, name } = user.user_metadata; + const { email, avatar_url, name } = user.user_metadata; return (
diff --git a/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementCard.tsx b/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementCard.tsx index 0cb4203..cd55aaa 100644 --- a/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementCard.tsx +++ b/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementCard.tsx @@ -47,7 +47,7 @@ export default function AnnouncementCard({ id: toastId, }); } - } catch (error) { + } catch { toast.error("An error occurred while sending", { id: toastId }); } finally { setIsSending(false); @@ -69,7 +69,7 @@ export default function AnnouncementCard({ id: toastId, }); } - } catch (error) { + } catch { toast.error("An error occurred while deleting", { id: toastId }); } finally { setIsDeleting(false); diff --git a/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementsClient.tsx b/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementsClient.tsx index 53f8ab2..2d26db4 100644 --- a/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementsClient.tsx +++ b/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementsClient.tsx @@ -46,7 +46,7 @@ export default function AnnouncementsClient({ } else { setError(result.error || "Failed to load announcements"); } - } catch (err) { + } catch { setError("An error occurred while loading announcements"); } finally { setIsLoading(false); @@ -55,7 +55,7 @@ export default function AnnouncementsClient({ useEffect(() => { loadAnnouncements(); - }, [teamId]); + }, [teamId, loadAnnouncements]); // Calculate pagination const totalPages = Math.ceil(announcements.length / ITEMS_PER_PAGE); diff --git a/app/dashboard/teams/[teamsId]/(announcements)/components/CreateAnnouncementDialog.tsx b/app/dashboard/teams/[teamsId]/(announcements)/components/CreateAnnouncementDialog.tsx index 909cbb6..f3e6858 100644 --- a/app/dashboard/teams/[teamsId]/(announcements)/components/CreateAnnouncementDialog.tsx +++ b/app/dashboard/teams/[teamsId]/(announcements)/components/CreateAnnouncementDialog.tsx @@ -63,7 +63,7 @@ export default function CreateAnnouncementDialog({ } else { toast.error(result.error || "Failed to create announcement"); } - } catch (error) { + } catch { toast.error("An error occurred"); } finally { setIsSubmitting(false); diff --git a/app/dashboard/teams/[teamsId]/(calendar)/components/(actions)/useCalendarActions.ts b/app/dashboard/teams/[teamsId]/(calendar)/components/(actions)/useCalendarActions.ts index 468d86f..b2d4373 100644 --- a/app/dashboard/teams/[teamsId]/(calendar)/components/(actions)/useCalendarActions.ts +++ b/app/dashboard/teams/[teamsId]/(calendar)/components/(actions)/useCalendarActions.ts @@ -49,6 +49,7 @@ export function useTeamCalendar(teamId: string) { }, [teamId]); // 5. Initial Load + // eslint-disable-next-line react-hooks/set-state-in-effect useEffect(() => { loadEvents(); }, [loadEvents]); diff --git a/app/dashboard/teams/[teamsId]/(calendar)/components/CalendarToolbar.tsx b/app/dashboard/teams/[teamsId]/(calendar)/components/CalendarToolbar.tsx index e69c6fa..1af6920 100644 --- a/app/dashboard/teams/[teamsId]/(calendar)/components/CalendarToolbar.tsx +++ b/app/dashboard/teams/[teamsId]/(calendar)/components/CalendarToolbar.tsx @@ -13,16 +13,8 @@ interface CalendarToolbarProps extends ToolbarProps { } export default function CalendarToolbar(props: CalendarToolbarProps) { - const { - date, - view, - views, - label, - onView, - onNavigate, - localizer, - onAddEvent, - } = props; + const { view, views, label, onView, onNavigate, localizer, onAddEvent } = + props; const navigate = (action: "PREV" | "NEXT" | "TODAY") => { onNavigate(action); @@ -37,8 +29,6 @@ export default function CalendarToolbar(props: CalendarToolbarProps) { const btnFirst = "rounded-l-md"; const btnLast = "rounded-r-md"; const btnMiddle = "-ml-px"; - const btnActive = - "bg-[#2563eb] text-white border-[#2563eb] hover:bg-[#1d4ed8] z-20"; return (
diff --git a/app/dashboard/teams/[teamsId]/(calendar)/components/EventModal.tsx b/app/dashboard/teams/[teamsId]/(calendar)/components/EventModal.tsx index 412db10..5742b52 100644 --- a/app/dashboard/teams/[teamsId]/(calendar)/components/EventModal.tsx +++ b/app/dashboard/teams/[teamsId]/(calendar)/components/EventModal.tsx @@ -45,6 +45,7 @@ export default function EventModal({ const [errors, setErrors] = useState>({}); // Sync state with event data when editing + // eslint-disable-next-line react-hooks/set-state-in-effect useEffect(() => { if (mode === "edit" && event) { setTitle(event.title); diff --git a/app/dashboard/teams/[teamsId]/(members)/components/ImportMembersModal.tsx b/app/dashboard/teams/[teamsId]/(members)/components/ImportMembersModal.tsx index 4a091c7..f8d6af4 100644 --- a/app/dashboard/teams/[teamsId]/(members)/components/ImportMembersModal.tsx +++ b/app/dashboard/teams/[teamsId]/(members)/components/ImportMembersModal.tsx @@ -2,7 +2,6 @@ import { useState, useRef } from "react"; import { AlertDialog, - AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogFooter, @@ -150,11 +149,9 @@ function ImportMembersModal({ setFileError(result.error || "Failed to import members"); // Don't close modal - let user try again } - } catch (error) { + } catch { toast.dismiss(); - setFileError( - error instanceof Error ? error.message : "Failed to import members", - ); + setFileError("Failed to import members"); // Don't close modal on error } finally { setIsLoading(false); diff --git a/app/dashboard/teams/[teamsId]/(members)/components/MemberTable.tsx b/app/dashboard/teams/[teamsId]/(members)/components/MemberTable.tsx index bd1ea48..b4b1591 100644 --- a/app/dashboard/teams/[teamsId]/(members)/components/MemberTable.tsx +++ b/app/dashboard/teams/[teamsId]/(members)/components/MemberTable.tsx @@ -79,7 +79,7 @@ export default function MemberTable({ teamId }: MemberTableProps) { setMembers(previousMembers); toast.error(result.error || "Failed to remove member"); } - } catch (error) { + } catch { // Rollback on error setMembers(previousMembers); toast.dismiss(); diff --git a/components/ui/AlertDialog.tsx b/components/ui/AlertDialog.tsx index 105ded2..8ee1671 100644 --- a/components/ui/AlertDialog.tsx +++ b/components/ui/AlertDialog.tsx @@ -4,8 +4,6 @@ import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; import * as React from "react"; -import { buttonVariants } from "@/components/ui/Button"; - import { cn } from "@/lib/utils"; function AlertDialog({ diff --git a/components/ui/DataTable.tsx b/components/ui/DataTable.tsx index 0ada630..bf7adc2 100644 --- a/components/ui/DataTable.tsx +++ b/components/ui/DataTable.tsx @@ -40,11 +40,11 @@ export interface DataTableProps { }>; } +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function DataTable>({ data, columns, searchKey, - searchPlaceholder = "Search...", emptyMessage = "No results found.", onRowClick, renderActions, diff --git a/lint-final.json b/lint-final.json new file mode 100644 index 0000000..c775942 --- /dev/null +++ b/lint-final.json @@ -0,0 +1 @@ +[{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(announcements)\\announcements.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(announcements)\\crud.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(announcements)\\resend.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'valid' is assigned a value but never used.","line":54,"column":11,"nodeType":null,"messageId":"unusedVar","endLine":54,"endColumn":16},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'batchId' is defined but never used.","line":109,"column":38,"nodeType":null,"messageId":"unusedVar","endLine":109,"endColumn":45}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { resend } from \"@/lib/resend\";\r\nimport { render } from \"@react-email/render\";\r\nimport { AnnouncementEmail } from \"@/components/email/announcement\";\r\nimport React from \"react\";\r\n\r\ninterface SendEmailParams {\r\n recipients: Array<{ email: string; name?: string }>;\r\n title: string;\r\n content: string;\r\n teamName: string;\r\n}\r\n\r\ninterface BatchSendResult {\r\n batchIds: string[];\r\n totalRecipients: number;\r\n}\r\n\r\nconst BATCH_LIMIT = 100;\r\n\r\n/**\r\n * Validates email addresses\r\n */\r\nexport function validateEmails(emails: string[]): {\r\n valid: string[];\r\n invalid: string[];\r\n} {\r\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\r\n const valid: string[] = [];\r\n const invalid: string[] = [];\r\n\r\n for (const email of emails) {\r\n if (emailRegex.test(email)) {\r\n valid.push(email);\r\n } else {\r\n invalid.push(email);\r\n }\r\n }\r\n\r\n return { valid, invalid };\r\n}\r\n\r\n/**\r\n * Sends announcement email to multiple recipients using Resend batch API\r\n * Handles batch splitting for >100 recipients\r\n */\r\nexport async function sendAnnouncementEmail({\r\n recipients,\r\n title,\r\n content,\r\n teamName,\r\n}: SendEmailParams): Promise {\r\n // Validate all emails first\r\n const emails = recipients.map((r) => r.email);\r\n const { valid, invalid } = validateEmails(emails);\r\n\r\n if (invalid.length > 0) {\r\n throw new Error(`Invalid email addresses: ${invalid.join(\", \")}`);\r\n }\r\n\r\n const batchIds: string[] = [];\r\n\r\n // Split into batches if needed\r\n for (let i = 0; i < recipients.length; i += BATCH_LIMIT) {\r\n const batch = recipients.slice(i, i + BATCH_LIMIT);\r\n\r\n // Render all emails in the batch (render is async in v2.x)\r\n const renderedHtmls = await Promise.all(\r\n batch.map((recipient) =>\r\n render(\r\n React.createElement(AnnouncementEmail, {\r\n title,\r\n content,\r\n teamName,\r\n memberName: recipient.name,\r\n }),\r\n ),\r\n ),\r\n );\r\n\r\n // Create email objects with rendered HTML\r\n const emails = batch.map((recipient, index) => ({\r\n from: \"Taskboard \", // TODO: Replace with your verified domain\r\n to: recipient.email,\r\n subject: `${teamName}: ${title}`,\r\n html: renderedHtmls[index],\r\n }));\r\n\r\n // Send batch\r\n const { data, error } = await resend.batch.send(emails);\r\n\r\n if (error) {\r\n throw new Error(`Resend API error: ${error.message}`);\r\n }\r\n\r\n if (data?.id) {\r\n batchIds.push(data.id);\r\n }\r\n }\r\n\r\n return {\r\n batchIds,\r\n totalRecipients: recipients.length,\r\n };\r\n}\r\n\r\n/**\r\n * Query Resend API for batch status (for reconciliation)\r\n */\r\nexport async function getBatchStatus(batchId: string) {\r\n try {\r\n // Note: Resend doesn't have a direct batch status endpoint yet\r\n // This is a placeholder for future implementation\r\n // For now, rely on webhooks for status updates\r\n return { status: \"unknown\" };\r\n } catch (error) {\r\n console.error(\"Error fetching batch status:\", error);\r\n return { status: \"error\" };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(calendar)\\calendar.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(calendar)\\calendar_db.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(events)\\event.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'CalendarEventSchema' is defined but never used.","line":3,"column":10,"nodeType":null,"messageId":"unusedVar","endLine":3,"endColumn":29}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use server\";\r\nimport { getCalendarClient } from \"./google\";\r\nimport { CalendarEventSchema } from \"@/lib/validations\";\r\nimport {\r\n createCalendarEvent_DB,\r\n updateCalendarEvent_DB,\r\n deleteCalendarEvent_DB,\r\n} from \"./event_db\";\r\nimport { getTeamCalendarId } from \"../(calendar)/calendar\";\r\nimport { getUser } from \"../auth\";\r\nimport prisma from \"@/utils/prisma/prisma\";\r\nimport { getMembersForTeam } from \"../members\";\r\n\r\n// TODO: Can be set by user\r\nconst DEFAULT_TIMEZONE = \"Asia/Manila\";\r\n\r\n// Creates an event in a team's calendar.\r\nexport async function createCalendarEvent(\r\n teamId: string,\r\n eventDetails: {\r\n title: string;\r\n start: Date;\r\n end: Date;\r\n description?: string;\r\n },\r\n) {\r\n let leader_id: string;\r\n\r\n // validate and get user id\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: unknown) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.error(`Error creating calendar: ${teamId}`, message);\r\n return {\r\n success: false,\r\n error: message || `Failed to create calendar: ${teamId}`,\r\n };\r\n }\r\n\r\n // get calendar id\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n try {\r\n const { client } = await getCalendarClient();\r\n\r\n // Note: Validation is handled by the caller (Zod schema at form level)\r\n\r\n // Fetch team members to add as attendees\r\n const membersResult = await getMembersForTeam(teamId);\r\n const attendees =\r\n membersResult.success && membersResult.members\r\n ? membersResult.members.map((m) => ({ email: m.email }))\r\n : [];\r\n\r\n // Create Event\r\n const response = await client.events.insert({\r\n calendarId: calendarId,\r\n sendUpdates: \"none\", // Don't send email notifications\r\n requestBody: {\r\n summary: eventDetails.title,\r\n description: eventDetails.description,\r\n start: {\r\n dateTime: eventDetails.start.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n end: {\r\n dateTime: eventDetails.end.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n attendees: attendees,\r\n },\r\n });\r\n\r\n const calendarEventId = response.data.id;\r\n\r\n // Check if event was created before db update\r\n if (!calendarEventId) {\r\n return {\r\n success: false,\r\n error: \"Failed to create event\",\r\n };\r\n }\r\n\r\n // save to database\r\n try {\r\n await createCalendarEvent_DB(\r\n calendarId,\r\n teamId,\r\n eventDetails,\r\n calendarEventId,\r\n );\r\n } catch (dbError: unknown) {\r\n const dbMessage =\r\n dbError instanceof Error ? dbError.message : String(dbError);\r\n console.error(\"DB Error. Rolling back Google Event...\", dbMessage);\r\n\r\n // Delete event from google calendar\r\n try {\r\n await client.events.delete({\r\n calendarId: calendarId,\r\n eventId: calendarEventId,\r\n });\r\n console.log(\"✅ Rollback successful: Google Event deleted.\");\r\n } catch (rollbackError) {\r\n // Rollback error\r\n console.error(\"System data is inconsistent.\", rollbackError);\r\n // TODO: Send alert to admin / deadlock queue\r\n }\r\n\r\n throw new Error(`System Error: Failed to save event. ${dbMessage}`);\r\n }\r\n\r\n return {\r\n success: true,\r\n eventId: response.data.id,\r\n eventLink: response.data.htmlLink,\r\n };\r\n } catch (error: unknown) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.error(\"Error creating event:\", message);\r\n return {\r\n success: false,\r\n error: message || \"Failed to create event\",\r\n };\r\n }\r\n}\r\n// Get team events (google)\r\nexport async function getTeamEvents(teamId: string) {\r\n // Get user\r\n let leader_id: string;\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: unknown) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.error(`Error in getTeamEvents: ${teamId}`, message);\r\n return {\r\n success: false,\r\n error: message || \"Authentication failed\",\r\n };\r\n }\r\n\r\n // Resolve calendarId from teamId\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n\r\n try {\r\n const { client } = await getCalendarClient();\r\n const response = await client.events.list({\r\n calendarId: calendarId,\r\n });\r\n return {\r\n success: true,\r\n events: response.data.items,\r\n };\r\n } catch (error: unknown) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.error(\"Error getting events:\", message);\r\n return {\r\n success: false,\r\n error: message || \"Failed to get events\",\r\n };\r\n }\r\n}\r\nexport async function updateCalendarEvent(\r\n teamId: string,\r\n eventData: {\r\n id: string;\r\n title?: string;\r\n start?: Date;\r\n end?: Date;\r\n googleEventId?: string | null;\r\n desc?: string;\r\n },\r\n) {\r\n // Extract and validate required fields\r\n const { id: eventId, title, start, end, googleEventId, desc } = eventData;\r\n\r\n // Map desc to description for DB layer\r\n const description = desc;\r\n\r\n if (!eventId) {\r\n return {\r\n success: false,\r\n error: \"Missing required field: eventId is required for update\",\r\n };\r\n }\r\n if (!title || !start || !end) {\r\n return {\r\n success: false,\r\n error:\r\n \"Missing required fields: title, start, and end are required for update\",\r\n };\r\n }\r\n\r\n const eventDetails = { title, start, end, description };\r\n let leader_id: string;\r\n // validate and get user id\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: unknown) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.error(`Error creating calendar: ${teamId}`, message);\r\n return {\r\n success: false,\r\n error: message || `Failed to create calendar: ${teamId}`,\r\n };\r\n }\r\n\r\n // get calendar id\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n\r\n // Validate googleEventId for Google API call\r\n if (!googleEventId) {\r\n return {\r\n success: false,\r\n error: \"Missing googleEventId: cannot update event in Google Calendar\",\r\n };\r\n }\r\n\r\n try {\r\n const { client } = await getCalendarClient();\r\n\r\n // Fetch team members to add as attendees\r\n const membersResult = await getMembersForTeam(teamId);\r\n const attendees =\r\n membersResult.success && membersResult.members\r\n ? membersResult.members.map((m) => ({ email: m.email }))\r\n : [];\r\n\r\n const response = await client.events.update({\r\n calendarId: calendarId,\r\n eventId: googleEventId,\r\n sendUpdates: \"none\", // Don't send email notifications\r\n requestBody: {\r\n summary: eventDetails.title,\r\n description: eventDetails.description,\r\n start: {\r\n dateTime: eventDetails.start.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n end: {\r\n dateTime: eventDetails.end.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n attendees: attendees,\r\n },\r\n });\r\n // Check if event was updated before db update\r\n if (!response.data.id) {\r\n return {\r\n success: false,\r\n error: \"Failed to update event\",\r\n };\r\n }\r\n // Save changes to DB\r\n try {\r\n await updateCalendarEvent_DB(eventId, eventDetails);\r\n } catch (dbError: unknown) {\r\n const dbMessage =\r\n dbError instanceof Error ? dbError.message : String(dbError);\r\n console.error(\r\n \"❌ DB Consistency Failure during Update. Google Event updated, but DB failed.\",\r\n dbMessage,\r\n );\r\n // NOTE: Rolling back an update is complex (needs old data).\r\n // For now, we log the inconsistency.\r\n throw new Error(\r\n `System Error: Failed to update event in DB. ${dbMessage}`,\r\n );\r\n }\r\n\r\n return {\r\n success: true,\r\n eventId: response.data.id,\r\n eventLink: response.data.htmlLink,\r\n };\r\n } catch (error: unknown) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.error(\"Error updating event:\", message);\r\n return {\r\n success: false,\r\n error: message || \"Failed to update event\",\r\n };\r\n }\r\n}\r\nexport async function deleteCalendarEvent(teamId: string, eventId: string) {\r\n // Get user\r\n let leader_id: string;\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: unknown) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.error(`Error in deleteCalendarEvent: ${teamId}`, message);\r\n return {\r\n success: false,\r\n error: message || \"Authentication failed\",\r\n };\r\n }\r\n\r\n // Resolve calendarId from teamId\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n\r\n // Fetch event from DB to get googleEventId\r\n let dbEvent;\r\n try {\r\n dbEvent = await prisma.events.findUnique({\r\n where: { id: eventId },\r\n select: { google_event_id: true },\r\n });\r\n } catch (dbError: unknown) {\r\n const dbMessage =\r\n dbError instanceof Error ? dbError.message : String(dbError);\r\n console.error(\"Database query failed in deleteCalendarEvent:\", dbMessage);\r\n return {\r\n success: false,\r\n error: `Database error: ${dbMessage}`,\r\n };\r\n }\r\n\r\n if (!dbEvent || !dbEvent.google_event_id) {\r\n return {\r\n success: false,\r\n error: \"Event not found or missing Google Event ID\",\r\n };\r\n }\r\n\r\n try {\r\n const { client } = await getCalendarClient();\r\n await client.events.delete({\r\n calendarId: calendarId,\r\n eventId: dbEvent.google_event_id,\r\n });\r\n try {\r\n await deleteCalendarEvent_DB(eventId);\r\n } catch (dbError: unknown) {\r\n const dbMessage =\r\n dbError instanceof Error ? dbError.message : String(dbError);\r\n console.error(\r\n \"❌ DB Consistency Failure during Delete. Google Event deleted, but DB failed (Zombie Data).\",\r\n dbMessage,\r\n );\r\n // NOTE: Rolling back a delete means RE-CREATING the event (new ID).\r\n // For now, we log the inconsistency.\r\n throw new Error(\r\n `System Error: Failed to delete event in DB. ${dbMessage}`,\r\n );\r\n }\r\n\r\n return {\r\n success: true,\r\n };\r\n } catch (error: unknown) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.error(\"Error deleting event:\", message);\r\n return {\r\n success: false,\r\n error: message || \"Failed to delete event\",\r\n };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(events)\\event_db.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(events)\\google.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\auth.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\members.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\teams.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\actions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\AboutBtn.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\ErrorMsg.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\SignInButton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\Star.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\404\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\about\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\api\\webhooks\\resend\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\auth\\auth-code-error\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\auth\\callback\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\CalendarPermissionsBanner.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\NewTeamBtn.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\SignOutBtn.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\WelcomeMsg.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\ActionsMenu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\ConfirmationModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\EditTeamModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\Pagination.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\SearchInput.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\TeamTable.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\data.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\loading.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\AnnouncementCard.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\AnnouncementsClient.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'err' is defined but never used.","line":49,"column":14,"nodeType":null,"messageId":"unusedVar","endLine":49,"endColumn":17},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'loadAnnouncements'. Either include it or remove the dependency array.","line":58,"column":6,"nodeType":"ArrayExpression","endLine":58,"endColumn":14,"suggestions":[{"desc":"Update the dependencies array to be: [loadAnnouncements, teamId]","fix":{"range":[1678,1686],"text":"[loadAnnouncements, teamId]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport { useState, useEffect, useMemo } from \"react\";\r\nimport { getAnnouncementsForTeam } from \"@/actions/(announcements)/crud\";\r\nimport CreateAnnouncementDialog from \"./CreateAnnouncementDialog\";\r\nimport AnnouncementCard from \"./AnnouncementCard\";\r\nimport { EmailStatus } from \"@prisma/client\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\r\n\r\ninterface AnnouncementsClientProps {\r\n teamId: string;\r\n}\r\n\r\ninterface Announcement {\r\n id: bigint;\r\n title: string;\r\n content: string;\r\n created_at: Date;\r\n email_status: EmailStatus | null;\r\n sent_at: Date | null;\r\n recipient_count: number;\r\n delivered_count: number;\r\n error_message: string | null;\r\n}\r\n\r\nconst ITEMS_PER_PAGE = 5;\r\n\r\nexport default function AnnouncementsClient({\r\n teamId,\r\n}: AnnouncementsClientProps) {\r\n const [announcements, setAnnouncements] = useState([]);\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [error, setError] = useState(null);\r\n const [currentPage, setCurrentPage] = useState(1);\r\n\r\n async function loadAnnouncements() {\r\n setIsLoading(true);\r\n setError(null);\r\n\r\n try {\r\n const result = await getAnnouncementsForTeam(teamId);\r\n\r\n if (result.success && result.announcements) {\r\n setAnnouncements(result.announcements as Announcement[]);\r\n } else {\r\n setError(result.error || \"Failed to load announcements\");\r\n }\r\n } catch (err) {\r\n setError(\"An error occurred while loading announcements\");\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }\r\n\r\n useEffect(() => {\r\n loadAnnouncements();\r\n }, [teamId]);\r\n\r\n // Calculate pagination\r\n const totalPages = Math.ceil(announcements.length / ITEMS_PER_PAGE);\r\n const paginatedAnnouncements = useMemo(() => {\r\n const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;\r\n return announcements.slice(startIndex, startIndex + ITEMS_PER_PAGE);\r\n }, [announcements, currentPage]);\r\n\r\n // Reset to page 1 when announcements change\r\n useEffect(() => {\r\n setCurrentPage(1);\r\n }, [announcements.length]);\r\n\r\n if (isLoading) {\r\n return (\r\n
\r\n {/* Skeleton cards */}\r\n {[1, 2].map((i) => (\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n ))}\r\n
\r\n );\r\n }\r\n\r\n if (error) {\r\n return (\r\n
\r\n

{error}

\r\n
\r\n );\r\n }\r\n\r\n return (\r\n
\r\n {/* Header */}\r\n
\r\n
\r\n

\r\n Announcements\r\n

\r\n \r\n
\r\n \r\n
\r\n\r\n {/* Announcements List */}\r\n {announcements.length === 0 ? (\r\n
\r\n

No announcements yet

\r\n

\r\n Create your first announcement to get started\r\n

\r\n
\r\n ) : (\r\n <>\r\n
\r\n {paginatedAnnouncements.map((announcement) => (\r\n \r\n ))}\r\n
\r\n\r\n {/* Pagination */}\r\n {totalPages > 1 && (\r\n
\r\n

\r\n Page {currentPage} of {totalPages} ({announcements.length}{\" \"}\r\n total)\r\n

\r\n
\r\n setCurrentPage((p) => Math.max(1, p - 1))}\r\n disabled={currentPage === 1}\r\n className=\"px-2\"\r\n >\r\n \r\n Previous\r\n \r\n \r\n setCurrentPage((p) => Math.min(totalPages, p + 1))\r\n }\r\n disabled={currentPage === totalPages}\r\n className=\"px-2\"\r\n >\r\n Next\r\n \r\n \r\n
\r\n
\r\n )}\r\n \r\n )}\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\CreateAnnouncementDialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\EmailStatusBadge.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\(actions)\\useCalendarActions.ts","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nC:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\(actions)\\useCalendarActions.ts:53:5\n 51 | // 5. Initial Load\n 52 | useEffect(() => {\n> 53 | loadEvents();\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 54 | }, [loadEvents]);\n 55 | const handleCreate = useCallback(\n 56 | async (data: CreateEventInput) => {","line":53,"column":5,"nodeType":null,"endLine":53,"endColumn":15}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useState, useCallback, useEffect } from \"react\";\r\nimport { View } from \"react-big-calendar\";\r\nimport {\r\n CalendarEvent,\r\n CreateEventInput,\r\n UpdateEventInput,\r\n} from \"@/lib/validations\";\r\nimport {\r\n createCalendarEvent,\r\n updateCalendarEvent,\r\n deleteCalendarEvent,\r\n} from \"@/actions/(events)/event\";\r\nimport { getTeamEvents_DB } from \"@/actions/(events)/event_db\";\r\nimport toast from \"react-hot-toast\";\r\n\r\nexport function useTeamCalendar(teamId: string) {\r\n // 1. Data State\r\n const [events, setEvents] = useState([]);\r\n const [isLoading, setIsLoading] = useState(true);\r\n\r\n // 2. Calendar View State\r\n const [date, setDate] = useState(new Date());\r\n const [view, setView] = useState(\"month\");\r\n\r\n // 3. Modal State\r\n const [modal, setModal] = useState<{\r\n isOpen: boolean;\r\n mode: \"create\" | \"edit\";\r\n selectedEvent: CalendarEvent | null;\r\n }>({\r\n isOpen: false,\r\n mode: \"create\",\r\n selectedEvent: null,\r\n });\r\n\r\n // 4. Load Events (extracted for refetch)\r\n const loadEvents = useCallback(async () => {\r\n setIsLoading(true);\r\n\r\n try {\r\n const events = await getTeamEvents_DB(teamId);\r\n setEvents(events);\r\n } catch (error) {\r\n console.error(\"Failed to load events:\", error);\r\n setEvents([]);\r\n }\r\n\r\n setIsLoading(false);\r\n }, [teamId]);\r\n\r\n // 5. Initial Load\r\n useEffect(() => {\r\n loadEvents();\r\n }, [loadEvents]);\r\n const handleCreate = useCallback(\r\n async (data: CreateEventInput) => {\r\n const toastId = toast.loading(\"Creating event...\");\r\n try {\r\n // Map desc to description for server action\r\n const result = await createCalendarEvent(teamId, {\r\n title: data.title,\r\n start: data.start,\r\n end: data.end,\r\n description: data.desc,\r\n });\r\n if (result.success) {\r\n toast.success(\"Event created successfully!\", { id: toastId });\r\n await loadEvents(); // Refetch canonical list\r\n } else {\r\n toast.error(result.error || \"Failed to create event\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\r\n error instanceof Error ? error.message : \"Failed to create event\",\r\n { id: toastId },\r\n );\r\n }\r\n },\r\n [teamId, loadEvents],\r\n );\r\n const handleUpdate = useCallback(\r\n async (data: UpdateEventInput) => {\r\n const toastId = toast.loading(\"Updating event...\");\r\n try {\r\n const result = await updateCalendarEvent(teamId, data);\r\n if (result.success) {\r\n toast.success(\"Event updated successfully!\", { id: toastId });\r\n await loadEvents(); // Refetch canonical list\r\n } else {\r\n toast.error(result.error || \"Failed to update event\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\r\n error instanceof Error ? error.message : \"Failed to update event\",\r\n { id: toastId },\r\n );\r\n }\r\n },\r\n [teamId, loadEvents],\r\n );\r\n const handleDelete = useCallback(\r\n async (id: string) => {\r\n const toastId = toast.loading(\"Deleting event...\");\r\n try {\r\n const result = await deleteCalendarEvent(teamId, id);\r\n if (result.success) {\r\n toast.success(\"Event deleted successfully!\", { id: toastId });\r\n await loadEvents(); // Refetch canonical list\r\n } else {\r\n toast.error(result.error || \"Failed to delete event\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\r\n error instanceof Error ? error.message : \"Failed to delete event\",\r\n { id: toastId },\r\n );\r\n }\r\n },\r\n [teamId, loadEvents],\r\n );\r\n\r\n // 5. Actions (Scaffolded for User Implementation)\r\n const openCreate = useCallback(() => {\r\n setModal({ isOpen: true, mode: \"create\", selectedEvent: null });\r\n }, []);\r\n\r\n const openEdit = useCallback((event: CalendarEvent) => {\r\n setModal({ isOpen: true, mode: \"edit\", selectedEvent: event });\r\n }, []);\r\n\r\n const close = useCallback(() => {\r\n setModal((prev) => ({ ...prev, isOpen: false }));\r\n }, []);\r\n\r\n const submit = useCallback(\r\n async (data: CreateEventInput | UpdateEventInput) => {\r\n close(); // Close modal before showing toasts\r\n if (modal.mode === \"create\") {\r\n await handleCreate(data as CreateEventInput);\r\n } else if (modal.mode === \"edit\") {\r\n await handleUpdate(data as UpdateEventInput);\r\n }\r\n },\r\n [modal.mode, close, handleCreate, handleUpdate],\r\n );\r\n\r\n const remove = useCallback(\r\n async (id: string) => {\r\n close(); // Close modal before showing toasts\r\n await handleDelete(id);\r\n },\r\n [close, handleDelete],\r\n );\r\n\r\n return {\r\n // State\r\n events,\r\n isLoading,\r\n date,\r\n view,\r\n modal,\r\n\r\n // Setters\r\n setDate,\r\n setView,\r\n\r\n // Actions\r\n actions: {\r\n openCreate,\r\n openEdit,\r\n close,\r\n submit,\r\n remove,\r\n },\r\n };\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\CalendarToolbar.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'date' is assigned a value but never used.","line":17,"column":5,"nodeType":null,"messageId":"unusedVar","endLine":17,"endColumn":9},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'btnActive' is assigned a value but never used.","line":40,"column":9,"nodeType":null,"messageId":"unusedVar","endLine":40,"endColumn":18}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { View, ToolbarProps } from \"react-big-calendar\";\r\nimport { CalendarEvent } from \"@/lib/validations\";\r\nimport { ChevronDown, ChevronLeft, ChevronRight, Plus } from \"lucide-react\";\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from \"@/components/ui/DropdownMenu\";\r\n\r\ninterface CalendarToolbarProps extends ToolbarProps {\r\n onAddEvent?: () => void;\r\n}\r\n\r\nexport default function CalendarToolbar(props: CalendarToolbarProps) {\r\n const {\r\n date,\r\n view,\r\n views,\r\n label,\r\n onView,\r\n onNavigate,\r\n localizer,\r\n onAddEvent,\r\n } = props;\r\n\r\n const navigate = (action: \"PREV\" | \"NEXT\" | \"TODAY\") => {\r\n onNavigate(action);\r\n };\r\n\r\n const currentView = view;\r\n const availableViews = views as View[];\r\n\r\n // Shared button styles\r\n const btnBase =\r\n \"relative inline-flex items-center px-4 py-2 border border-[#e5e7eb] bg-white text-sm font-semibold text-[#111827] hover:bg-[#f9fafb] focus:z-10 focus:outline-none transition-colors\";\r\n const btnFirst = \"rounded-l-md\";\r\n const btnLast = \"rounded-r-md\";\r\n const btnMiddle = \"-ml-px\";\r\n const btnActive =\r\n \"bg-[#2563eb] text-white border-[#2563eb] hover:bg-[#1d4ed8] z-20\";\r\n\r\n return (\r\n
\r\n {/* Mobile Layout: Row 1 (Nav + View), Row 2 (Label) */}\r\n
\r\n
\r\n {/* Navigation Group - Connected Buttons */}\r\n
\r\n navigate(\"TODAY\")}\r\n >\r\n Today\r\n \r\n navigate(\"PREV\")}\r\n aria-label=\"Previous range\"\r\n >\r\n \r\n <\r\n \r\n \r\n navigate(\"NEXT\")}\r\n aria-label=\"Next range\"\r\n >\r\n \r\n >\r\n \r\n \r\n
\r\n\r\n {/* Mobile View Selector + Add Button */}\r\n
\r\n \r\n \r\n \r\n \r\n {localizer.messages[currentView] || currentView}\r\n \r\n \r\n \r\n \r\n \r\n {availableViews.map((v) => (\r\n onView(v)}\r\n className={`\r\n cursor-pointer px-3 py-2 font-medium capitalize rounded-sm text-sm\r\n hover:bg-[#f3f4f6] focus:bg-[#f3f4f6]\r\n ${currentView === v ? \"bg-[#eff6ff] text-[#2563eb]\" : \"text-[#111827]\"}\r\n `}\r\n >\r\n {localizer.messages[v] || v}\r\n \r\n ))}\r\n \r\n \r\n {onAddEvent && (\r\n \r\n \r\n Add Event\r\n \r\n )}\r\n
\r\n
\r\n\r\n {/* Label (Mobile) */}\r\n \r\n {label}\r\n \r\n
\r\n\r\n {/* Desktop Layout */}\r\n
\r\n {/* Navigation Group */}\r\n
\r\n navigate(\"TODAY\")}\r\n >\r\n Today\r\n \r\n navigate(\"PREV\")}\r\n aria-label=\"Previous range\"\r\n >\r\n \r\n \r\n navigate(\"NEXT\")}\r\n aria-label=\"Next range\"\r\n >\r\n \r\n \r\n
\r\n\r\n {/* Label (Desktop) */}\r\n \r\n {label}\r\n \r\n\r\n {/* View Selector Dropdown + Add Button (Desktop - matches mobile) */}\r\n
\r\n \r\n \r\n \r\n \r\n {localizer.messages[currentView] || currentView}\r\n \r\n \r\n \r\n \r\n \r\n {availableViews.map((v) => (\r\n onView(v)}\r\n className={`\r\n cursor-pointer px-3 py-2 font-medium capitalize rounded-sm text-sm\r\n hover:bg-[#f3f4f6] focus:bg-[#f3f4f6]\r\n ${currentView === v ? \"bg-[#eff6ff] text-[#2563eb]\" : \"text-[#111827]\"}\r\n `}\r\n >\r\n {localizer.messages[v] || v}\r\n \r\n ))}\r\n \r\n \r\n {onAddEvent && (\r\n \r\n \r\n Add Event\r\n \r\n )}\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\EventModal.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nC:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\EventModal.tsx:50:7\n 48 | useEffect(() => {\n 49 | if (mode === \"edit\" && event) {\n> 50 | setTitle(event.title);\n | ^^^^^^^^ Avoid calling setState() directly within an effect\n 51 | setStartStr(format(event.start, \"yyyy-MM-dd'T'HH:mm\"));\n 52 | setEndStr(format(event.end, \"yyyy-MM-dd'T'HH:mm\"));\n 53 | setDesc(event.desc || \"\");","line":50,"column":7,"nodeType":null,"endLine":50,"endColumn":15}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useState, useEffect } from \"react\";\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogDescription,\r\n DialogFooter,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogOverlay,\r\n} from \"@/components/ui/Dialog\";\r\nimport { Input } from \"@/components/ui/Input\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport {\r\n CalendarEvent,\r\n CreateEventInput,\r\n UpdateEventInput,\r\n CreateEventSchema,\r\n UpdateEventSchema,\r\n} from \"@/lib/validations\";\r\nimport { format } from \"date-fns\";\r\nimport { inter, instrumentSerif } from \"@/app/fonts\";\r\nimport toast from \"react-hot-toast\";\r\n\r\ninterface EventModalProps {\r\n isOpen: boolean;\r\n mode: \"create\" | \"edit\";\r\n event: CalendarEvent | null;\r\n onClose: () => void;\r\n onSubmit: (data: CreateEventInput | UpdateEventInput) => Promise;\r\n onDelete?: (id: string) => Promise;\r\n}\r\n\r\nexport default function EventModal({\r\n isOpen,\r\n mode,\r\n event,\r\n onClose,\r\n onSubmit,\r\n onDelete,\r\n}: EventModalProps) {\r\n const [title, setTitle] = useState(\"\");\r\n const [startStr, setStartStr] = useState(\"\");\r\n const [endStr, setEndStr] = useState(\"\");\r\n const [desc, setDesc] = useState(\"\");\r\n const [errors, setErrors] = useState>({});\r\n\r\n // Sync state with event data when editing\r\n useEffect(() => {\r\n if (mode === \"edit\" && event) {\r\n setTitle(event.title);\r\n setStartStr(format(event.start, \"yyyy-MM-dd'T'HH:mm\"));\r\n setEndStr(format(event.end, \"yyyy-MM-dd'T'HH:mm\"));\r\n setDesc(event.desc || \"\");\r\n } else {\r\n setTitle(\"\");\r\n setStartStr(\"\");\r\n setEndStr(\"\");\r\n setDesc(\"\");\r\n }\r\n setErrors({});\r\n }, [mode, event, isOpen]);\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setErrors({});\r\n\r\n if (mode === \"create\") {\r\n const data = {\r\n title,\r\n start: new Date(startStr),\r\n end: endStr ? new Date(endStr) : undefined, // Allow empty end date\r\n desc: desc || undefined,\r\n };\r\n\r\n // Validate with Zod\r\n const result = CreateEventSchema.safeParse(data);\r\n if (!result.success) {\r\n const fieldErrors: Record = {};\r\n result.error.issues.forEach((err) => {\r\n const path = err.path.join(\".\");\r\n fieldErrors[path] = err.message;\r\n });\r\n setErrors(fieldErrors);\r\n toast.error(\"Please fix the validation errors\");\r\n return;\r\n }\r\n\r\n await onSubmit(result.data);\r\n } else {\r\n const data = {\r\n id: event!.id,\r\n title,\r\n start: new Date(startStr),\r\n end: endStr ? new Date(endStr) : undefined, // Allow empty end date\r\n desc: desc || undefined,\r\n googleEventId: event!.googleEventId,\r\n };\r\n\r\n // Validate with Zod\r\n const result = UpdateEventSchema.safeParse(data);\r\n if (!result.success) {\r\n const fieldErrors: Record = {};\r\n result.error.issues.forEach((err) => {\r\n const path = err.path.join(\".\");\r\n fieldErrors[path] = err.message;\r\n });\r\n setErrors(fieldErrors);\r\n toast.error(\"Please fix the validation errors\");\r\n return;\r\n }\r\n\r\n await onSubmit(result.data);\r\n }\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n {mode === \"create\" ? \"📅 Add New Event\" : \"📝 Edit Event\"}\r\n \r\n \r\n {mode === \"create\"\r\n ? \"Enter the details for the new team event.\"\r\n : \"Update the details of this event.\"}\r\n \r\n \r\n\r\n
\r\n
\r\n \r\n setTitle(e.target.value)}\r\n placeholder=\"e.g. Design Sprint\"\r\n className={errors.title ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.title && (\r\n

{errors.title}

\r\n )}\r\n
\r\n
\r\n
\r\n \r\n setStartStr(e.target.value)}\r\n className={errors.start ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.start && (\r\n

{errors.start}

\r\n )}\r\n
\r\n
\r\n \r\n setEndStr(e.target.value)}\r\n className={errors.end ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.end && (\r\n

{errors.end}

\r\n )}\r\n
\r\n
\r\n
\r\n \r\n setDesc(e.target.value)}\r\n placeholder=\"Short description\"\r\n className={errors.desc ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.desc && (\r\n

{errors.desc}

\r\n )}\r\n
\r\n\r\n \r\n {mode === \"edit\" && onDelete && (\r\n onDelete(event!.id)}\r\n >\r\n Delete\r\n \r\n )}\r\n
\r\n \r\n Cancel\r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\TeamCalendar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\AddMemberModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\EditMemberModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\ImportMembersModal.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'AlertDialogAction' is defined but never used.","line":5,"column":3,"nodeType":null,"messageId":"unusedVar","endLine":5,"endColumn":20},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":83,"column":18,"nodeType":null,"messageId":"unusedVar","endLine":83,"endColumn":23}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\nimport { useState, useRef } from \"react\";\r\nimport {\r\n AlertDialog,\r\n AlertDialogAction,\r\n AlertDialogCancel,\r\n AlertDialogContent,\r\n AlertDialogFooter,\r\n AlertDialogHeader,\r\n AlertDialogTitle,\r\n AlertDialogDescription,\r\n} from \"@/components/ui/AlertDialog\";\r\nimport { Upload, FileText, AlertCircle } from \"lucide-react\";\r\nimport { importMembersToTeam } from \"@/actions/members\";\r\nimport toast from \"react-hot-toast\";\r\n\r\ninterface ImportMembersModalProps {\r\n teamId: string;\r\n open: boolean;\r\n onOpenChange: (open: boolean) => void;\r\n onSuccess?: () => void;\r\n}\r\n\r\ninterface ImportResult {\r\n success: boolean;\r\n added: number;\r\n failed: number;\r\n errors?: string[];\r\n}\r\n\r\nfunction ImportMembersModal({\r\n teamId,\r\n open,\r\n onOpenChange,\r\n onSuccess,\r\n}: ImportMembersModalProps) {\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [selectedFile, setSelectedFile] = useState(null);\r\n const [fileError, setFileError] = useState(undefined);\r\n const fileInputRef = useRef(null);\r\n\r\n const handleFileSelect = (e: React.ChangeEvent) => {\r\n const file = e.target.files?.[0];\r\n if (file) {\r\n if (file.type !== \"text/csv\" && !file.name.endsWith(\".csv\")) {\r\n setFileError(\"Please select a CSV file\");\r\n setSelectedFile(null);\r\n return;\r\n }\r\n setSelectedFile(file);\r\n setFileError(undefined); // Clear error when valid file selected\r\n }\r\n };\r\n\r\n const parseCSV = async (\r\n file: File,\r\n ): Promise> => {\r\n return new Promise((resolve, reject) => {\r\n const reader = new FileReader();\r\n reader.onload = (e) => {\r\n try {\r\n const text = e.target?.result as string;\r\n const lines = text\r\n .split(\"\\n\")\r\n .map((line) => line.trim())\r\n .filter((line) => line.length > 0); // Remove empty lines\r\n\r\n if (lines.length <= 1) {\r\n // Only header or empty file\r\n resolve([]);\r\n return;\r\n }\r\n\r\n // Skip header row\r\n const dataLines = lines.slice(1);\r\n\r\n const members = dataLines.map((line) => {\r\n const [email, full_name] = line.split(\",\").map((s) => s.trim());\r\n return { email, full_name: full_name || undefined };\r\n });\r\n\r\n resolve(members);\r\n } catch (error) {\r\n reject(new Error(\"Failed to parse CSV file\"));\r\n }\r\n };\r\n reader.onerror = () => reject(new Error(\"Failed to read file\"));\r\n reader.readAsText(file);\r\n });\r\n };\r\n\r\n const handleImport = async () => {\r\n if (!selectedFile) {\r\n setFileError(\"Please select a file to import\");\r\n return; // Don't close modal\r\n }\r\n\r\n setFileError(undefined);\r\n setIsLoading(true);\r\n\r\n try {\r\n toast.loading(\"Parsing CSV file...\");\r\n const parsedMembers = await parseCSV(selectedFile);\r\n\r\n // Filter out invalid entries (empty email)\r\n const members = parsedMembers.filter((m) => m.email && m.email.trim());\r\n\r\n if (members.length === 0) {\r\n toast.dismiss();\r\n setFileError(\r\n \"No valid members found in CSV. Please check the file format.\",\r\n );\r\n setIsLoading(false);\r\n return; // Don't close modal\r\n }\r\n\r\n // Show warning if some entries were skipped\r\n if (members.length < parsedMembers.length) {\r\n const skipped = parsedMembers.length - members.length;\r\n toast.dismiss();\r\n toast(\r\n `Skipped ${skipped} invalid ${skipped === 1 ? \"entry\" : \"entries\"}. Importing ${members.length} members...`,\r\n { icon: \"⚠️\", duration: 3000 },\r\n );\r\n }\r\n\r\n toast.dismiss();\r\n toast.loading(`Importing ${members.length} members...`);\r\n\r\n const result = await importMembersToTeam(teamId, members);\r\n\r\n toast.dismiss();\r\n\r\n if (result.success) {\r\n const { added, failed } = result as ImportResult;\r\n if (failed > 0) {\r\n toast.success(\r\n `Imported ${added} members successfully. ${failed} failed.`,\r\n { duration: 5000 },\r\n );\r\n } else {\r\n toast.success(`Successfully imported ${added} members!`);\r\n }\r\n setSelectedFile(null);\r\n if (fileInputRef.current) fileInputRef.current.value = \"\";\r\n setFileError(undefined);\r\n onOpenChange(false); // Only close on success\r\n onSuccess?.();\r\n } else {\r\n setFileError(result.error || \"Failed to import members\");\r\n // Don't close modal - let user try again\r\n }\r\n } catch (error) {\r\n toast.dismiss();\r\n setFileError(\r\n error instanceof Error ? error.message : \"Failed to import members\",\r\n );\r\n // Don't close modal on error\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n Import Members\r\n \r\n \r\n Upload a CSV file with columns: email, full_name\r\n \r\n \r\n\r\n
\r\n
\r\n \r\n \r\n \r\n {selectedFile ? (\r\n <>\r\n \r\n

\r\n {selectedFile.name}\r\n

\r\n

\r\n Click to change file\r\n

\r\n \r\n ) : (\r\n <>\r\n \r\n

\r\n Click to upload CSV\r\n

\r\n

or drag and drop

\r\n \r\n )}\r\n \r\n
\r\n {fileError && (\r\n

{fileError}

\r\n )}\r\n
\r\n\r\n
\r\n \r\n
\r\n

CSV Format:

\r\n \r\n email,full_name\r\n
\r\n john@example.com,John Doe\r\n
\r\n jane@example.com,Jane Smith\r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n Cancel\r\n \r\n \r\n \r\n {isLoading ? \"Importing...\" : \"Import Members\"}\r\n \r\n \r\n \r\n \r\n );\r\n}\r\n\r\nexport default ImportMembersModal;\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\MemberActionsMenu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\MemberTable.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":82,"column":14,"nodeType":null,"messageId":"unusedVar","endLine":82,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport { DataTable, ColumnDef } from \"@/components/ui/DataTable\";\r\nimport { getMembersForTeam, removeMemberFromTeam } from \"@/actions/members\";\r\nimport { SearchInput } from \"@/app/dashboard/components/team-table/SearchInput\";\r\nimport { Pagination } from \"@/app/dashboard/components/team-table/Pagination\";\r\nimport { MemberActionsMenu } from \"./MemberActionsMenu\";\r\nimport ConfirmationModal from \"@/app/dashboard/components/team-table/ConfirmationModal\";\r\nimport EditMemberModal, { Member } from \"./EditMemberModal\";\r\nimport AddMemberModal from \"./AddMemberModal\";\r\nimport ImportMembersModal from \"./ImportMembersModal\";\r\nimport { MemberTableSkeleton } from \"./MemberTableSkeleton\";\r\nimport toast from \"react-hot-toast\";\r\n\r\nimport { UserPlus, Upload } from \"lucide-react\";\r\n\r\ninterface MemberTableProps {\r\n teamId: string;\r\n}\r\n\r\nexport default function MemberTable({ teamId }: MemberTableProps) {\r\n const [members, setMembers] = React.useState([]);\r\n const [isLoading, setIsLoading] = React.useState(true);\r\n const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);\r\n const [memberToRemove, setMemberToRemove] = React.useState(\r\n null,\r\n );\r\n const [editModalOpen, setEditModalOpen] = React.useState(false);\r\n const [memberToEdit, setMemberToEdit] = React.useState(null);\r\n const [addModalOpen, setAddModalOpen] = React.useState(false);\r\n const [importModalOpen, setImportModalOpen] = React.useState(false);\r\n\r\n // Fetch members on mount\r\n const fetchMembers = React.useCallback(async () => {\r\n setIsLoading(true);\r\n const result = await getMembersForTeam(teamId);\r\n if (result.success && result.members) {\r\n setMembers(result.members as Member[]);\r\n }\r\n setIsLoading(false);\r\n }, [teamId]);\r\n\r\n React.useEffect(() => {\r\n fetchMembers();\r\n }, [fetchMembers]);\r\n\r\n const handleRemoveClick = (member: Member) => {\r\n setMemberToRemove(member);\r\n setDeleteModalOpen(true);\r\n };\r\n\r\n const handleEditClick = (member: Member) => {\r\n setMemberToEdit(member);\r\n setEditModalOpen(true);\r\n };\r\n\r\n const handleConfirmRemove = async () => {\r\n if (!memberToRemove) return;\r\n\r\n // Immediate update\r\n const previousMembers = members;\r\n setMembers((prev) => prev.filter((m) => m.id !== memberToRemove.id));\r\n setDeleteModalOpen(false);\r\n setMemberToRemove(null);\r\n\r\n toast.loading(\r\n `Removing ${memberToRemove.full_name || memberToRemove.email}...`,\r\n );\r\n\r\n try {\r\n const result = await removeMemberFromTeam(teamId, memberToRemove.id);\r\n toast.dismiss();\r\n\r\n if (result.success) {\r\n toast.success(\"Member removed successfully!\");\r\n } else {\r\n // Rollback on error\r\n setMembers(previousMembers);\r\n toast.error(result.error || \"Failed to remove member\");\r\n }\r\n } catch (error) {\r\n // Rollback on error\r\n setMembers(previousMembers);\r\n toast.dismiss();\r\n toast.error(\"Failed to remove member\");\r\n }\r\n };\r\n\r\n const columns: ColumnDef[] = [\r\n {\r\n key: \"full_name\",\r\n header: \"Name\",\r\n className: \"w-[250px]\",\r\n render: (member) => (\r\n
\r\n \r\n {member.full_name || \"—\"}\r\n \r\n
\r\n ),\r\n },\r\n {\r\n key: \"email\",\r\n header: \"Email\",\r\n className: \"\",\r\n render: (member) =>
{member.email}
,\r\n },\r\n {\r\n key: \"added_at\",\r\n header: \"Added\",\r\n className: \"text-center\",\r\n render: (member) => (\r\n
\r\n {member.added_at\r\n ? new Date(member.added_at).toLocaleDateString()\r\n : \"—\"}\r\n
\r\n ),\r\n },\r\n ];\r\n\r\n return (\r\n <>\r\n {isLoading ? (\r\n \r\n ) : (\r\n
\r\n \r\n data={members}\r\n columns={columns}\r\n searchKey=\"full_name\"\r\n searchPlaceholder=\"Search members...\"\r\n emptyMessage=\"No members in this team yet.\"\r\n headerActions={\r\n <>\r\n setAddModalOpen(true)}\r\n className=\"px-3 md:px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium flex items-center gap-2\"\r\n >\r\n \r\n Add Member\r\n \r\n setImportModalOpen(true)}\r\n className=\"px-3 md:px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors font-medium flex items-center gap-2\"\r\n >\r\n \r\n Import\r\n \r\n \r\n }\r\n renderActions={(member) => (\r\n handleEditClick(member)}\r\n onRemove={() => handleRemoveClick(member)}\r\n />\r\n )}\r\n SearchComponent={SearchInput}\r\n PaginationComponent={Pagination}\r\n />\r\n
\r\n )}\r\n\r\n \r\n\r\n \r\n\r\n \r\n\r\n \r\n \r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\MemberTableSkeleton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\TeamDashboardClient.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\TeamHeader.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\fonts.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\hero\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\auth\\callback\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\email\\announcement.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\AlertDialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\DataTable.tsx","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":43,"column":52,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":43,"endColumn":55,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[969,972],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[969,972],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'searchPlaceholder' is assigned a value but never used.","line":47,"column":3,"nodeType":null,"messageId":"unusedVar","endLine":47,"endColumn":20}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from \"@/components/ui/Table\";\r\n\r\nexport interface ColumnDef {\r\n key: string;\r\n header: string;\r\n className?: string;\r\n render?: (item: T) => React.ReactNode;\r\n}\r\n\r\nexport interface DataTableProps {\r\n data: T[];\r\n columns: ColumnDef[];\r\n searchKey: keyof T;\r\n searchPlaceholder?: string;\r\n emptyMessage?: string;\r\n onRowClick?: (item: T) => void;\r\n renderActions?: (item: T) => React.ReactNode;\r\n itemsPerPage?: number;\r\n headerActions?: React.ReactNode;\r\n SearchComponent?: React.ComponentType<{\r\n value: string;\r\n onChange: (value: string) => void;\r\n }>;\r\n PaginationComponent?: React.ComponentType<{\r\n totalCount: number;\r\n canPrevious: boolean;\r\n canNext: boolean;\r\n onPrevious: () => void;\r\n onNext: () => void;\r\n }>;\r\n}\r\n\r\nexport function DataTable>({\r\n data,\r\n columns,\r\n searchKey,\r\n searchPlaceholder = \"Search...\",\r\n emptyMessage = \"No results found.\",\r\n onRowClick,\r\n renderActions,\r\n itemsPerPage = 5,\r\n headerActions,\r\n SearchComponent,\r\n PaginationComponent,\r\n}: DataTableProps) {\r\n const [searchValue, setSearchValue] = React.useState(\"\");\r\n const [currentPage, setCurrentPage] = React.useState(1);\r\n\r\n // Filter data based on search\r\n const filteredData = React.useMemo(() => {\r\n if (!searchValue) return data;\r\n return data.filter((item) => {\r\n const value = item[searchKey];\r\n if (typeof value === \"string\") {\r\n return value.toLowerCase().includes(searchValue.toLowerCase());\r\n }\r\n return false;\r\n });\r\n }, [data, searchValue, searchKey]);\r\n\r\n // Paginate filtered data\r\n const totalPages = Math.ceil(filteredData.length / itemsPerPage);\r\n const paginatedData = React.useMemo(() => {\r\n const start = (currentPage - 1) * itemsPerPage;\r\n return filteredData.slice(start, start + itemsPerPage);\r\n }, [filteredData, currentPage, itemsPerPage]);\r\n\r\n // Reset page when search changes\r\n React.useEffect(() => {\r\n setCurrentPage(1);\r\n }, [searchValue]);\r\n\r\n return (\r\n
\r\n
\r\n {SearchComponent && (\r\n
\r\n \r\n {headerActions && (\r\n
{headerActions}
\r\n )}\r\n
\r\n )}\r\n\r\n
\r\n \r\n \r\n \r\n {columns.map((column) => (\r\n \r\n \r\n {column.header}\r\n \r\n \r\n ))}\r\n {renderActions && (\r\n \r\n \r\n Actions\r\n \r\n \r\n )}\r\n \r\n \r\n \r\n {paginatedData.length ? (\r\n paginatedData.map((item, index) => (\r\n onRowClick(item) : undefined}\r\n className={onRowClick ? \"cursor-pointer\" : \"\"}\r\n >\r\n {columns.map((column) => (\r\n \r\n {column.render\r\n ? column.render(item)\r\n : String(item[column.key] || \"\")}\r\n \r\n ))}\r\n {renderActions && (\r\n \r\n {renderActions(item)}\r\n \r\n )}\r\n \r\n ))\r\n ) : (\r\n \r\n \r\n {emptyMessage}\r\n \r\n \r\n )}\r\n \r\n
\r\n
\r\n
\r\n\r\n {PaginationComponent && (\r\n 1}\r\n canNext={currentPage < totalPages}\r\n onPrevious={() => setCurrentPage((p) => Math.max(1, p - 1))}\r\n onNext={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}\r\n />\r\n )}\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\DropdownMenu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Input.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\RichTextEditor.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Skeleton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Table.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Toast.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\eslint.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\calendar-utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\google-calendar.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\resend.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\validations.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lucide-react.d.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\next.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\postcss.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\postcss.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\prisma.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\proxy.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\public\\fonts.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Archivo' is defined but never used.","line":1,"column":17,"nodeType":null,"messageId":"unusedVar","endLine":1,"endColumn":24}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Inter, Archivo, Instrument_Serif } from \"next/font/google\";\r\n\r\nexport const inter = Inter({\r\n subsets: [\"latin\"],\r\n variable: \"--font-inter\",\r\n display: \"swap\",\r\n});\r\n\r\nexport const instrumentSerif = Instrument_Serif({\r\n subsets: [\"latin\"],\r\n weight: \"400\",\r\n variable: \"--font-display\",\r\n display: \"swap\",\r\n});\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\supabase\\functions\\store-google-refresh\\index.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'serve' is defined but never used.","line":1,"column":10,"nodeType":null,"messageId":"unusedVar","endLine":1,"endColumn":15},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":58,"column":27,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":58,"endColumn":30,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[2169,2172],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[2169,2172],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { serve } from \"https://deno.land/std@0.170.0/http/server.ts\";\r\n\r\n// Using Deno.serve per guidelines\r\nDeno.serve(async (req: Request) => {\r\n try {\r\n // Only allow POST\r\n if (req.method !== \"POST\") {\r\n return new Response(\r\n JSON.stringify({ success: false, message: \"Method not allowed\" }),\r\n { status: 405, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n const body = await req.json().catch(() => null);\r\n if (!body) {\r\n return new Response(\r\n JSON.stringify({ success: false, message: \"Invalid JSON body\" }),\r\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n const { userId, googleRefreshToken, email, fullName, avatarUrl } = body;\r\n if (!userId) {\r\n return new Response(\r\n JSON.stringify({ success: false, message: \"userId is required\" }),\r\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n // Build SQL parameters safely\r\n // Use Supabase DB URL to call Postgres directly\r\n const dbUrl = Deno.env.get(\"SUPABASE_DB_URL\");\r\n if (!dbUrl) {\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Database URL not configured\",\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n // Use fetch to call Postgres via connection string -- Use pg client would require npm; instead use RESTful SQL via Supabase REST? Not available. Instead use Postgres via Deno Postgres driver is not allowed. But Supabase provides REST admin via /rest/v1? We'll use the Supabase Admin REST endpoint using SUPABASE_URL and SERVICE_ROLE_KEY\r\n\r\n const supabaseUrl = Deno.env.get(\"SUPABASE_URL\");\r\n const serviceKey = Deno.env.get(\"SUPABASE_SERVICE_ROLE_KEY\");\r\n if (!supabaseUrl || !serviceKey) {\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Supabase env vars missing\",\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n // Upsert the profile\r\n const profilePayload: any = { id: userId };\r\n if (email !== undefined) profilePayload.email = email;\r\n if (fullName !== undefined) profilePayload.full_name = fullName;\r\n if (avatarUrl !== undefined) profilePayload.avatar_url = avatarUrl;\r\n if (googleRefreshToken !== undefined)\r\n profilePayload.google_refresh_token = googleRefreshToken;\r\n\r\n // Build PATCH (upsert) request to profiles table via Supabase REST\r\n // Use POST with Prefer: resolution=merge-duplicates for upsert on conflict\r\n\r\n const res = await fetch(`${supabaseUrl}/rest/v1/profiles?on_conflict=id`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n apikey: serviceKey,\r\n Authorization: `Bearer ${serviceKey}`,\r\n Prefer: \"resolution=merge-duplicates\",\r\n },\r\n body: JSON.stringify(profilePayload),\r\n });\r\n\r\n if (!res.ok) {\r\n const text = await res.text();\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Database request failed\",\r\n detail: text,\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n return new Response(\r\n JSON.stringify({ success: true, message: \"Token stored successfully\" }),\r\n { status: 200, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n } catch (err) {\r\n console.error(err);\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Internal server error\",\r\n error: String(err),\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n});\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\tailwind.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\auth-helpers.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\cn-helper.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\prisma\\prisma.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\supabase\\client.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\supabase\\proxy.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":56,"column":12,"nodeType":null,"messageId":"unusedVar","endLine":56,"endColumn":17}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { createServerClient } from \"@supabase/ssr\";\r\nimport { NextResponse, type NextRequest } from \"next/server\";\r\n\r\nexport async function updateSession(request: NextRequest) {\r\n let supabaseResponse = NextResponse.next({\r\n request,\r\n });\r\n\r\n // With Fluid compute, don't put this client in a global environment\r\n // variable. Always create a new one on each request.\r\n const supabase = createServerClient(\r\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\r\n process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,\r\n {\r\n cookies: {\r\n getAll() {\r\n return request.cookies.getAll();\r\n },\r\n setAll(cookiesToSet) {\r\n cookiesToSet.forEach(({ name, value }) =>\r\n request.cookies.set(name, value)\r\n );\r\n supabaseResponse = NextResponse.next({\r\n request,\r\n });\r\n cookiesToSet.forEach(({ name, value, options }) =>\r\n supabaseResponse.cookies.set(name, value, options)\r\n );\r\n },\r\n },\r\n }\r\n );\r\n\r\n // Do not run code between createServerClient and\r\n // supabase.auth.getClaims(). A simple mistake could make it very hard to debug\r\n // issues with users being randomly logged out.\r\n\r\n // IMPORTANT: If you remove getClaims() and you use server-side rendering\r\n // with the Supabase client, your users may be randomly logged out.\r\n\r\n // Routes that don't require authentication\r\n const PUBLIC_ROUTES = [\"/hero\", \"/about\", \"/login\", \"/auth/callback\", \"/404\"];\r\n const isPublicRoute = PUBLIC_ROUTES.some((route) =>\r\n request.nextUrl.pathname.startsWith(route)\r\n );\r\n\r\n // Skip auth check for public routes to prevent redirect loops\r\n if (isPublicRoute) {\r\n return supabaseResponse;\r\n }\r\n\r\n let user;\r\n try {\r\n const { data } = await supabase.auth.getClaims();\r\n user = data?.claims;\r\n } catch (error) {\r\n //redirect login\r\n const url = request.nextUrl.clone();\r\n url.pathname = \"/login\";\r\n return NextResponse.redirect(url);\r\n }\r\n\r\n if (!user) {\r\n // no user, potentially respond by redirecting the user to the login page\r\n const url = request.nextUrl.clone();\r\n url.pathname = \"/login\";\r\n return NextResponse.redirect(url);\r\n }\r\n\r\n // IMPORTANT: You *must* return the supabaseResponse object as it is. If you're\r\n // creating a new response object with NextResponse.next() make sure to:\r\n // 1. Pass the request in it, like so:\r\n // const myNewResponse = NextResponse.next({ request })\r\n // 2. Copy over the cookies, like so:\r\n // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())\r\n // 3. Change the myNewResponse object to fit your needs, but avoid changing\r\n // the cookies!\r\n // 4. Finally:\r\n // return myNewResponse\r\n // If this is not done, you may be causing the browser and server to go out\r\n // of sync and terminate the user's session prematurely!\r\n supabaseResponse.headers.set(\"X-Content-Type-Options\", \"nosniff\");\r\n supabaseResponse.headers.set(\"X-Frame-Options\", \"DENY\");\r\n supabaseResponse.headers.set(\"X-XSS-Protection\", \"1; mode=block\");\r\n\r\n return supabaseResponse;\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\supabase\\server.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]}] \ No newline at end of file diff --git a/lint-results-updated.json b/lint-results-updated.json new file mode 100644 index 0000000..76fc1b0 --- /dev/null +++ b/lint-results-updated.json @@ -0,0 +1 @@ +[{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(announcements)\\announcements.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(announcements)\\crud.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(announcements)\\resend.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'valid' is assigned a value but never used.","line":54,"column":11,"nodeType":null,"messageId":"unusedVar","endLine":54,"endColumn":16},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'batchId' is defined but never used.","line":109,"column":38,"nodeType":null,"messageId":"unusedVar","endLine":109,"endColumn":45}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { resend } from \"@/lib/resend\";\r\nimport { render } from \"@react-email/render\";\r\nimport { AnnouncementEmail } from \"@/components/email/announcement\";\r\nimport React from \"react\";\r\n\r\ninterface SendEmailParams {\r\n recipients: Array<{ email: string; name?: string }>;\r\n title: string;\r\n content: string;\r\n teamName: string;\r\n}\r\n\r\ninterface BatchSendResult {\r\n batchIds: string[];\r\n totalRecipients: number;\r\n}\r\n\r\nconst BATCH_LIMIT = 100;\r\n\r\n/**\r\n * Validates email addresses\r\n */\r\nexport function validateEmails(emails: string[]): {\r\n valid: string[];\r\n invalid: string[];\r\n} {\r\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\r\n const valid: string[] = [];\r\n const invalid: string[] = [];\r\n\r\n for (const email of emails) {\r\n if (emailRegex.test(email)) {\r\n valid.push(email);\r\n } else {\r\n invalid.push(email);\r\n }\r\n }\r\n\r\n return { valid, invalid };\r\n}\r\n\r\n/**\r\n * Sends announcement email to multiple recipients using Resend batch API\r\n * Handles batch splitting for >100 recipients\r\n */\r\nexport async function sendAnnouncementEmail({\r\n recipients,\r\n title,\r\n content,\r\n teamName,\r\n}: SendEmailParams): Promise {\r\n // Validate all emails first\r\n const emails = recipients.map((r) => r.email);\r\n const { valid, invalid } = validateEmails(emails);\r\n\r\n if (invalid.length > 0) {\r\n throw new Error(`Invalid email addresses: ${invalid.join(\", \")}`);\r\n }\r\n\r\n const batchIds: string[] = [];\r\n\r\n // Split into batches if needed\r\n for (let i = 0; i < recipients.length; i += BATCH_LIMIT) {\r\n const batch = recipients.slice(i, i + BATCH_LIMIT);\r\n\r\n // Render all emails in the batch (render is async in v2.x)\r\n const renderedHtmls = await Promise.all(\r\n batch.map((recipient) =>\r\n render(\r\n React.createElement(AnnouncementEmail, {\r\n title,\r\n content,\r\n teamName,\r\n memberName: recipient.name,\r\n }),\r\n ),\r\n ),\r\n );\r\n\r\n // Create email objects with rendered HTML\r\n const emails = batch.map((recipient, index) => ({\r\n from: \"Taskboard \", // TODO: Replace with your verified domain\r\n to: recipient.email,\r\n subject: `${teamName}: ${title}`,\r\n html: renderedHtmls[index],\r\n }));\r\n\r\n // Send batch\r\n const { data, error } = await resend.batch.send(emails);\r\n\r\n if (error) {\r\n throw new Error(`Resend API error: ${error.message}`);\r\n }\r\n\r\n if (data?.id) {\r\n batchIds.push(data.id);\r\n }\r\n }\r\n\r\n return {\r\n batchIds,\r\n totalRecipients: recipients.length,\r\n };\r\n}\r\n\r\n/**\r\n * Query Resend API for batch status (for reconciliation)\r\n */\r\nexport async function getBatchStatus(batchId: string) {\r\n try {\r\n // Note: Resend doesn't have a direct batch status endpoint yet\r\n // This is a placeholder for future implementation\r\n // For now, rely on webhooks for status updates\r\n return { status: \"unknown\" };\r\n } catch (error) {\r\n console.error(\"Error fetching batch status:\", error);\r\n return { status: \"error\" };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(calendar)\\calendar.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(calendar)\\calendar_db.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(events)\\event.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'CalendarEventSchema' is defined but never used.","line":3,"column":10,"nodeType":null,"messageId":"unusedVar","endLine":3,"endColumn":29},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":37,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":37,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[984,987],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[984,987],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":102,"column":23,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":102,"endColumn":26,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[2794,2797],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[2794,2797],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":126,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":126,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[3561,3564],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[3561,3564],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":145,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":145,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[4076,4079],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[4076,4079],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":171,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":171,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[4733,4736],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[4733,4736],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":220,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":220,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[5980,5983],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[5980,5983],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":283,"column":23,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":283,"endColumn":26,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[7804,7807],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[7804,7807],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":300,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":300,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[8328,8331],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[8328,8331],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":318,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":318,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[8837,8840],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[8837,8840],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":342,"column":21,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":342,"endColumn":24,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[9475,9478],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[9475,9478],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":368,"column":23,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":368,"endColumn":26,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[10108,10111],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[10108,10111],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":383,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":383,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[10578,10581],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[10578,10581],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":12,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use server\";\r\nimport { getCalendarClient } from \"./google\";\r\nimport { CalendarEventSchema } from \"@/lib/validations\";\r\nimport {\r\n createCalendarEvent_DB,\r\n updateCalendarEvent_DB,\r\n deleteCalendarEvent_DB,\r\n} from \"./event_db\";\r\nimport { getTeamCalendarId } from \"../(calendar)/calendar\";\r\nimport { getUser } from \"../auth\";\r\nimport prisma from \"@/utils/prisma/prisma\";\r\nimport { getMembersForTeam } from \"../members\";\r\n\r\n// TODO: Can be set by user\r\nconst DEFAULT_TIMEZONE = \"Asia/Manila\";\r\n\r\n// Creates an event in a team's calendar.\r\nexport async function createCalendarEvent(\r\n teamId: string,\r\n eventDetails: {\r\n title: string;\r\n start: Date;\r\n end: Date;\r\n description?: string;\r\n },\r\n) {\r\n let leader_id: string;\r\n\r\n // validate and get user id\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: any) {\r\n console.error(`Error creating calendar: ${teamId}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || `Failed to create calendar: ${teamId}`,\r\n };\r\n }\r\n\r\n // get calendar id\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n try {\r\n const { client } = await getCalendarClient();\r\n\r\n // Note: Validation is handled by the caller (Zod schema at form level)\r\n\r\n // Fetch team members to add as attendees\r\n const membersResult = await getMembersForTeam(teamId);\r\n const attendees =\r\n membersResult.success && membersResult.members\r\n ? membersResult.members.map((m) => ({ email: m.email }))\r\n : [];\r\n\r\n // Create Event\r\n const response = await client.events.insert({\r\n calendarId: calendarId,\r\n sendUpdates: \"none\", // Don't send email notifications\r\n requestBody: {\r\n summary: eventDetails.title,\r\n description: eventDetails.description,\r\n start: {\r\n dateTime: eventDetails.start.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n end: {\r\n dateTime: eventDetails.end.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n attendees: attendees,\r\n },\r\n });\r\n\r\n const calendarEventId = response.data.id;\r\n\r\n // Check if event was created before db update\r\n if (!calendarEventId) {\r\n return {\r\n success: false,\r\n error: \"Failed to create event\",\r\n };\r\n }\r\n\r\n // save to database\r\n try {\r\n await createCalendarEvent_DB(\r\n calendarId,\r\n teamId,\r\n eventDetails,\r\n calendarEventId,\r\n );\r\n } catch (dbError: any) {\r\n console.error(\"DB Error. Rolling back Google Event...\", dbError.message);\r\n\r\n // Delete event from google calendar\r\n try {\r\n await client.events.delete({\r\n calendarId: calendarId,\r\n eventId: calendarEventId,\r\n });\r\n console.log(\"✅ Rollback successful: Google Event deleted.\");\r\n } catch (rollbackError) {\r\n // Rollback error\r\n console.error(\"System data is inconsistent.\", rollbackError);\r\n // TODO: Send alert to admin / deadlock queue\r\n }\r\n\r\n throw new Error(`System Error: Failed to save event. ${dbError.message}`);\r\n }\r\n\r\n return {\r\n success: true,\r\n eventId: response.data.id,\r\n eventLink: response.data.htmlLink,\r\n };\r\n } catch (error: any) {\r\n console.error(\"Error creating event:\", error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Failed to create event\",\r\n };\r\n }\r\n}\r\n// Get team events (google)\r\nexport async function getTeamEvents(teamId: string) {\r\n // Get user\r\n let leader_id: string;\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: any) {\r\n console.error(`Error in getTeamEvents: ${teamId}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Authentication failed\",\r\n };\r\n }\r\n\r\n // Resolve calendarId from teamId\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n\r\n try {\r\n const { client } = await getCalendarClient();\r\n const response = await client.events.list({\r\n calendarId: calendarId,\r\n });\r\n return {\r\n success: true,\r\n events: response.data.items,\r\n };\r\n } catch (error: any) {\r\n console.error(\"Error getting events:\", error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Failed to get events\",\r\n };\r\n }\r\n}\r\nexport async function updateCalendarEvent(\r\n teamId: string,\r\n eventData: {\r\n id: string;\r\n title?: string;\r\n start?: Date;\r\n end?: Date;\r\n googleEventId?: string | null;\r\n desc?: string;\r\n },\r\n) {\r\n // Extract and validate required fields\r\n const { id: eventId, title, start, end, googleEventId, desc } = eventData;\r\n\r\n // Map desc to description for DB layer\r\n const description = desc;\r\n\r\n if (!eventId) {\r\n return {\r\n success: false,\r\n error: \"Missing required field: eventId is required for update\",\r\n };\r\n }\r\n if (!title || !start || !end) {\r\n return {\r\n success: false,\r\n error:\r\n \"Missing required fields: title, start, and end are required for update\",\r\n };\r\n }\r\n\r\n const eventDetails = { title, start, end, description };\r\n let leader_id: string;\r\n // validate and get user id\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: any) {\r\n console.error(`Error creating calendar: ${teamId}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || `Failed to create calendar: ${teamId}`,\r\n };\r\n }\r\n\r\n // get calendar id\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n\r\n // Validate googleEventId for Google API call\r\n if (!googleEventId) {\r\n return {\r\n success: false,\r\n error: \"Missing googleEventId: cannot update event in Google Calendar\",\r\n };\r\n }\r\n\r\n try {\r\n const { client } = await getCalendarClient();\r\n\r\n // Fetch team members to add as attendees\r\n const membersResult = await getMembersForTeam(teamId);\r\n const attendees =\r\n membersResult.success && membersResult.members\r\n ? membersResult.members.map((m) => ({ email: m.email }))\r\n : [];\r\n\r\n const response = await client.events.update({\r\n calendarId: calendarId,\r\n eventId: googleEventId,\r\n sendUpdates: \"none\", // Don't send email notifications\r\n requestBody: {\r\n summary: eventDetails.title,\r\n description: eventDetails.description,\r\n start: {\r\n dateTime: eventDetails.start.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n end: {\r\n dateTime: eventDetails.end.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n attendees: attendees,\r\n },\r\n });\r\n // Check if event was updated before db update\r\n if (!response.data.id) {\r\n return {\r\n success: false,\r\n error: \"Failed to update event\",\r\n };\r\n }\r\n // Save changes to DB\r\n try {\r\n await updateCalendarEvent_DB(eventId, eventDetails);\r\n } catch (dbError: any) {\r\n console.error(\r\n \"❌ DB Consistency Failure during Update. Google Event updated, but DB failed.\",\r\n dbError.message,\r\n );\r\n // NOTE: Rolling back an update is complex (needs old data).\r\n // For now, we log the inconsistency.\r\n throw new Error(\r\n `System Error: Failed to update event in DB. ${dbError.message}`,\r\n );\r\n }\r\n\r\n return {\r\n success: true,\r\n eventId: response.data.id,\r\n eventLink: response.data.htmlLink,\r\n };\r\n } catch (error: any) {\r\n console.error(\"Error updating event:\", error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Failed to update event\",\r\n };\r\n }\r\n}\r\nexport async function deleteCalendarEvent(teamId: string, eventId: string) {\r\n // Get user\r\n let leader_id: string;\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: any) {\r\n console.error(`Error in deleteCalendarEvent: ${teamId}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Authentication failed\",\r\n };\r\n }\r\n\r\n // Resolve calendarId from teamId\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n\r\n // Fetch event from DB to get googleEventId\r\n let dbEvent;\r\n try {\r\n dbEvent = await prisma.events.findUnique({\r\n where: { id: eventId },\r\n select: { google_event_id: true },\r\n });\r\n } catch (dbError: any) {\r\n console.error(\r\n \"Database query failed in deleteCalendarEvent:\",\r\n dbError.message,\r\n );\r\n return {\r\n success: false,\r\n error: `Database error: ${dbError.message}`,\r\n };\r\n }\r\n\r\n if (!dbEvent || !dbEvent.google_event_id) {\r\n return {\r\n success: false,\r\n error: \"Event not found or missing Google Event ID\",\r\n };\r\n }\r\n\r\n try {\r\n const { client } = await getCalendarClient();\r\n await client.events.delete({\r\n calendarId: calendarId,\r\n eventId: dbEvent.google_event_id,\r\n });\r\n try {\r\n await deleteCalendarEvent_DB(eventId);\r\n } catch (dbError: any) {\r\n console.error(\r\n \"❌ DB Consistency Failure during Delete. Google Event deleted, but DB failed (Zombie Data).\",\r\n dbError.message,\r\n );\r\n // NOTE: Rolling back a delete means RE-CREATING the event (new ID).\r\n // For now, we log the inconsistency.\r\n throw new Error(\r\n `System Error: Failed to delete event in DB. ${dbError.message}`,\r\n );\r\n }\r\n\r\n return {\r\n success: true,\r\n };\r\n } catch (error: any) {\r\n console.error(\"Error deleting event:\", error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Failed to delete event\",\r\n };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(events)\\event_db.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(events)\\google.ts","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":47,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":47,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1487,1490],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1487,1490],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use server\";\r\nimport { google } from \"googleapis\";\r\nimport { getUser } from \"../auth\";\r\nimport prisma from \"@/utils/prisma/prisma\";\r\nimport { getOAuth2Client } from \"@/lib/google-calendar\";\r\n\r\n// Nullify to break auth loops\r\nasync function nullifyToken(userId: string) {\r\n console.warn(`Nullifying invalid token for user ${userId}`);\r\n await prisma.profiles.update({\r\n where: { id: userId },\r\n data: { google_refresh_token: null },\r\n });\r\n}\r\n\r\n// Gets calendar client, if invalid token (400) nullify and re-auth\r\nexport async function getCalendarClient() {\r\n const user = await getUser();\r\n const userId = user.data.user?.id;\r\n\r\n if (!userId) throw new Error(\"User not authenticated\");\r\n\r\n const profile = await prisma.profiles.findUnique({\r\n where: { id: userId },\r\n select: { google_refresh_token: true },\r\n });\r\n\r\n if (!profile?.google_refresh_token) {\r\n throw new Error(\"No Google refresh token found. Please reconnect.\");\r\n }\r\n\r\n const oauth2Client = getOAuth2Client();\r\n oauth2Client.setCredentials({ refresh_token: profile.google_refresh_token });\r\n\r\n return {\r\n client: google.calendar({ version: \"v3\", auth: oauth2Client }),\r\n userId,\r\n };\r\n}\r\n\r\n// Checks permission, if error, nullify and re-auth\r\nexport async function checkCalendarPermissions() {\r\n try {\r\n const { client } = await getCalendarClient();\r\n await client.calendarList.list({ maxResults: 1 });\r\n return { hasValidToken: true, needsReauth: false };\r\n } catch (error: any) {\r\n console.error(\"Permission check failed:\", error?.message);\r\n\r\n // If any auth error, we need re-auth\r\n const isAuthError =\r\n [400, 401, 403].includes(error?.code) ||\r\n error?.response?.data?.error === \"invalid_request\";\r\n\r\n if (isAuthError) {\r\n const user = await getUser();\r\n const userId = user.data.user?.id;\r\n if (userId) await nullifyToken(userId);\r\n return { hasValidToken: false, needsReauth: true };\r\n }\r\n\r\n return { hasValidToken: false, needsReauth: true };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\auth.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\members.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\teams.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\actions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\AboutBtn.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\ErrorMsg.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\SignInButton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\Star.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\404\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\about\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\api\\webhooks\\resend\\route.ts","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":42,"column":18,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":42,"endColumn":21,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1337,1340],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1337,1340],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":105,"column":14,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":105,"endColumn":17,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[3275,3278],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[3275,3278],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { NextRequest, NextResponse } from \"next/server\";\r\nimport prisma from \"@/utils/prisma/prisma\";\r\nimport { EmailStatus } from \"@prisma/client\";\r\nimport { Webhook } from \"svix\";\r\n\r\n/**\r\n * Resend webhook handler\r\n * Receives delivery status updates from Resend\r\n *\r\n * Events: email.sent, email.delivered, email.bounced, email.complained\r\n */\r\nexport async function POST(request: NextRequest) {\r\n try {\r\n const webhookSecret = process.env.RESEND_WEBHOOK_SECRET;\r\n\r\n if (!webhookSecret) {\r\n console.error(\"RESEND_WEBHOOK_SECRET not configured\");\r\n return NextResponse.json(\r\n { error: \"Webhook not configured\" },\r\n { status: 500 },\r\n );\r\n }\r\n\r\n // Get webhook headers for signature verification\r\n const svixId = request.headers.get(\"svix-id\");\r\n const svixTimestamp = request.headers.get(\"svix-timestamp\");\r\n const svixSignature = request.headers.get(\"svix-signature\");\r\n\r\n if (!svixId || !svixTimestamp || !svixSignature) {\r\n console.warn(\"Missing svix headers\");\r\n return NextResponse.json(\r\n { error: \"Missing webhook headers\" },\r\n { status: 401 },\r\n );\r\n }\r\n\r\n // Get raw body for signature verification\r\n const body = await request.text();\r\n\r\n // Verify webhook signature\r\n const wh = new Webhook(webhookSecret);\r\n let payload: any;\r\n\r\n try {\r\n payload = wh.verify(body, {\r\n \"svix-id\": svixId,\r\n \"svix-timestamp\": svixTimestamp,\r\n \"svix-signature\": svixSignature,\r\n });\r\n } catch (err) {\r\n console.error(\"Webhook signature verification failed:\", err);\r\n return NextResponse.json({ error: \"Invalid signature\" }, { status: 401 });\r\n }\r\n\r\n // Parse verified payload\r\n const { type, data } = payload;\r\n\r\n console.log(\"Received webhook:\", type, data);\r\n\r\n // Extract batch ID from the event\r\n // Note: Resend's webhook structure may vary, adjust as needed\r\n const batchId = data?.batch_id || data?.id;\r\n\r\n if (!batchId) {\r\n console.warn(\"No batch ID in webhook payload\");\r\n return NextResponse.json({ received: true }, { status: 200 });\r\n }\r\n\r\n // Find announcement by batch ID\r\n // Handle both single ID and JSON array of IDs\r\n const announcements = await prisma.announcements.findMany({\r\n where: {\r\n OR: [\r\n { resend_batch_id: batchId },\r\n { resend_batch_id: { contains: batchId } },\r\n ],\r\n },\r\n });\r\n\r\n if (announcements.length === 0) {\r\n console.warn(`No announcement found for batch ID: ${batchId}`);\r\n // Return 200 to acknowledge webhook (idempotent)\r\n return NextResponse.json({ received: true }, { status: 200 });\r\n }\r\n\r\n // Process each announcement\r\n for (const announcement of announcements) {\r\n await processWebhookEvent(announcement.id, type, data);\r\n }\r\n\r\n return NextResponse.json({ received: true }, { status: 200 });\r\n } catch (error) {\r\n console.error(\"Error processing webhook:\", error);\r\n // Always return 200 to prevent Resend from retrying\r\n return NextResponse.json({ received: true }, { status: 200 });\r\n }\r\n}\r\n\r\n/**\r\n * Process individual webhook event\r\n */\r\nasync function processWebhookEvent(\r\n announcementId: bigint,\r\n eventType: string,\r\n eventData: any,\r\n) {\r\n try {\r\n const announcement = await prisma.announcements.findUnique({\r\n where: { id: announcementId },\r\n });\r\n\r\n if (!announcement) return;\r\n\r\n switch (eventType) {\r\n case \"email.sent\":\r\n // Email accepted by Resend, no action needed\r\n break;\r\n\r\n case \"email.delivered\":\r\n // Increment delivered count\r\n await prisma.announcements.update({\r\n where: { id: announcementId },\r\n data: {\r\n delivered_count: {\r\n increment: 1,\r\n },\r\n },\r\n });\r\n\r\n // Check if all emails delivered\r\n const updated = await prisma.announcements.findUnique({\r\n where: { id: announcementId },\r\n });\r\n\r\n if (\r\n updated &&\r\n updated.delivered_count >= updated.recipient_count &&\r\n updated.email_status === EmailStatus.SENDING\r\n ) {\r\n await prisma.announcements.update({\r\n where: { id: announcementId },\r\n data: {\r\n email_status: EmailStatus.SENT,\r\n },\r\n });\r\n }\r\n break;\r\n\r\n case \"email.bounced\":\r\n case \"email.complained\":\r\n // Handle failures\r\n const failedEmail = eventData?.email || \"unknown\";\r\n const currentErrors = announcement.error_message || \"\";\r\n const failedEmails = currentErrors ? JSON.parse(currentErrors) : [];\r\n\r\n if (!failedEmails.includes(failedEmail)) {\r\n failedEmails.push(failedEmail);\r\n }\r\n\r\n await prisma.announcements.update({\r\n where: { id: announcementId },\r\n data: {\r\n error_message: JSON.stringify(failedEmails),\r\n email_status:\r\n failedEmails.length > 0\r\n ? EmailStatus.PARTIALLY_FAILED\r\n : announcement.email_status,\r\n },\r\n });\r\n break;\r\n\r\n default:\r\n console.log(`Unhandled event type: ${eventType}`);\r\n }\r\n } catch (error) {\r\n console.error(\"Error processing webhook event:\", error);\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\auth\\auth-code-error\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\auth\\callback\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\CalendarPermissionsBanner.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\NewTeamBtn.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\SignOutBtn.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\WelcomeMsg.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\ActionsMenu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\ConfirmationModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\EditTeamModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\Pagination.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\SearchInput.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\TeamTable.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\data.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\loading.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\AnnouncementCard.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\AnnouncementsClient.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'err' is defined but never used.","line":49,"column":14,"nodeType":null,"messageId":"unusedVar","endLine":49,"endColumn":17},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'loadAnnouncements'. Either include it or remove the dependency array.","line":58,"column":6,"nodeType":"ArrayExpression","endLine":58,"endColumn":14,"suggestions":[{"desc":"Update the dependencies array to be: [loadAnnouncements, teamId]","fix":{"range":[1678,1686],"text":"[loadAnnouncements, teamId]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport { useState, useEffect, useMemo } from \"react\";\r\nimport { getAnnouncementsForTeam } from \"@/actions/(announcements)/crud\";\r\nimport CreateAnnouncementDialog from \"./CreateAnnouncementDialog\";\r\nimport AnnouncementCard from \"./AnnouncementCard\";\r\nimport { EmailStatus } from \"@prisma/client\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\r\n\r\ninterface AnnouncementsClientProps {\r\n teamId: string;\r\n}\r\n\r\ninterface Announcement {\r\n id: bigint;\r\n title: string;\r\n content: string;\r\n created_at: Date;\r\n email_status: EmailStatus | null;\r\n sent_at: Date | null;\r\n recipient_count: number;\r\n delivered_count: number;\r\n error_message: string | null;\r\n}\r\n\r\nconst ITEMS_PER_PAGE = 5;\r\n\r\nexport default function AnnouncementsClient({\r\n teamId,\r\n}: AnnouncementsClientProps) {\r\n const [announcements, setAnnouncements] = useState([]);\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [error, setError] = useState(null);\r\n const [currentPage, setCurrentPage] = useState(1);\r\n\r\n async function loadAnnouncements() {\r\n setIsLoading(true);\r\n setError(null);\r\n\r\n try {\r\n const result = await getAnnouncementsForTeam(teamId);\r\n\r\n if (result.success && result.announcements) {\r\n setAnnouncements(result.announcements as Announcement[]);\r\n } else {\r\n setError(result.error || \"Failed to load announcements\");\r\n }\r\n } catch (err) {\r\n setError(\"An error occurred while loading announcements\");\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }\r\n\r\n useEffect(() => {\r\n loadAnnouncements();\r\n }, [teamId]);\r\n\r\n // Calculate pagination\r\n const totalPages = Math.ceil(announcements.length / ITEMS_PER_PAGE);\r\n const paginatedAnnouncements = useMemo(() => {\r\n const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;\r\n return announcements.slice(startIndex, startIndex + ITEMS_PER_PAGE);\r\n }, [announcements, currentPage]);\r\n\r\n // Reset to page 1 when announcements change\r\n useEffect(() => {\r\n setCurrentPage(1);\r\n }, [announcements.length]);\r\n\r\n if (isLoading) {\r\n return (\r\n
\r\n {/* Skeleton cards */}\r\n {[1, 2].map((i) => (\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n ))}\r\n
\r\n );\r\n }\r\n\r\n if (error) {\r\n return (\r\n
\r\n

{error}

\r\n
\r\n );\r\n }\r\n\r\n return (\r\n
\r\n {/* Header */}\r\n
\r\n
\r\n

\r\n Announcements\r\n

\r\n \r\n
\r\n \r\n
\r\n\r\n {/* Announcements List */}\r\n {announcements.length === 0 ? (\r\n
\r\n

No announcements yet

\r\n

\r\n Create your first announcement to get started\r\n

\r\n
\r\n ) : (\r\n <>\r\n
\r\n {paginatedAnnouncements.map((announcement) => (\r\n \r\n ))}\r\n
\r\n\r\n {/* Pagination */}\r\n {totalPages > 1 && (\r\n
\r\n

\r\n Page {currentPage} of {totalPages} ({announcements.length}{\" \"}\r\n total)\r\n

\r\n
\r\n setCurrentPage((p) => Math.max(1, p - 1))}\r\n disabled={currentPage === 1}\r\n className=\"px-2\"\r\n >\r\n \r\n Previous\r\n \r\n \r\n setCurrentPage((p) => Math.min(totalPages, p + 1))\r\n }\r\n disabled={currentPage === totalPages}\r\n className=\"px-2\"\r\n >\r\n Next\r\n \r\n \r\n
\r\n
\r\n )}\r\n \r\n )}\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\CreateAnnouncementDialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\EmailStatusBadge.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\(actions)\\useCalendarActions.ts","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nC:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\(actions)\\useCalendarActions.ts:53:5\n 51 | // 5. Initial Load\n 52 | useEffect(() => {\n> 53 | loadEvents();\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 54 | }, [loadEvents]);\n 55 | const handleCreate = useCallback(\n 56 | async (data: CreateEventInput) => {","line":53,"column":5,"nodeType":null,"endLine":53,"endColumn":15}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useState, useCallback, useEffect } from \"react\";\r\nimport { View } from \"react-big-calendar\";\r\nimport {\r\n CalendarEvent,\r\n CreateEventInput,\r\n UpdateEventInput,\r\n} from \"@/lib/validations\";\r\nimport {\r\n createCalendarEvent,\r\n updateCalendarEvent,\r\n deleteCalendarEvent,\r\n} from \"@/actions/(events)/event\";\r\nimport { getTeamEvents_DB } from \"@/actions/(events)/event_db\";\r\nimport toast from \"react-hot-toast\";\r\n\r\nexport function useTeamCalendar(teamId: string) {\r\n // 1. Data State\r\n const [events, setEvents] = useState([]);\r\n const [isLoading, setIsLoading] = useState(true);\r\n\r\n // 2. Calendar View State\r\n const [date, setDate] = useState(new Date());\r\n const [view, setView] = useState(\"month\");\r\n\r\n // 3. Modal State\r\n const [modal, setModal] = useState<{\r\n isOpen: boolean;\r\n mode: \"create\" | \"edit\";\r\n selectedEvent: CalendarEvent | null;\r\n }>({\r\n isOpen: false,\r\n mode: \"create\",\r\n selectedEvent: null,\r\n });\r\n\r\n // 4. Load Events (extracted for refetch)\r\n const loadEvents = useCallback(async () => {\r\n setIsLoading(true);\r\n\r\n try {\r\n const events = await getTeamEvents_DB(teamId);\r\n setEvents(events);\r\n } catch (error) {\r\n console.error(\"Failed to load events:\", error);\r\n setEvents([]);\r\n }\r\n\r\n setIsLoading(false);\r\n }, [teamId]);\r\n\r\n // 5. Initial Load\r\n useEffect(() => {\r\n loadEvents();\r\n }, [loadEvents]);\r\n const handleCreate = useCallback(\r\n async (data: CreateEventInput) => {\r\n const toastId = toast.loading(\"Creating event...\");\r\n try {\r\n // Map desc to description for server action\r\n const result = await createCalendarEvent(teamId, {\r\n title: data.title,\r\n start: data.start,\r\n end: data.end,\r\n description: data.desc,\r\n });\r\n if (result.success) {\r\n toast.success(\"Event created successfully!\", { id: toastId });\r\n await loadEvents(); // Refetch canonical list\r\n } else {\r\n toast.error(result.error || \"Failed to create event\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\r\n error instanceof Error ? error.message : \"Failed to create event\",\r\n { id: toastId },\r\n );\r\n }\r\n },\r\n [teamId, loadEvents],\r\n );\r\n const handleUpdate = useCallback(\r\n async (data: UpdateEventInput) => {\r\n const toastId = toast.loading(\"Updating event...\");\r\n try {\r\n const result = await updateCalendarEvent(teamId, data);\r\n if (result.success) {\r\n toast.success(\"Event updated successfully!\", { id: toastId });\r\n await loadEvents(); // Refetch canonical list\r\n } else {\r\n toast.error(result.error || \"Failed to update event\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\r\n error instanceof Error ? error.message : \"Failed to update event\",\r\n { id: toastId },\r\n );\r\n }\r\n },\r\n [teamId, loadEvents],\r\n );\r\n const handleDelete = useCallback(\r\n async (id: string) => {\r\n const toastId = toast.loading(\"Deleting event...\");\r\n try {\r\n const result = await deleteCalendarEvent(teamId, id);\r\n if (result.success) {\r\n toast.success(\"Event deleted successfully!\", { id: toastId });\r\n await loadEvents(); // Refetch canonical list\r\n } else {\r\n toast.error(result.error || \"Failed to delete event\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\r\n error instanceof Error ? error.message : \"Failed to delete event\",\r\n { id: toastId },\r\n );\r\n }\r\n },\r\n [teamId, loadEvents],\r\n );\r\n\r\n // 5. Actions (Scaffolded for User Implementation)\r\n const openCreate = useCallback(() => {\r\n setModal({ isOpen: true, mode: \"create\", selectedEvent: null });\r\n }, []);\r\n\r\n const openEdit = useCallback((event: CalendarEvent) => {\r\n setModal({ isOpen: true, mode: \"edit\", selectedEvent: event });\r\n }, []);\r\n\r\n const close = useCallback(() => {\r\n setModal((prev) => ({ ...prev, isOpen: false }));\r\n }, []);\r\n\r\n const submit = useCallback(\r\n async (data: CreateEventInput | UpdateEventInput) => {\r\n close(); // Close modal before showing toasts\r\n if (modal.mode === \"create\") {\r\n await handleCreate(data as CreateEventInput);\r\n } else if (modal.mode === \"edit\") {\r\n await handleUpdate(data as UpdateEventInput);\r\n }\r\n },\r\n [modal.mode, close, handleCreate, handleUpdate],\r\n );\r\n\r\n const remove = useCallback(\r\n async (id: string) => {\r\n close(); // Close modal before showing toasts\r\n await handleDelete(id);\r\n },\r\n [close, handleDelete],\r\n );\r\n\r\n return {\r\n // State\r\n events,\r\n isLoading,\r\n date,\r\n view,\r\n modal,\r\n\r\n // Setters\r\n setDate,\r\n setView,\r\n\r\n // Actions\r\n actions: {\r\n openCreate,\r\n openEdit,\r\n close,\r\n submit,\r\n remove,\r\n },\r\n };\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\CalendarToolbar.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'date' is assigned a value but never used.","line":17,"column":5,"nodeType":null,"messageId":"unusedVar","endLine":17,"endColumn":9},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'btnActive' is assigned a value but never used.","line":40,"column":9,"nodeType":null,"messageId":"unusedVar","endLine":40,"endColumn":18}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { View, ToolbarProps } from \"react-big-calendar\";\r\nimport { CalendarEvent } from \"@/lib/validations\";\r\nimport { ChevronDown, ChevronLeft, ChevronRight, Plus } from \"lucide-react\";\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from \"@/components/ui/DropdownMenu\";\r\n\r\ninterface CalendarToolbarProps extends ToolbarProps {\r\n onAddEvent?: () => void;\r\n}\r\n\r\nexport default function CalendarToolbar(props: CalendarToolbarProps) {\r\n const {\r\n date,\r\n view,\r\n views,\r\n label,\r\n onView,\r\n onNavigate,\r\n localizer,\r\n onAddEvent,\r\n } = props;\r\n\r\n const navigate = (action: \"PREV\" | \"NEXT\" | \"TODAY\") => {\r\n onNavigate(action);\r\n };\r\n\r\n const currentView = view;\r\n const availableViews = views as View[];\r\n\r\n // Shared button styles\r\n const btnBase =\r\n \"relative inline-flex items-center px-4 py-2 border border-[#e5e7eb] bg-white text-sm font-semibold text-[#111827] hover:bg-[#f9fafb] focus:z-10 focus:outline-none transition-colors\";\r\n const btnFirst = \"rounded-l-md\";\r\n const btnLast = \"rounded-r-md\";\r\n const btnMiddle = \"-ml-px\";\r\n const btnActive =\r\n \"bg-[#2563eb] text-white border-[#2563eb] hover:bg-[#1d4ed8] z-20\";\r\n\r\n return (\r\n
\r\n {/* Mobile Layout: Row 1 (Nav + View), Row 2 (Label) */}\r\n
\r\n
\r\n {/* Navigation Group - Connected Buttons */}\r\n
\r\n navigate(\"TODAY\")}\r\n >\r\n Today\r\n \r\n navigate(\"PREV\")}\r\n aria-label=\"Previous range\"\r\n >\r\n \r\n <\r\n \r\n \r\n navigate(\"NEXT\")}\r\n aria-label=\"Next range\"\r\n >\r\n \r\n >\r\n \r\n \r\n
\r\n\r\n {/* Mobile View Selector + Add Button */}\r\n
\r\n \r\n \r\n \r\n \r\n {localizer.messages[currentView] || currentView}\r\n \r\n \r\n \r\n \r\n \r\n {availableViews.map((v) => (\r\n onView(v)}\r\n className={`\r\n cursor-pointer px-3 py-2 font-medium capitalize rounded-sm text-sm\r\n hover:bg-[#f3f4f6] focus:bg-[#f3f4f6]\r\n ${currentView === v ? \"bg-[#eff6ff] text-[#2563eb]\" : \"text-[#111827]\"}\r\n `}\r\n >\r\n {localizer.messages[v] || v}\r\n \r\n ))}\r\n \r\n \r\n {onAddEvent && (\r\n \r\n \r\n Add Event\r\n \r\n )}\r\n
\r\n
\r\n\r\n {/* Label (Mobile) */}\r\n \r\n {label}\r\n \r\n
\r\n\r\n {/* Desktop Layout */}\r\n
\r\n {/* Navigation Group */}\r\n
\r\n navigate(\"TODAY\")}\r\n >\r\n Today\r\n \r\n navigate(\"PREV\")}\r\n aria-label=\"Previous range\"\r\n >\r\n \r\n \r\n navigate(\"NEXT\")}\r\n aria-label=\"Next range\"\r\n >\r\n \r\n \r\n
\r\n\r\n {/* Label (Desktop) */}\r\n \r\n {label}\r\n \r\n\r\n {/* View Selector Dropdown + Add Button (Desktop - matches mobile) */}\r\n
\r\n \r\n \r\n \r\n \r\n {localizer.messages[currentView] || currentView}\r\n \r\n \r\n \r\n \r\n \r\n {availableViews.map((v) => (\r\n onView(v)}\r\n className={`\r\n cursor-pointer px-3 py-2 font-medium capitalize rounded-sm text-sm\r\n hover:bg-[#f3f4f6] focus:bg-[#f3f4f6]\r\n ${currentView === v ? \"bg-[#eff6ff] text-[#2563eb]\" : \"text-[#111827]\"}\r\n `}\r\n >\r\n {localizer.messages[v] || v}\r\n \r\n ))}\r\n \r\n \r\n {onAddEvent && (\r\n \r\n \r\n Add Event\r\n \r\n )}\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\EventModal.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nC:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\EventModal.tsx:50:7\n 48 | useEffect(() => {\n 49 | if (mode === \"edit\" && event) {\n> 50 | setTitle(event.title);\n | ^^^^^^^^ Avoid calling setState() directly within an effect\n 51 | setStartStr(format(event.start, \"yyyy-MM-dd'T'HH:mm\"));\n 52 | setEndStr(format(event.end, \"yyyy-MM-dd'T'HH:mm\"));\n 53 | setDesc(event.desc || \"\");","line":50,"column":7,"nodeType":null,"endLine":50,"endColumn":15}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useState, useEffect } from \"react\";\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogDescription,\r\n DialogFooter,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogOverlay,\r\n} from \"@/components/ui/Dialog\";\r\nimport { Input } from \"@/components/ui/Input\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport {\r\n CalendarEvent,\r\n CreateEventInput,\r\n UpdateEventInput,\r\n CreateEventSchema,\r\n UpdateEventSchema,\r\n} from \"@/lib/validations\";\r\nimport { format } from \"date-fns\";\r\nimport { inter, instrumentSerif } from \"@/app/fonts\";\r\nimport toast from \"react-hot-toast\";\r\n\r\ninterface EventModalProps {\r\n isOpen: boolean;\r\n mode: \"create\" | \"edit\";\r\n event: CalendarEvent | null;\r\n onClose: () => void;\r\n onSubmit: (data: CreateEventInput | UpdateEventInput) => Promise;\r\n onDelete?: (id: string) => Promise;\r\n}\r\n\r\nexport default function EventModal({\r\n isOpen,\r\n mode,\r\n event,\r\n onClose,\r\n onSubmit,\r\n onDelete,\r\n}: EventModalProps) {\r\n const [title, setTitle] = useState(\"\");\r\n const [startStr, setStartStr] = useState(\"\");\r\n const [endStr, setEndStr] = useState(\"\");\r\n const [desc, setDesc] = useState(\"\");\r\n const [errors, setErrors] = useState>({});\r\n\r\n // Sync state with event data when editing\r\n useEffect(() => {\r\n if (mode === \"edit\" && event) {\r\n setTitle(event.title);\r\n setStartStr(format(event.start, \"yyyy-MM-dd'T'HH:mm\"));\r\n setEndStr(format(event.end, \"yyyy-MM-dd'T'HH:mm\"));\r\n setDesc(event.desc || \"\");\r\n } else {\r\n setTitle(\"\");\r\n setStartStr(\"\");\r\n setEndStr(\"\");\r\n setDesc(\"\");\r\n }\r\n setErrors({});\r\n }, [mode, event, isOpen]);\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setErrors({});\r\n\r\n if (mode === \"create\") {\r\n const data = {\r\n title,\r\n start: new Date(startStr),\r\n end: endStr ? new Date(endStr) : undefined, // Allow empty end date\r\n desc: desc || undefined,\r\n };\r\n\r\n // Validate with Zod\r\n const result = CreateEventSchema.safeParse(data);\r\n if (!result.success) {\r\n const fieldErrors: Record = {};\r\n result.error.issues.forEach((err) => {\r\n const path = err.path.join(\".\");\r\n fieldErrors[path] = err.message;\r\n });\r\n setErrors(fieldErrors);\r\n toast.error(\"Please fix the validation errors\");\r\n return;\r\n }\r\n\r\n await onSubmit(result.data);\r\n } else {\r\n const data = {\r\n id: event!.id,\r\n title,\r\n start: new Date(startStr),\r\n end: endStr ? new Date(endStr) : undefined, // Allow empty end date\r\n desc: desc || undefined,\r\n googleEventId: event!.googleEventId,\r\n };\r\n\r\n // Validate with Zod\r\n const result = UpdateEventSchema.safeParse(data);\r\n if (!result.success) {\r\n const fieldErrors: Record = {};\r\n result.error.issues.forEach((err) => {\r\n const path = err.path.join(\".\");\r\n fieldErrors[path] = err.message;\r\n });\r\n setErrors(fieldErrors);\r\n toast.error(\"Please fix the validation errors\");\r\n return;\r\n }\r\n\r\n await onSubmit(result.data);\r\n }\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n {mode === \"create\" ? \"📅 Add New Event\" : \"📝 Edit Event\"}\r\n \r\n \r\n {mode === \"create\"\r\n ? \"Enter the details for the new team event.\"\r\n : \"Update the details of this event.\"}\r\n \r\n \r\n\r\n
\r\n
\r\n \r\n setTitle(e.target.value)}\r\n placeholder=\"e.g. Design Sprint\"\r\n className={errors.title ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.title && (\r\n

{errors.title}

\r\n )}\r\n
\r\n
\r\n
\r\n \r\n setStartStr(e.target.value)}\r\n className={errors.start ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.start && (\r\n

{errors.start}

\r\n )}\r\n
\r\n
\r\n \r\n setEndStr(e.target.value)}\r\n className={errors.end ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.end && (\r\n

{errors.end}

\r\n )}\r\n
\r\n
\r\n
\r\n \r\n setDesc(e.target.value)}\r\n placeholder=\"Short description\"\r\n className={errors.desc ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.desc && (\r\n

{errors.desc}

\r\n )}\r\n
\r\n\r\n \r\n {mode === \"edit\" && onDelete && (\r\n onDelete(event!.id)}\r\n >\r\n Delete\r\n \r\n )}\r\n
\r\n \r\n Cancel\r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\TeamCalendar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\AddMemberModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\EditMemberModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\ImportMembersModal.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'AlertDialogAction' is defined but never used.","line":5,"column":3,"nodeType":null,"messageId":"unusedVar","endLine":5,"endColumn":20},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":83,"column":18,"nodeType":null,"messageId":"unusedVar","endLine":83,"endColumn":23}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\nimport { useState, useRef } from \"react\";\r\nimport {\r\n AlertDialog,\r\n AlertDialogAction,\r\n AlertDialogCancel,\r\n AlertDialogContent,\r\n AlertDialogFooter,\r\n AlertDialogHeader,\r\n AlertDialogTitle,\r\n AlertDialogDescription,\r\n} from \"@/components/ui/AlertDialog\";\r\nimport { Upload, FileText, AlertCircle } from \"lucide-react\";\r\nimport { importMembersToTeam } from \"@/actions/members\";\r\nimport toast from \"react-hot-toast\";\r\n\r\ninterface ImportMembersModalProps {\r\n teamId: string;\r\n open: boolean;\r\n onOpenChange: (open: boolean) => void;\r\n onSuccess?: () => void;\r\n}\r\n\r\ninterface ImportResult {\r\n success: boolean;\r\n added: number;\r\n failed: number;\r\n errors?: string[];\r\n}\r\n\r\nfunction ImportMembersModal({\r\n teamId,\r\n open,\r\n onOpenChange,\r\n onSuccess,\r\n}: ImportMembersModalProps) {\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [selectedFile, setSelectedFile] = useState(null);\r\n const [fileError, setFileError] = useState(undefined);\r\n const fileInputRef = useRef(null);\r\n\r\n const handleFileSelect = (e: React.ChangeEvent) => {\r\n const file = e.target.files?.[0];\r\n if (file) {\r\n if (file.type !== \"text/csv\" && !file.name.endsWith(\".csv\")) {\r\n setFileError(\"Please select a CSV file\");\r\n setSelectedFile(null);\r\n return;\r\n }\r\n setSelectedFile(file);\r\n setFileError(undefined); // Clear error when valid file selected\r\n }\r\n };\r\n\r\n const parseCSV = async (\r\n file: File,\r\n ): Promise> => {\r\n return new Promise((resolve, reject) => {\r\n const reader = new FileReader();\r\n reader.onload = (e) => {\r\n try {\r\n const text = e.target?.result as string;\r\n const lines = text\r\n .split(\"\\n\")\r\n .map((line) => line.trim())\r\n .filter((line) => line.length > 0); // Remove empty lines\r\n\r\n if (lines.length <= 1) {\r\n // Only header or empty file\r\n resolve([]);\r\n return;\r\n }\r\n\r\n // Skip header row\r\n const dataLines = lines.slice(1);\r\n\r\n const members = dataLines.map((line) => {\r\n const [email, full_name] = line.split(\",\").map((s) => s.trim());\r\n return { email, full_name: full_name || undefined };\r\n });\r\n\r\n resolve(members);\r\n } catch (error) {\r\n reject(new Error(\"Failed to parse CSV file\"));\r\n }\r\n };\r\n reader.onerror = () => reject(new Error(\"Failed to read file\"));\r\n reader.readAsText(file);\r\n });\r\n };\r\n\r\n const handleImport = async () => {\r\n if (!selectedFile) {\r\n setFileError(\"Please select a file to import\");\r\n return; // Don't close modal\r\n }\r\n\r\n setFileError(undefined);\r\n setIsLoading(true);\r\n\r\n try {\r\n toast.loading(\"Parsing CSV file...\");\r\n const parsedMembers = await parseCSV(selectedFile);\r\n\r\n // Filter out invalid entries (empty email)\r\n const members = parsedMembers.filter((m) => m.email && m.email.trim());\r\n\r\n if (members.length === 0) {\r\n toast.dismiss();\r\n setFileError(\r\n \"No valid members found in CSV. Please check the file format.\",\r\n );\r\n setIsLoading(false);\r\n return; // Don't close modal\r\n }\r\n\r\n // Show warning if some entries were skipped\r\n if (members.length < parsedMembers.length) {\r\n const skipped = parsedMembers.length - members.length;\r\n toast.dismiss();\r\n toast(\r\n `Skipped ${skipped} invalid ${skipped === 1 ? \"entry\" : \"entries\"}. Importing ${members.length} members...`,\r\n { icon: \"⚠️\", duration: 3000 },\r\n );\r\n }\r\n\r\n toast.dismiss();\r\n toast.loading(`Importing ${members.length} members...`);\r\n\r\n const result = await importMembersToTeam(teamId, members);\r\n\r\n toast.dismiss();\r\n\r\n if (result.success) {\r\n const { added, failed } = result as ImportResult;\r\n if (failed > 0) {\r\n toast.success(\r\n `Imported ${added} members successfully. ${failed} failed.`,\r\n { duration: 5000 },\r\n );\r\n } else {\r\n toast.success(`Successfully imported ${added} members!`);\r\n }\r\n setSelectedFile(null);\r\n if (fileInputRef.current) fileInputRef.current.value = \"\";\r\n setFileError(undefined);\r\n onOpenChange(false); // Only close on success\r\n onSuccess?.();\r\n } else {\r\n setFileError(result.error || \"Failed to import members\");\r\n // Don't close modal - let user try again\r\n }\r\n } catch (error) {\r\n toast.dismiss();\r\n setFileError(\r\n error instanceof Error ? error.message : \"Failed to import members\",\r\n );\r\n // Don't close modal on error\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n Import Members\r\n \r\n \r\n Upload a CSV file with columns: email, full_name\r\n \r\n \r\n\r\n
\r\n
\r\n \r\n \r\n \r\n {selectedFile ? (\r\n <>\r\n \r\n

\r\n {selectedFile.name}\r\n

\r\n

\r\n Click to change file\r\n

\r\n \r\n ) : (\r\n <>\r\n \r\n

\r\n Click to upload CSV\r\n

\r\n

or drag and drop

\r\n \r\n )}\r\n \r\n
\r\n {fileError && (\r\n

{fileError}

\r\n )}\r\n
\r\n\r\n
\r\n \r\n
\r\n

CSV Format:

\r\n \r\n email,full_name\r\n
\r\n john@example.com,John Doe\r\n
\r\n jane@example.com,Jane Smith\r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n Cancel\r\n \r\n \r\n \r\n {isLoading ? \"Importing...\" : \"Import Members\"}\r\n \r\n \r\n \r\n \r\n );\r\n}\r\n\r\nexport default ImportMembersModal;\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\MemberActionsMenu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\MemberTable.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":82,"column":14,"nodeType":null,"messageId":"unusedVar","endLine":82,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport { DataTable, ColumnDef } from \"@/components/ui/DataTable\";\r\nimport { getMembersForTeam, removeMemberFromTeam } from \"@/actions/members\";\r\nimport { SearchInput } from \"@/app/dashboard/components/team-table/SearchInput\";\r\nimport { Pagination } from \"@/app/dashboard/components/team-table/Pagination\";\r\nimport { MemberActionsMenu } from \"./MemberActionsMenu\";\r\nimport ConfirmationModal from \"@/app/dashboard/components/team-table/ConfirmationModal\";\r\nimport EditMemberModal, { Member } from \"./EditMemberModal\";\r\nimport AddMemberModal from \"./AddMemberModal\";\r\nimport ImportMembersModal from \"./ImportMembersModal\";\r\nimport { MemberTableSkeleton } from \"./MemberTableSkeleton\";\r\nimport toast from \"react-hot-toast\";\r\n\r\nimport { UserPlus, Upload } from \"lucide-react\";\r\n\r\ninterface MemberTableProps {\r\n teamId: string;\r\n}\r\n\r\nexport default function MemberTable({ teamId }: MemberTableProps) {\r\n const [members, setMembers] = React.useState([]);\r\n const [isLoading, setIsLoading] = React.useState(true);\r\n const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);\r\n const [memberToRemove, setMemberToRemove] = React.useState(\r\n null,\r\n );\r\n const [editModalOpen, setEditModalOpen] = React.useState(false);\r\n const [memberToEdit, setMemberToEdit] = React.useState(null);\r\n const [addModalOpen, setAddModalOpen] = React.useState(false);\r\n const [importModalOpen, setImportModalOpen] = React.useState(false);\r\n\r\n // Fetch members on mount\r\n const fetchMembers = React.useCallback(async () => {\r\n setIsLoading(true);\r\n const result = await getMembersForTeam(teamId);\r\n if (result.success && result.members) {\r\n setMembers(result.members as Member[]);\r\n }\r\n setIsLoading(false);\r\n }, [teamId]);\r\n\r\n React.useEffect(() => {\r\n fetchMembers();\r\n }, [fetchMembers]);\r\n\r\n const handleRemoveClick = (member: Member) => {\r\n setMemberToRemove(member);\r\n setDeleteModalOpen(true);\r\n };\r\n\r\n const handleEditClick = (member: Member) => {\r\n setMemberToEdit(member);\r\n setEditModalOpen(true);\r\n };\r\n\r\n const handleConfirmRemove = async () => {\r\n if (!memberToRemove) return;\r\n\r\n // Immediate update\r\n const previousMembers = members;\r\n setMembers((prev) => prev.filter((m) => m.id !== memberToRemove.id));\r\n setDeleteModalOpen(false);\r\n setMemberToRemove(null);\r\n\r\n toast.loading(\r\n `Removing ${memberToRemove.full_name || memberToRemove.email}...`,\r\n );\r\n\r\n try {\r\n const result = await removeMemberFromTeam(teamId, memberToRemove.id);\r\n toast.dismiss();\r\n\r\n if (result.success) {\r\n toast.success(\"Member removed successfully!\");\r\n } else {\r\n // Rollback on error\r\n setMembers(previousMembers);\r\n toast.error(result.error || \"Failed to remove member\");\r\n }\r\n } catch (error) {\r\n // Rollback on error\r\n setMembers(previousMembers);\r\n toast.dismiss();\r\n toast.error(\"Failed to remove member\");\r\n }\r\n };\r\n\r\n const columns: ColumnDef[] = [\r\n {\r\n key: \"full_name\",\r\n header: \"Name\",\r\n className: \"w-[250px]\",\r\n render: (member) => (\r\n
\r\n \r\n {member.full_name || \"—\"}\r\n \r\n
\r\n ),\r\n },\r\n {\r\n key: \"email\",\r\n header: \"Email\",\r\n className: \"\",\r\n render: (member) =>
{member.email}
,\r\n },\r\n {\r\n key: \"added_at\",\r\n header: \"Added\",\r\n className: \"text-center\",\r\n render: (member) => (\r\n
\r\n {member.added_at\r\n ? new Date(member.added_at).toLocaleDateString()\r\n : \"—\"}\r\n
\r\n ),\r\n },\r\n ];\r\n\r\n return (\r\n <>\r\n {isLoading ? (\r\n \r\n ) : (\r\n
\r\n \r\n data={members}\r\n columns={columns}\r\n searchKey=\"full_name\"\r\n searchPlaceholder=\"Search members...\"\r\n emptyMessage=\"No members in this team yet.\"\r\n headerActions={\r\n <>\r\n setAddModalOpen(true)}\r\n className=\"px-3 md:px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium flex items-center gap-2\"\r\n >\r\n \r\n Add Member\r\n \r\n setImportModalOpen(true)}\r\n className=\"px-3 md:px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors font-medium flex items-center gap-2\"\r\n >\r\n \r\n Import\r\n \r\n \r\n }\r\n renderActions={(member) => (\r\n handleEditClick(member)}\r\n onRemove={() => handleRemoveClick(member)}\r\n />\r\n )}\r\n SearchComponent={SearchInput}\r\n PaginationComponent={Pagination}\r\n />\r\n
\r\n )}\r\n\r\n \r\n\r\n \r\n\r\n \r\n\r\n \r\n \r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\MemberTableSkeleton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\TeamDashboardClient.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\TeamHeader.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\fonts.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\hero\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\auth\\callback\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\email\\announcement.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\AlertDialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\DataTable.tsx","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":43,"column":52,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":43,"endColumn":55,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[969,972],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[969,972],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'searchPlaceholder' is assigned a value but never used.","line":47,"column":3,"nodeType":null,"messageId":"unusedVar","endLine":47,"endColumn":20}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from \"@/components/ui/Table\";\r\n\r\nexport interface ColumnDef {\r\n key: string;\r\n header: string;\r\n className?: string;\r\n render?: (item: T) => React.ReactNode;\r\n}\r\n\r\nexport interface DataTableProps {\r\n data: T[];\r\n columns: ColumnDef[];\r\n searchKey: keyof T;\r\n searchPlaceholder?: string;\r\n emptyMessage?: string;\r\n onRowClick?: (item: T) => void;\r\n renderActions?: (item: T) => React.ReactNode;\r\n itemsPerPage?: number;\r\n headerActions?: React.ReactNode;\r\n SearchComponent?: React.ComponentType<{\r\n value: string;\r\n onChange: (value: string) => void;\r\n }>;\r\n PaginationComponent?: React.ComponentType<{\r\n totalCount: number;\r\n canPrevious: boolean;\r\n canNext: boolean;\r\n onPrevious: () => void;\r\n onNext: () => void;\r\n }>;\r\n}\r\n\r\nexport function DataTable>({\r\n data,\r\n columns,\r\n searchKey,\r\n searchPlaceholder = \"Search...\",\r\n emptyMessage = \"No results found.\",\r\n onRowClick,\r\n renderActions,\r\n itemsPerPage = 5,\r\n headerActions,\r\n SearchComponent,\r\n PaginationComponent,\r\n}: DataTableProps) {\r\n const [searchValue, setSearchValue] = React.useState(\"\");\r\n const [currentPage, setCurrentPage] = React.useState(1);\r\n\r\n // Filter data based on search\r\n const filteredData = React.useMemo(() => {\r\n if (!searchValue) return data;\r\n return data.filter((item) => {\r\n const value = item[searchKey];\r\n if (typeof value === \"string\") {\r\n return value.toLowerCase().includes(searchValue.toLowerCase());\r\n }\r\n return false;\r\n });\r\n }, [data, searchValue, searchKey]);\r\n\r\n // Paginate filtered data\r\n const totalPages = Math.ceil(filteredData.length / itemsPerPage);\r\n const paginatedData = React.useMemo(() => {\r\n const start = (currentPage - 1) * itemsPerPage;\r\n return filteredData.slice(start, start + itemsPerPage);\r\n }, [filteredData, currentPage, itemsPerPage]);\r\n\r\n // Reset page when search changes\r\n React.useEffect(() => {\r\n setCurrentPage(1);\r\n }, [searchValue]);\r\n\r\n return (\r\n
\r\n
\r\n {SearchComponent && (\r\n
\r\n \r\n {headerActions && (\r\n
{headerActions}
\r\n )}\r\n
\r\n )}\r\n\r\n
\r\n \r\n \r\n \r\n {columns.map((column) => (\r\n \r\n \r\n {column.header}\r\n \r\n \r\n ))}\r\n {renderActions && (\r\n \r\n \r\n Actions\r\n \r\n \r\n )}\r\n \r\n \r\n \r\n {paginatedData.length ? (\r\n paginatedData.map((item, index) => (\r\n onRowClick(item) : undefined}\r\n className={onRowClick ? \"cursor-pointer\" : \"\"}\r\n >\r\n {columns.map((column) => (\r\n \r\n {column.render\r\n ? column.render(item)\r\n : String(item[column.key] || \"\")}\r\n \r\n ))}\r\n {renderActions && (\r\n \r\n {renderActions(item)}\r\n \r\n )}\r\n \r\n ))\r\n ) : (\r\n \r\n \r\n {emptyMessage}\r\n \r\n \r\n )}\r\n \r\n
\r\n
\r\n
\r\n\r\n {PaginationComponent && (\r\n 1}\r\n canNext={currentPage < totalPages}\r\n onPrevious={() => setCurrentPage((p) => Math.max(1, p - 1))}\r\n onNext={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}\r\n />\r\n )}\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\DropdownMenu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Input.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\RichTextEditor.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Skeleton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Table.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Toast.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\eslint.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\calendar-utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\google-calendar.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\resend.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\validations.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lucide-react.d.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\next.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\postcss.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\postcss.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\prisma.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\proxy.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\public\\fonts.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Archivo' is defined but never used.","line":1,"column":17,"nodeType":null,"messageId":"unusedVar","endLine":1,"endColumn":24}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Inter, Archivo, Instrument_Serif } from \"next/font/google\";\r\n\r\nexport const inter = Inter({\r\n subsets: [\"latin\"],\r\n variable: \"--font-inter\",\r\n display: \"swap\",\r\n});\r\n\r\nexport const instrumentSerif = Instrument_Serif({\r\n subsets: [\"latin\"],\r\n weight: \"400\",\r\n variable: \"--font-display\",\r\n display: \"swap\",\r\n});\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\supabase\\functions\\store-google-refresh\\index.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'serve' is defined but never used.","line":1,"column":10,"nodeType":null,"messageId":"unusedVar","endLine":1,"endColumn":15},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":58,"column":27,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":58,"endColumn":30,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[2169,2172],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[2169,2172],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { serve } from \"https://deno.land/std@0.170.0/http/server.ts\";\r\n\r\n// Using Deno.serve per guidelines\r\nDeno.serve(async (req: Request) => {\r\n try {\r\n // Only allow POST\r\n if (req.method !== \"POST\") {\r\n return new Response(\r\n JSON.stringify({ success: false, message: \"Method not allowed\" }),\r\n { status: 405, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n const body = await req.json().catch(() => null);\r\n if (!body) {\r\n return new Response(\r\n JSON.stringify({ success: false, message: \"Invalid JSON body\" }),\r\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n const { userId, googleRefreshToken, email, fullName, avatarUrl } = body;\r\n if (!userId) {\r\n return new Response(\r\n JSON.stringify({ success: false, message: \"userId is required\" }),\r\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n // Build SQL parameters safely\r\n // Use Supabase DB URL to call Postgres directly\r\n const dbUrl = Deno.env.get(\"SUPABASE_DB_URL\");\r\n if (!dbUrl) {\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Database URL not configured\",\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n // Use fetch to call Postgres via connection string -- Use pg client would require npm; instead use RESTful SQL via Supabase REST? Not available. Instead use Postgres via Deno Postgres driver is not allowed. But Supabase provides REST admin via /rest/v1? We'll use the Supabase Admin REST endpoint using SUPABASE_URL and SERVICE_ROLE_KEY\r\n\r\n const supabaseUrl = Deno.env.get(\"SUPABASE_URL\");\r\n const serviceKey = Deno.env.get(\"SUPABASE_SERVICE_ROLE_KEY\");\r\n if (!supabaseUrl || !serviceKey) {\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Supabase env vars missing\",\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n // Upsert the profile\r\n const profilePayload: any = { id: userId };\r\n if (email !== undefined) profilePayload.email = email;\r\n if (fullName !== undefined) profilePayload.full_name = fullName;\r\n if (avatarUrl !== undefined) profilePayload.avatar_url = avatarUrl;\r\n if (googleRefreshToken !== undefined)\r\n profilePayload.google_refresh_token = googleRefreshToken;\r\n\r\n // Build PATCH (upsert) request to profiles table via Supabase REST\r\n // Use POST with Prefer: resolution=merge-duplicates for upsert on conflict\r\n\r\n const res = await fetch(`${supabaseUrl}/rest/v1/profiles?on_conflict=id`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n apikey: serviceKey,\r\n Authorization: `Bearer ${serviceKey}`,\r\n Prefer: \"resolution=merge-duplicates\",\r\n },\r\n body: JSON.stringify(profilePayload),\r\n });\r\n\r\n if (!res.ok) {\r\n const text = await res.text();\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Database request failed\",\r\n detail: text,\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n return new Response(\r\n JSON.stringify({ success: true, message: \"Token stored successfully\" }),\r\n { status: 200, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n } catch (err) {\r\n console.error(err);\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Internal server error\",\r\n error: String(err),\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n});\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\tailwind.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\auth-helpers.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\cn-helper.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\prisma\\prisma.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\supabase\\client.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\supabase\\proxy.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":56,"column":12,"nodeType":null,"messageId":"unusedVar","endLine":56,"endColumn":17}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { createServerClient } from \"@supabase/ssr\";\r\nimport { NextResponse, type NextRequest } from \"next/server\";\r\n\r\nexport async function updateSession(request: NextRequest) {\r\n let supabaseResponse = NextResponse.next({\r\n request,\r\n });\r\n\r\n // With Fluid compute, don't put this client in a global environment\r\n // variable. Always create a new one on each request.\r\n const supabase = createServerClient(\r\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\r\n process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,\r\n {\r\n cookies: {\r\n getAll() {\r\n return request.cookies.getAll();\r\n },\r\n setAll(cookiesToSet) {\r\n cookiesToSet.forEach(({ name, value }) =>\r\n request.cookies.set(name, value)\r\n );\r\n supabaseResponse = NextResponse.next({\r\n request,\r\n });\r\n cookiesToSet.forEach(({ name, value, options }) =>\r\n supabaseResponse.cookies.set(name, value, options)\r\n );\r\n },\r\n },\r\n }\r\n );\r\n\r\n // Do not run code between createServerClient and\r\n // supabase.auth.getClaims(). A simple mistake could make it very hard to debug\r\n // issues with users being randomly logged out.\r\n\r\n // IMPORTANT: If you remove getClaims() and you use server-side rendering\r\n // with the Supabase client, your users may be randomly logged out.\r\n\r\n // Routes that don't require authentication\r\n const PUBLIC_ROUTES = [\"/hero\", \"/about\", \"/login\", \"/auth/callback\", \"/404\"];\r\n const isPublicRoute = PUBLIC_ROUTES.some((route) =>\r\n request.nextUrl.pathname.startsWith(route)\r\n );\r\n\r\n // Skip auth check for public routes to prevent redirect loops\r\n if (isPublicRoute) {\r\n return supabaseResponse;\r\n }\r\n\r\n let user;\r\n try {\r\n const { data } = await supabase.auth.getClaims();\r\n user = data?.claims;\r\n } catch (error) {\r\n //redirect login\r\n const url = request.nextUrl.clone();\r\n url.pathname = \"/login\";\r\n return NextResponse.redirect(url);\r\n }\r\n\r\n if (!user) {\r\n // no user, potentially respond by redirecting the user to the login page\r\n const url = request.nextUrl.clone();\r\n url.pathname = \"/login\";\r\n return NextResponse.redirect(url);\r\n }\r\n\r\n // IMPORTANT: You *must* return the supabaseResponse object as it is. If you're\r\n // creating a new response object with NextResponse.next() make sure to:\r\n // 1. Pass the request in it, like so:\r\n // const myNewResponse = NextResponse.next({ request })\r\n // 2. Copy over the cookies, like so:\r\n // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())\r\n // 3. Change the myNewResponse object to fit your needs, but avoid changing\r\n // the cookies!\r\n // 4. Finally:\r\n // return myNewResponse\r\n // If this is not done, you may be causing the browser and server to go out\r\n // of sync and terminate the user's session prematurely!\r\n supabaseResponse.headers.set(\"X-Content-Type-Options\", \"nosniff\");\r\n supabaseResponse.headers.set(\"X-Frame-Options\", \"DENY\");\r\n supabaseResponse.headers.set(\"X-XSS-Protection\", \"1; mode=block\");\r\n\r\n return supabaseResponse;\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\supabase\\server.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]}] \ No newline at end of file diff --git a/lint-results.json b/lint-results.json new file mode 100644 index 0000000..9e91ac1 --- /dev/null +++ b/lint-results.json @@ -0,0 +1 @@ +[{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(announcements)\\announcements.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(announcements)\\crud.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(announcements)\\resend.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'valid' is assigned a value but never used.","line":54,"column":11,"nodeType":null,"messageId":"unusedVar","endLine":54,"endColumn":16},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'batchId' is defined but never used.","line":109,"column":38,"nodeType":null,"messageId":"unusedVar","endLine":109,"endColumn":45}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { resend } from \"@/lib/resend\";\r\nimport { render } from \"@react-email/render\";\r\nimport { AnnouncementEmail } from \"@/components/email/announcement\";\r\nimport React from \"react\";\r\n\r\ninterface SendEmailParams {\r\n recipients: Array<{ email: string; name?: string }>;\r\n title: string;\r\n content: string;\r\n teamName: string;\r\n}\r\n\r\ninterface BatchSendResult {\r\n batchIds: string[];\r\n totalRecipients: number;\r\n}\r\n\r\nconst BATCH_LIMIT = 100;\r\n\r\n/**\r\n * Validates email addresses\r\n */\r\nexport function validateEmails(emails: string[]): {\r\n valid: string[];\r\n invalid: string[];\r\n} {\r\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\r\n const valid: string[] = [];\r\n const invalid: string[] = [];\r\n\r\n for (const email of emails) {\r\n if (emailRegex.test(email)) {\r\n valid.push(email);\r\n } else {\r\n invalid.push(email);\r\n }\r\n }\r\n\r\n return { valid, invalid };\r\n}\r\n\r\n/**\r\n * Sends announcement email to multiple recipients using Resend batch API\r\n * Handles batch splitting for >100 recipients\r\n */\r\nexport async function sendAnnouncementEmail({\r\n recipients,\r\n title,\r\n content,\r\n teamName,\r\n}: SendEmailParams): Promise {\r\n // Validate all emails first\r\n const emails = recipients.map((r) => r.email);\r\n const { valid, invalid } = validateEmails(emails);\r\n\r\n if (invalid.length > 0) {\r\n throw new Error(`Invalid email addresses: ${invalid.join(\", \")}`);\r\n }\r\n\r\n const batchIds: string[] = [];\r\n\r\n // Split into batches if needed\r\n for (let i = 0; i < recipients.length; i += BATCH_LIMIT) {\r\n const batch = recipients.slice(i, i + BATCH_LIMIT);\r\n\r\n // Render all emails in the batch (render is async in v2.x)\r\n const renderedHtmls = await Promise.all(\r\n batch.map((recipient) =>\r\n render(\r\n React.createElement(AnnouncementEmail, {\r\n title,\r\n content,\r\n teamName,\r\n memberName: recipient.name,\r\n }),\r\n ),\r\n ),\r\n );\r\n\r\n // Create email objects with rendered HTML\r\n const emails = batch.map((recipient, index) => ({\r\n from: \"Taskboard \", // TODO: Replace with your verified domain\r\n to: recipient.email,\r\n subject: `${teamName}: ${title}`,\r\n html: renderedHtmls[index],\r\n }));\r\n\r\n // Send batch\r\n const { data, error } = await resend.batch.send(emails);\r\n\r\n if (error) {\r\n throw new Error(`Resend API error: ${error.message}`);\r\n }\r\n\r\n if (data?.id) {\r\n batchIds.push(data.id);\r\n }\r\n }\r\n\r\n return {\r\n batchIds,\r\n totalRecipients: recipients.length,\r\n };\r\n}\r\n\r\n/**\r\n * Query Resend API for batch status (for reconciliation)\r\n */\r\nexport async function getBatchStatus(batchId: string) {\r\n try {\r\n // Note: Resend doesn't have a direct batch status endpoint yet\r\n // This is a placeholder for future implementation\r\n // For now, rely on webhooks for status updates\r\n return { status: \"unknown\" };\r\n } catch (error) {\r\n console.error(\"Error fetching batch status:\", error);\r\n return { status: \"error\" };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(calendar)\\calendar.ts","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":38,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":38,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1002,1005],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1002,1005],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":75,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":75,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[2060,2063],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[2060,2063],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":106,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":106,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[2927,2930],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[2927,2930],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":3,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use server\";\r\n\r\nimport { getUser } from \"../auth\";\r\nimport prisma from \"@/utils/prisma/prisma\";\r\nimport { getCalendarClient } from \"../(events)/google\";\r\n\r\n// Get calendar id\r\nexport async function getTeamCalendarId(\r\n teamId: string,\r\n leader_id: string,\r\n): Promise {\r\n const team = await prisma.teams.findFirst({\r\n where: {\r\n id: teamId,\r\n leader_id: leader_id,\r\n },\r\n select: {\r\n google_calendar_id: true,\r\n },\r\n });\r\n return team?.google_calendar_id ?? null;\r\n}\r\n\r\n// Creates a new Google Calendar for a team.\r\nexport async function createTeamCalendar(teamName: string) {\r\n try {\r\n const { client } = await getCalendarClient();\r\n\r\n const response = await client.calendars.insert({\r\n requestBody: {\r\n summary: `${teamName} Calendar`,\r\n description: `Shared calendar for ${teamName} team`,\r\n timeZone: \"Asia/Manila\",\r\n },\r\n });\r\n\r\n return { success: true, calendarId: response.data.id };\r\n } catch (error: any) {\r\n console.error(`Error creating calendar: ${teamName}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || `Failed to create calendar: ${teamName}`,\r\n };\r\n }\r\n}\r\n// update calendar\r\nexport async function updateTeamCalendar(teamId: string, teamName: string) {\r\n const user = await getUser();\r\n const userId = user.data.user?.id;\r\n if (!userId) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n // retrieve calendar id\r\n const calendarId = await getTeamCalendarId(teamId, userId);\r\n\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Calendar not found\",\r\n };\r\n }\r\n\r\n try {\r\n const { client } = await getCalendarClient();\r\n const response = await client.calendars.update({\r\n calendarId: calendarId,\r\n requestBody: {\r\n summary: `${teamName} Calendar`,\r\n description: `Shared calendar for ${teamName} team`,\r\n timeZone: \"Asia/Manila\",\r\n },\r\n });\r\n\r\n return { success: true, calendarId: response.data.id };\r\n } catch (error: any) {\r\n console.error(`Error updating calendar: ${calendarId}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || `Failed to update calendar: ${calendarId}`,\r\n };\r\n }\r\n}\r\n// delete calendar\r\nexport async function deleteTeamCalendar(teamId: string) {\r\n const user = await getUser();\r\n const userId = user.data.user?.id;\r\n if (!userId) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n // retrieve calendar id\r\n const calendarId = await getTeamCalendarId(teamId, userId);\r\n\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Calendar not found\",\r\n };\r\n }\r\n try {\r\n const { client } = await getCalendarClient();\r\n const response = await client.calendars.delete({\r\n calendarId: calendarId,\r\n });\r\n\r\n return { success: true, response: response.data };\r\n } catch (error: any) {\r\n console.error(`Error deleting calendar: ${calendarId}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || `Failed to delete calendar: ${calendarId}`,\r\n };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(calendar)\\calendar_db.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(events)\\event.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'CalendarEventSchema' is defined but never used.","line":3,"column":10,"nodeType":null,"messageId":"unusedVar","endLine":3,"endColumn":29},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":37,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":37,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[984,987],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[984,987],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":102,"column":23,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":102,"endColumn":26,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[2794,2797],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[2794,2797],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":126,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":126,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[3561,3564],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[3561,3564],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":145,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":145,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[4076,4079],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[4076,4079],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":171,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":171,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[4733,4736],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[4733,4736],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":220,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":220,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[5980,5983],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[5980,5983],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":283,"column":23,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":283,"endColumn":26,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[7804,7807],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[7804,7807],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":300,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":300,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[8328,8331],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[8328,8331],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":318,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":318,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[8837,8840],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[8837,8840],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":342,"column":21,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":342,"endColumn":24,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[9475,9478],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[9475,9478],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":368,"column":23,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":368,"endColumn":26,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[10108,10111],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[10108,10111],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":383,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":383,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[10578,10581],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[10578,10581],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":12,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use server\";\r\nimport { getCalendarClient } from \"./google\";\r\nimport { CalendarEventSchema } from \"@/lib/validations\";\r\nimport {\r\n createCalendarEvent_DB,\r\n updateCalendarEvent_DB,\r\n deleteCalendarEvent_DB,\r\n} from \"./event_db\";\r\nimport { getTeamCalendarId } from \"../(calendar)/calendar\";\r\nimport { getUser } from \"../auth\";\r\nimport prisma from \"@/utils/prisma/prisma\";\r\nimport { getMembersForTeam } from \"../members\";\r\n\r\n// TODO: Can be set by user\r\nconst DEFAULT_TIMEZONE = \"Asia/Manila\";\r\n\r\n// Creates an event in a team's calendar.\r\nexport async function createCalendarEvent(\r\n teamId: string,\r\n eventDetails: {\r\n title: string;\r\n start: Date;\r\n end: Date;\r\n description?: string;\r\n },\r\n) {\r\n let leader_id: string;\r\n\r\n // validate and get user id\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: any) {\r\n console.error(`Error creating calendar: ${teamId}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || `Failed to create calendar: ${teamId}`,\r\n };\r\n }\r\n\r\n // get calendar id\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n try {\r\n const { client } = await getCalendarClient();\r\n\r\n // Note: Validation is handled by the caller (Zod schema at form level)\r\n\r\n // Fetch team members to add as attendees\r\n const membersResult = await getMembersForTeam(teamId);\r\n const attendees =\r\n membersResult.success && membersResult.members\r\n ? membersResult.members.map((m) => ({ email: m.email }))\r\n : [];\r\n\r\n // Create Event\r\n const response = await client.events.insert({\r\n calendarId: calendarId,\r\n sendUpdates: \"none\", // Don't send email notifications\r\n requestBody: {\r\n summary: eventDetails.title,\r\n description: eventDetails.description,\r\n start: {\r\n dateTime: eventDetails.start.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n end: {\r\n dateTime: eventDetails.end.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n attendees: attendees,\r\n },\r\n });\r\n\r\n const calendarEventId = response.data.id;\r\n\r\n // Check if event was created before db update\r\n if (!calendarEventId) {\r\n return {\r\n success: false,\r\n error: \"Failed to create event\",\r\n };\r\n }\r\n\r\n // save to database\r\n try {\r\n await createCalendarEvent_DB(\r\n calendarId,\r\n teamId,\r\n eventDetails,\r\n calendarEventId,\r\n );\r\n } catch (dbError: any) {\r\n console.error(\"DB Error. Rolling back Google Event...\", dbError.message);\r\n\r\n // Delete event from google calendar\r\n try {\r\n await client.events.delete({\r\n calendarId: calendarId,\r\n eventId: calendarEventId,\r\n });\r\n console.log(\"✅ Rollback successful: Google Event deleted.\");\r\n } catch (rollbackError) {\r\n // Rollback error\r\n console.error(\"System data is inconsistent.\", rollbackError);\r\n // TODO: Send alert to admin / deadlock queue\r\n }\r\n\r\n throw new Error(`System Error: Failed to save event. ${dbError.message}`);\r\n }\r\n\r\n return {\r\n success: true,\r\n eventId: response.data.id,\r\n eventLink: response.data.htmlLink,\r\n };\r\n } catch (error: any) {\r\n console.error(\"Error creating event:\", error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Failed to create event\",\r\n };\r\n }\r\n}\r\n// Get team events (google)\r\nexport async function getTeamEvents(teamId: string) {\r\n // Get user\r\n let leader_id: string;\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: any) {\r\n console.error(`Error in getTeamEvents: ${teamId}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Authentication failed\",\r\n };\r\n }\r\n\r\n // Resolve calendarId from teamId\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n\r\n try {\r\n const { client } = await getCalendarClient();\r\n const response = await client.events.list({\r\n calendarId: calendarId,\r\n });\r\n return {\r\n success: true,\r\n events: response.data.items,\r\n };\r\n } catch (error: any) {\r\n console.error(\"Error getting events:\", error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Failed to get events\",\r\n };\r\n }\r\n}\r\nexport async function updateCalendarEvent(\r\n teamId: string,\r\n eventData: {\r\n id: string;\r\n title?: string;\r\n start?: Date;\r\n end?: Date;\r\n googleEventId?: string | null;\r\n desc?: string;\r\n },\r\n) {\r\n // Extract and validate required fields\r\n const { id: eventId, title, start, end, googleEventId, desc } = eventData;\r\n\r\n // Map desc to description for DB layer\r\n const description = desc;\r\n\r\n if (!eventId) {\r\n return {\r\n success: false,\r\n error: \"Missing required field: eventId is required for update\",\r\n };\r\n }\r\n if (!title || !start || !end) {\r\n return {\r\n success: false,\r\n error:\r\n \"Missing required fields: title, start, and end are required for update\",\r\n };\r\n }\r\n\r\n const eventDetails = { title, start, end, description };\r\n let leader_id: string;\r\n // validate and get user id\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: any) {\r\n console.error(`Error creating calendar: ${teamId}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || `Failed to create calendar: ${teamId}`,\r\n };\r\n }\r\n\r\n // get calendar id\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n\r\n // Validate googleEventId for Google API call\r\n if (!googleEventId) {\r\n return {\r\n success: false,\r\n error: \"Missing googleEventId: cannot update event in Google Calendar\",\r\n };\r\n }\r\n\r\n try {\r\n const { client } = await getCalendarClient();\r\n\r\n // Fetch team members to add as attendees\r\n const membersResult = await getMembersForTeam(teamId);\r\n const attendees =\r\n membersResult.success && membersResult.members\r\n ? membersResult.members.map((m) => ({ email: m.email }))\r\n : [];\r\n\r\n const response = await client.events.update({\r\n calendarId: calendarId,\r\n eventId: googleEventId,\r\n sendUpdates: \"none\", // Don't send email notifications\r\n requestBody: {\r\n summary: eventDetails.title,\r\n description: eventDetails.description,\r\n start: {\r\n dateTime: eventDetails.start.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n end: {\r\n dateTime: eventDetails.end.toISOString(),\r\n timeZone: DEFAULT_TIMEZONE,\r\n },\r\n attendees: attendees,\r\n },\r\n });\r\n // Check if event was updated before db update\r\n if (!response.data.id) {\r\n return {\r\n success: false,\r\n error: \"Failed to update event\",\r\n };\r\n }\r\n // Save changes to DB\r\n try {\r\n await updateCalendarEvent_DB(eventId, eventDetails);\r\n } catch (dbError: any) {\r\n console.error(\r\n \"❌ DB Consistency Failure during Update. Google Event updated, but DB failed.\",\r\n dbError.message,\r\n );\r\n // NOTE: Rolling back an update is complex (needs old data).\r\n // For now, we log the inconsistency.\r\n throw new Error(\r\n `System Error: Failed to update event in DB. ${dbError.message}`,\r\n );\r\n }\r\n\r\n return {\r\n success: true,\r\n eventId: response.data.id,\r\n eventLink: response.data.htmlLink,\r\n };\r\n } catch (error: any) {\r\n console.error(\"Error updating event:\", error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Failed to update event\",\r\n };\r\n }\r\n}\r\nexport async function deleteCalendarEvent(teamId: string, eventId: string) {\r\n // Get user\r\n let leader_id: string;\r\n try {\r\n const user = await getUser();\r\n const idHelper = user.data.user?.id;\r\n if (!idHelper) {\r\n throw new Error(\"User not authenticated\");\r\n }\r\n leader_id = idHelper;\r\n } catch (error: any) {\r\n console.error(`Error in deleteCalendarEvent: ${teamId}`, error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Authentication failed\",\r\n };\r\n }\r\n\r\n // Resolve calendarId from teamId\r\n const calendarId = await getTeamCalendarId(teamId, leader_id);\r\n if (!calendarId) {\r\n return {\r\n success: false,\r\n error: \"Failed to get calendar id\",\r\n };\r\n }\r\n\r\n // Fetch event from DB to get googleEventId\r\n let dbEvent;\r\n try {\r\n dbEvent = await prisma.events.findUnique({\r\n where: { id: eventId },\r\n select: { google_event_id: true },\r\n });\r\n } catch (dbError: any) {\r\n console.error(\r\n \"Database query failed in deleteCalendarEvent:\",\r\n dbError.message,\r\n );\r\n return {\r\n success: false,\r\n error: `Database error: ${dbError.message}`,\r\n };\r\n }\r\n\r\n if (!dbEvent || !dbEvent.google_event_id) {\r\n return {\r\n success: false,\r\n error: \"Event not found or missing Google Event ID\",\r\n };\r\n }\r\n\r\n try {\r\n const { client } = await getCalendarClient();\r\n await client.events.delete({\r\n calendarId: calendarId,\r\n eventId: dbEvent.google_event_id,\r\n });\r\n try {\r\n await deleteCalendarEvent_DB(eventId);\r\n } catch (dbError: any) {\r\n console.error(\r\n \"❌ DB Consistency Failure during Delete. Google Event deleted, but DB failed (Zombie Data).\",\r\n dbError.message,\r\n );\r\n // NOTE: Rolling back a delete means RE-CREATING the event (new ID).\r\n // For now, we log the inconsistency.\r\n throw new Error(\r\n `System Error: Failed to delete event in DB. ${dbError.message}`,\r\n );\r\n }\r\n\r\n return {\r\n success: true,\r\n };\r\n } catch (error: any) {\r\n console.error(\"Error deleting event:\", error?.message);\r\n return {\r\n success: false,\r\n error: error?.message || \"Failed to delete event\",\r\n };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(events)\\event_db.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\(events)\\google.ts","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":47,"column":19,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":47,"endColumn":22,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1487,1490],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1487,1490],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use server\";\r\nimport { google } from \"googleapis\";\r\nimport { getUser } from \"../auth\";\r\nimport prisma from \"@/utils/prisma/prisma\";\r\nimport { getOAuth2Client } from \"@/lib/google-calendar\";\r\n\r\n// Nullify to break auth loops\r\nasync function nullifyToken(userId: string) {\r\n console.warn(`Nullifying invalid token for user ${userId}`);\r\n await prisma.profiles.update({\r\n where: { id: userId },\r\n data: { google_refresh_token: null },\r\n });\r\n}\r\n\r\n// Gets calendar client, if invalid token (400) nullify and re-auth\r\nexport async function getCalendarClient() {\r\n const user = await getUser();\r\n const userId = user.data.user?.id;\r\n\r\n if (!userId) throw new Error(\"User not authenticated\");\r\n\r\n const profile = await prisma.profiles.findUnique({\r\n where: { id: userId },\r\n select: { google_refresh_token: true },\r\n });\r\n\r\n if (!profile?.google_refresh_token) {\r\n throw new Error(\"No Google refresh token found. Please reconnect.\");\r\n }\r\n\r\n const oauth2Client = getOAuth2Client();\r\n oauth2Client.setCredentials({ refresh_token: profile.google_refresh_token });\r\n\r\n return {\r\n client: google.calendar({ version: \"v3\", auth: oauth2Client }),\r\n userId,\r\n };\r\n}\r\n\r\n// Checks permission, if error, nullify and re-auth\r\nexport async function checkCalendarPermissions() {\r\n try {\r\n const { client } = await getCalendarClient();\r\n await client.calendarList.list({ maxResults: 1 });\r\n return { hasValidToken: true, needsReauth: false };\r\n } catch (error: any) {\r\n console.error(\"Permission check failed:\", error?.message);\r\n\r\n // If any auth error, we need re-auth\r\n const isAuthError =\r\n [400, 401, 403].includes(error?.code) ||\r\n error?.response?.data?.error === \"invalid_request\";\r\n\r\n if (isAuthError) {\r\n const user = await getUser();\r\n const userId = user.data.user?.id;\r\n if (userId) await nullifyToken(userId);\r\n return { hasValidToken: false, needsReauth: true };\r\n }\r\n\r\n return { hasValidToken: false, needsReauth: true };\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\auth.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\members.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\actions\\teams.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\actions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\AboutBtn.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\ErrorMsg.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\SignInButton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\components\\Star.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\(auth)\\login\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\404\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\about\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\api\\webhooks\\resend\\route.ts","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":42,"column":18,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":42,"endColumn":21,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[1337,1340],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[1337,1340],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":105,"column":14,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":105,"endColumn":17,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[3275,3278],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[3275,3278],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { NextRequest, NextResponse } from \"next/server\";\r\nimport prisma from \"@/utils/prisma/prisma\";\r\nimport { EmailStatus } from \"@prisma/client\";\r\nimport { Webhook } from \"svix\";\r\n\r\n/**\r\n * Resend webhook handler\r\n * Receives delivery status updates from Resend\r\n *\r\n * Events: email.sent, email.delivered, email.bounced, email.complained\r\n */\r\nexport async function POST(request: NextRequest) {\r\n try {\r\n const webhookSecret = process.env.RESEND_WEBHOOK_SECRET;\r\n\r\n if (!webhookSecret) {\r\n console.error(\"RESEND_WEBHOOK_SECRET not configured\");\r\n return NextResponse.json(\r\n { error: \"Webhook not configured\" },\r\n { status: 500 },\r\n );\r\n }\r\n\r\n // Get webhook headers for signature verification\r\n const svixId = request.headers.get(\"svix-id\");\r\n const svixTimestamp = request.headers.get(\"svix-timestamp\");\r\n const svixSignature = request.headers.get(\"svix-signature\");\r\n\r\n if (!svixId || !svixTimestamp || !svixSignature) {\r\n console.warn(\"Missing svix headers\");\r\n return NextResponse.json(\r\n { error: \"Missing webhook headers\" },\r\n { status: 401 },\r\n );\r\n }\r\n\r\n // Get raw body for signature verification\r\n const body = await request.text();\r\n\r\n // Verify webhook signature\r\n const wh = new Webhook(webhookSecret);\r\n let payload: any;\r\n\r\n try {\r\n payload = wh.verify(body, {\r\n \"svix-id\": svixId,\r\n \"svix-timestamp\": svixTimestamp,\r\n \"svix-signature\": svixSignature,\r\n });\r\n } catch (err) {\r\n console.error(\"Webhook signature verification failed:\", err);\r\n return NextResponse.json({ error: \"Invalid signature\" }, { status: 401 });\r\n }\r\n\r\n // Parse verified payload\r\n const { type, data } = payload;\r\n\r\n console.log(\"Received webhook:\", type, data);\r\n\r\n // Extract batch ID from the event\r\n // Note: Resend's webhook structure may vary, adjust as needed\r\n const batchId = data?.batch_id || data?.id;\r\n\r\n if (!batchId) {\r\n console.warn(\"No batch ID in webhook payload\");\r\n return NextResponse.json({ received: true }, { status: 200 });\r\n }\r\n\r\n // Find announcement by batch ID\r\n // Handle both single ID and JSON array of IDs\r\n const announcements = await prisma.announcements.findMany({\r\n where: {\r\n OR: [\r\n { resend_batch_id: batchId },\r\n { resend_batch_id: { contains: batchId } },\r\n ],\r\n },\r\n });\r\n\r\n if (announcements.length === 0) {\r\n console.warn(`No announcement found for batch ID: ${batchId}`);\r\n // Return 200 to acknowledge webhook (idempotent)\r\n return NextResponse.json({ received: true }, { status: 200 });\r\n }\r\n\r\n // Process each announcement\r\n for (const announcement of announcements) {\r\n await processWebhookEvent(announcement.id, type, data);\r\n }\r\n\r\n return NextResponse.json({ received: true }, { status: 200 });\r\n } catch (error) {\r\n console.error(\"Error processing webhook:\", error);\r\n // Always return 200 to prevent Resend from retrying\r\n return NextResponse.json({ received: true }, { status: 200 });\r\n }\r\n}\r\n\r\n/**\r\n * Process individual webhook event\r\n */\r\nasync function processWebhookEvent(\r\n announcementId: bigint,\r\n eventType: string,\r\n eventData: any,\r\n) {\r\n try {\r\n const announcement = await prisma.announcements.findUnique({\r\n where: { id: announcementId },\r\n });\r\n\r\n if (!announcement) return;\r\n\r\n switch (eventType) {\r\n case \"email.sent\":\r\n // Email accepted by Resend, no action needed\r\n break;\r\n\r\n case \"email.delivered\":\r\n // Increment delivered count\r\n await prisma.announcements.update({\r\n where: { id: announcementId },\r\n data: {\r\n delivered_count: {\r\n increment: 1,\r\n },\r\n },\r\n });\r\n\r\n // Check if all emails delivered\r\n const updated = await prisma.announcements.findUnique({\r\n where: { id: announcementId },\r\n });\r\n\r\n if (\r\n updated &&\r\n updated.delivered_count >= updated.recipient_count &&\r\n updated.email_status === EmailStatus.SENDING\r\n ) {\r\n await prisma.announcements.update({\r\n where: { id: announcementId },\r\n data: {\r\n email_status: EmailStatus.SENT,\r\n },\r\n });\r\n }\r\n break;\r\n\r\n case \"email.bounced\":\r\n case \"email.complained\":\r\n // Handle failures\r\n const failedEmail = eventData?.email || \"unknown\";\r\n const currentErrors = announcement.error_message || \"\";\r\n const failedEmails = currentErrors ? JSON.parse(currentErrors) : [];\r\n\r\n if (!failedEmails.includes(failedEmail)) {\r\n failedEmails.push(failedEmail);\r\n }\r\n\r\n await prisma.announcements.update({\r\n where: { id: announcementId },\r\n data: {\r\n error_message: JSON.stringify(failedEmails),\r\n email_status:\r\n failedEmails.length > 0\r\n ? EmailStatus.PARTIALLY_FAILED\r\n : announcement.email_status,\r\n },\r\n });\r\n break;\r\n\r\n default:\r\n console.log(`Unhandled event type: ${eventType}`);\r\n }\r\n } catch (error) {\r\n console.error(\"Error processing webhook event:\", error);\r\n }\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\auth\\auth-code-error\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\auth\\callback\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\CalendarPermissionsBanner.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\NewTeamBtn.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'inter' is defined but never used.","line":17,"column":10,"nodeType":null,"messageId":"unusedVar","endLine":17,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\nimport { useState } from \"react\";\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogFooter,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogTrigger,\r\n DialogOverlay,\r\n} from \"@/components/ui/Dialog\";\r\nimport { Input } from \"@/components/ui/Input\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport { Plus, Users } from \"lucide-react\";\r\nimport { createTeam } from \"@/actions/teams\";\r\nimport toast from \"react-hot-toast\";\r\nimport { inter, instrumentSerif } from \"@/app/fonts\";\r\n\r\nfunction NewTeamBtn() {\r\n const [teamName, setTeamName] = useState(\"\");\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [open, setOpen] = useState(false);\r\n\r\n const handleCreate = async () => {\r\n if (!teamName.trim()) return;\r\n setIsLoading(true);\r\n\r\n try {\r\n toast.loading(\"Creating team...\");\r\n await createTeam(teamName);\r\n toast.success(`Team \"${teamName}\" created successfully!`);\r\n setTeamName(\"\");\r\n setOpen(false);\r\n } catch (error) {\r\n toast.error(\r\n error instanceof Error ? error.message : \"Failed to create team\",\r\n );\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n Create New Team\r\n \r\n \r\n\r\n setTeamName(e.target.value)}\r\n className=\"border-gray-200 focus:border-blue-500 rounded-lg\"\r\n />\r\n \r\n setOpen(false)}\r\n className=\"flex-1 sm:flex-none bg-slate-100 text-black\"\r\n >\r\n Cancel\r\n \r\n \r\n \r\n {isLoading ? \"Creating...\" : \"Add Team\"}\r\n \r\n \r\n \r\n \r\n \r\n );\r\n}\r\n\r\nexport default NewTeamBtn;\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\SignOutBtn.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\WelcomeMsg.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\ActionsMenu.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'DropdownMenuLabel' is defined but never used.","line":7,"column":3,"nodeType":null,"messageId":"unusedVar","endLine":7,"endColumn":20}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { MoreHorizontal } from \"lucide-react\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuLabel,\r\n DropdownMenuSeparator,\r\n DropdownMenuTrigger,\r\n} from \"@/components/ui/DropdownMenu\";\r\nimport { Team } from \"./types\";\r\n\r\ninterface ActionsMenuProps {\r\n team: Team;\r\n onEdit: (id: string) => void;\r\n onDelete: (id: string) => void;\r\n}\r\n\r\nexport function ActionsMenu({ team, onEdit, onDelete }: ActionsMenuProps) {\r\n return (\r\n \r\n \r\n e.stopPropagation()}\r\n >\r\n Open menu\r\n \r\n \r\n \r\n \r\n {\r\n e.stopPropagation();\r\n onEdit(team.id);\r\n }}\r\n >\r\n Edit team\r\n \r\n \r\n {\r\n e.stopPropagation();\r\n onDelete(team.id);\r\n }}\r\n className=\"text-red-600\"\r\n >\r\n Delete team\r\n \r\n \r\n \r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\ConfirmationModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\EditTeamModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\Pagination.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\SearchInput.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\TeamTable.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\data.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\components\\team-table\\types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\layout.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'SignOutBtn' is defined but never used.","line":1,"column":8,"nodeType":null,"messageId":"unusedVar","endLine":1,"endColumn":18}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import SignOutBtn from \"./components/SignOutBtn\";\r\nexport default function DashboardLayout({\r\n children,\r\n}: Readonly<{\r\n children: React.ReactNode;\r\n}>) {\r\n return (\r\n
\r\n {/* */}\r\n {children}\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\loading.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\page.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'getUser' is defined but never used.","line":1,"column":10,"nodeType":null,"messageId":"unusedVar","endLine":1,"endColumn":17},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'full_name' is assigned a value but never used.","line":21,"column":11,"nodeType":null,"messageId":"unusedVar","endLine":21,"endColumn":20}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { getUser, requireAuth } from \"@/actions/auth\";\r\nimport { getTeams } from \"@/actions/teams\";\r\nimport WelcomeMsg from \"./components/WelcomeMsg\";\r\nimport { inter } from \"@/app/fonts\";\r\nimport { TeamTable } from \"./components/team-table\";\r\nimport NewTeamBtn from \"./components/NewTeamBtn\";\r\nimport CalendarPermissionsBanner from \"./components/CalendarPermissionsBanner\";\r\nimport type { Metadata } from \"next\";\r\n\r\nexport const metadata: Metadata = {\r\n title: \"Dashboard | Taskboard\",\r\n};\r\n\r\nexport default async function Page() {\r\n const user = await requireAuth();\r\n\r\n // Fetch teams data\r\n const teams = await getTeams();\r\n\r\n // Google auth info is in user.user_metadata\r\n const { full_name, email, avatar_url, name } = user.user_metadata;\r\n\r\n return (\r\n
\r\n
\r\n {/* Welcome Section */}\r\n
\r\n \r\n
\r\n\r\n {/* Calendar Permissions Banner */}\r\n \r\n\r\n {/* Team Selection Card */}\r\n
\r\n
\r\n
\r\n

\r\n Select a Team\r\n

\r\n

\r\n Choose a workspace to continue your progress.\r\n

\r\n
\r\n \r\n
\r\n {/* Team Table */}\r\n \r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\AnnouncementCard.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":50,"column":14,"nodeType":null,"messageId":"unusedVar","endLine":50,"endColumn":19},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":72,"column":14,"nodeType":null,"messageId":"unusedVar","endLine":72,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport { useState } from \"react\";\r\nimport { EmailStatus } from \"@prisma/client\";\r\nimport { sendAnnouncement } from \"@/actions/(announcements)/announcements\";\r\nimport { deleteAnnouncement } from \"@/actions/(announcements)/crud\";\r\nimport EmailStatusBadge from \"./EmailStatusBadge\";\r\nimport { Send, Trash2 } from \"lucide-react\";\r\nimport toast from \"react-hot-toast\";\r\nimport { format } from \"date-fns\";\r\nimport ConfirmationModal from \"@/app/dashboard/components/team-table/ConfirmationModal\";\r\n\r\ninterface AnnouncementCardProps {\r\n announcement: {\r\n id: bigint;\r\n title: string;\r\n content: string;\r\n created_at: Date;\r\n email_status: EmailStatus | null;\r\n sent_at: Date | null;\r\n recipient_count: number;\r\n delivered_count: number;\r\n error_message: string | null;\r\n };\r\n onUpdate: () => void;\r\n}\r\n\r\nexport default function AnnouncementCard({\r\n announcement,\r\n onUpdate,\r\n}: AnnouncementCardProps) {\r\n const [isSending, setIsSending] = useState(false);\r\n const [isDeleting, setIsDeleting] = useState(false);\r\n const [deleteModalOpen, setDeleteModalOpen] = useState(false);\r\n\r\n async function handleSend() {\r\n setIsSending(true);\r\n const toastId = toast.loading(\"Sending announcement emails...\");\r\n try {\r\n const result = await sendAnnouncement(announcement.id);\r\n\r\n if (result.success) {\r\n toast.success(\"Announcement sent successfully!\", { id: toastId });\r\n onUpdate();\r\n } else {\r\n toast.error(result.error || \"Failed to send announcement\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\"An error occurred while sending\", { id: toastId });\r\n } finally {\r\n setIsSending(false);\r\n }\r\n }\r\n\r\n async function confirmDelete() {\r\n setDeleteModalOpen(false);\r\n setIsDeleting(true);\r\n const toastId = toast.loading(\"Deleting announcement...\");\r\n try {\r\n const result = await deleteAnnouncement(announcement.id);\r\n\r\n if (result.success) {\r\n toast.success(\"Announcement deleted\", { id: toastId });\r\n onUpdate();\r\n } else {\r\n toast.error(result.error || \"Failed to delete announcement\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\"An error occurred while deleting\", { id: toastId });\r\n } finally {\r\n setIsDeleting(false);\r\n }\r\n }\r\n\r\n const canSend = announcement.email_status === EmailStatus.PENDING;\r\n const canDelete =\r\n announcement.email_status === EmailStatus.PENDING ||\r\n announcement.email_status === EmailStatus.FAILED;\r\n\r\n return (\r\n <>\r\n
\r\n {/* Content */}\r\n
\r\n
\r\n

\r\n {announcement.title}\r\n

\r\n \r\n
\r\n

\r\n {format(new Date(announcement.created_at), \"MMM d, yyyy\")}\r\n {announcement.sent_at &&\r\n ` • Sent ${format(new Date(announcement.sent_at), \"MMM d\")}`}\r\n

\r\n
\r\n\r\n {/* Actions */}\r\n
\r\n {canSend && (\r\n \r\n {isSending ? (\r\n
\r\n ) : (\r\n \r\n )}\r\n \r\n )}\r\n\r\n {canDelete && (\r\n setDeleteModalOpen(true)}\r\n disabled={isDeleting}\r\n className=\"p-2 text-red-500 hover:text-red-700 hover:bg-red-50 rounded-lg transition-colors disabled:opacity-50\"\r\n title=\"Delete\"\r\n >\r\n {isDeleting ? (\r\n
\r\n ) : (\r\n \r\n )}\r\n \r\n )}\r\n
\r\n
\r\n\r\n \r\n \r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\AnnouncementsClient.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'err' is defined but never used.","line":49,"column":14,"nodeType":null,"messageId":"unusedVar","endLine":49,"endColumn":17},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'loadAnnouncements'. Either include it or remove the dependency array.","line":58,"column":6,"nodeType":"ArrayExpression","endLine":58,"endColumn":14,"suggestions":[{"desc":"Update the dependencies array to be: [loadAnnouncements, teamId]","fix":{"range":[1678,1686],"text":"[loadAnnouncements, teamId]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport { useState, useEffect, useMemo } from \"react\";\r\nimport { getAnnouncementsForTeam } from \"@/actions/(announcements)/crud\";\r\nimport CreateAnnouncementDialog from \"./CreateAnnouncementDialog\";\r\nimport AnnouncementCard from \"./AnnouncementCard\";\r\nimport { EmailStatus } from \"@prisma/client\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\r\n\r\ninterface AnnouncementsClientProps {\r\n teamId: string;\r\n}\r\n\r\ninterface Announcement {\r\n id: bigint;\r\n title: string;\r\n content: string;\r\n created_at: Date;\r\n email_status: EmailStatus | null;\r\n sent_at: Date | null;\r\n recipient_count: number;\r\n delivered_count: number;\r\n error_message: string | null;\r\n}\r\n\r\nconst ITEMS_PER_PAGE = 5;\r\n\r\nexport default function AnnouncementsClient({\r\n teamId,\r\n}: AnnouncementsClientProps) {\r\n const [announcements, setAnnouncements] = useState([]);\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [error, setError] = useState(null);\r\n const [currentPage, setCurrentPage] = useState(1);\r\n\r\n async function loadAnnouncements() {\r\n setIsLoading(true);\r\n setError(null);\r\n\r\n try {\r\n const result = await getAnnouncementsForTeam(teamId);\r\n\r\n if (result.success && result.announcements) {\r\n setAnnouncements(result.announcements as Announcement[]);\r\n } else {\r\n setError(result.error || \"Failed to load announcements\");\r\n }\r\n } catch (err) {\r\n setError(\"An error occurred while loading announcements\");\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }\r\n\r\n useEffect(() => {\r\n loadAnnouncements();\r\n }, [teamId]);\r\n\r\n // Calculate pagination\r\n const totalPages = Math.ceil(announcements.length / ITEMS_PER_PAGE);\r\n const paginatedAnnouncements = useMemo(() => {\r\n const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;\r\n return announcements.slice(startIndex, startIndex + ITEMS_PER_PAGE);\r\n }, [announcements, currentPage]);\r\n\r\n // Reset to page 1 when announcements change\r\n useEffect(() => {\r\n setCurrentPage(1);\r\n }, [announcements.length]);\r\n\r\n if (isLoading) {\r\n return (\r\n
\r\n {/* Skeleton cards */}\r\n {[1, 2].map((i) => (\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n ))}\r\n
\r\n );\r\n }\r\n\r\n if (error) {\r\n return (\r\n
\r\n

{error}

\r\n
\r\n );\r\n }\r\n\r\n return (\r\n
\r\n {/* Header */}\r\n
\r\n
\r\n

\r\n Announcements\r\n

\r\n \r\n
\r\n \r\n
\r\n\r\n {/* Announcements List */}\r\n {announcements.length === 0 ? (\r\n
\r\n

No announcements yet

\r\n

\r\n Create your first announcement to get started\r\n

\r\n
\r\n ) : (\r\n <>\r\n
\r\n {paginatedAnnouncements.map((announcement) => (\r\n \r\n ))}\r\n
\r\n\r\n {/* Pagination */}\r\n {totalPages > 1 && (\r\n
\r\n

\r\n Page {currentPage} of {totalPages} ({announcements.length}{\" \"}\r\n total)\r\n

\r\n
\r\n setCurrentPage((p) => Math.max(1, p - 1))}\r\n disabled={currentPage === 1}\r\n className=\"px-2\"\r\n >\r\n \r\n Previous\r\n \r\n \r\n setCurrentPage((p) => Math.min(totalPages, p + 1))\r\n }\r\n disabled={currentPage === totalPages}\r\n className=\"px-2\"\r\n >\r\n Next\r\n \r\n \r\n
\r\n
\r\n )}\r\n \r\n )}\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\CreateAnnouncementDialog.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":66,"column":14,"nodeType":null,"messageId":"unusedVar","endLine":66,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport { useState } from \"react\";\r\nimport dynamic from \"next/dynamic\";\r\nimport { createAnnouncement } from \"@/actions/(announcements)/crud\";\r\nimport {\r\n AlertDialog,\r\n AlertDialogAction,\r\n AlertDialogCancel,\r\n AlertDialogContent,\r\n AlertDialogFooter,\r\n AlertDialogHeader,\r\n AlertDialogTitle,\r\n AlertDialogTrigger,\r\n} from \"@/components/ui/AlertDialog\";\r\nimport { Input } from \"@/components/ui/Input\";\r\nimport { Megaphone } from \"lucide-react\";\r\nimport toast from \"react-hot-toast\";\r\n\r\n// Dynamic import to avoid SSR issues with TipTap\r\nconst RichTextEditor = dynamic(() => import(\"@/components/ui/RichTextEditor\"), {\r\n ssr: false,\r\n loading: () => (\r\n
\r\n ),\r\n});\r\n\r\ninterface CreateAnnouncementDialogProps {\r\n teamId: string;\r\n onSuccess: () => void;\r\n}\r\n\r\nexport default function CreateAnnouncementDialog({\r\n teamId,\r\n onSuccess,\r\n}: CreateAnnouncementDialogProps) {\r\n const [open, setOpen] = useState(false);\r\n const [title, setTitle] = useState(\"\");\r\n const [content, setContent] = useState(\"\");\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n\r\n async function handleSubmit() {\r\n // Strip HTML to check if content is empty\r\n const textContent = content.replace(/<[^>]*>/g, \"\").trim();\r\n if (!title.trim() || !textContent) {\r\n toast.error(\"Please fill in all required fields\");\r\n return;\r\n }\r\n\r\n setIsSubmitting(true);\r\n\r\n try {\r\n const result = await createAnnouncement(teamId, title, content);\r\n\r\n if (result.success) {\r\n toast.success(\r\n \"Announcement created! Click Send to email team members.\",\r\n );\r\n setTitle(\"\");\r\n setContent(\"\");\r\n setOpen(false);\r\n onSuccess();\r\n } else {\r\n toast.error(result.error || \"Failed to create announcement\");\r\n }\r\n } catch (error) {\r\n toast.error(\"An error occurred\");\r\n } finally {\r\n setIsSubmitting(false);\r\n }\r\n }\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n \r\n Create Announcement\r\n \r\n\r\n
\r\n
\r\n setTitle(e.target.value)}\r\n maxLength={200}\r\n className=\"border-gray-200 focus:border-blue-500 rounded-lg\"\r\n required\r\n />\r\n

\r\n {title.length}/200 characters\r\n

\r\n
\r\n
\r\n \r\n
\r\n
\r\n\r\n \r\n Cancel\r\n ]*>/g, \"\").trim()\r\n }\r\n onClick={handleSubmit}\r\n >\r\n \r\n {isSubmitting ? \"Creating...\" : \"Create\"}\r\n \r\n \r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(announcements)\\components\\EmailStatusBadge.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\(actions)\\useCalendarActions.ts","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nC:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\(actions)\\useCalendarActions.ts:53:5\n 51 | // 5. Initial Load\n 52 | useEffect(() => {\n> 53 | loadEvents();\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 54 | }, [loadEvents]);\n 55 | const handleCreate = useCallback(\n 56 | async (data: CreateEventInput) => {","line":53,"column":5,"nodeType":null,"endLine":53,"endColumn":15}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useState, useCallback, useEffect } from \"react\";\r\nimport { View } from \"react-big-calendar\";\r\nimport {\r\n CalendarEvent,\r\n CreateEventInput,\r\n UpdateEventInput,\r\n} from \"@/lib/validations\";\r\nimport {\r\n createCalendarEvent,\r\n updateCalendarEvent,\r\n deleteCalendarEvent,\r\n} from \"@/actions/(events)/event\";\r\nimport { getTeamEvents_DB } from \"@/actions/(events)/event_db\";\r\nimport toast from \"react-hot-toast\";\r\n\r\nexport function useTeamCalendar(teamId: string) {\r\n // 1. Data State\r\n const [events, setEvents] = useState([]);\r\n const [isLoading, setIsLoading] = useState(true);\r\n\r\n // 2. Calendar View State\r\n const [date, setDate] = useState(new Date());\r\n const [view, setView] = useState(\"month\");\r\n\r\n // 3. Modal State\r\n const [modal, setModal] = useState<{\r\n isOpen: boolean;\r\n mode: \"create\" | \"edit\";\r\n selectedEvent: CalendarEvent | null;\r\n }>({\r\n isOpen: false,\r\n mode: \"create\",\r\n selectedEvent: null,\r\n });\r\n\r\n // 4. Load Events (extracted for refetch)\r\n const loadEvents = useCallback(async () => {\r\n setIsLoading(true);\r\n\r\n try {\r\n const events = await getTeamEvents_DB(teamId);\r\n setEvents(events);\r\n } catch (error) {\r\n console.error(\"Failed to load events:\", error);\r\n setEvents([]);\r\n }\r\n\r\n setIsLoading(false);\r\n }, [teamId]);\r\n\r\n // 5. Initial Load\r\n useEffect(() => {\r\n loadEvents();\r\n }, [loadEvents]);\r\n const handleCreate = useCallback(\r\n async (data: CreateEventInput) => {\r\n const toastId = toast.loading(\"Creating event...\");\r\n try {\r\n // Map desc to description for server action\r\n const result = await createCalendarEvent(teamId, {\r\n title: data.title,\r\n start: data.start,\r\n end: data.end,\r\n description: data.desc,\r\n });\r\n if (result.success) {\r\n toast.success(\"Event created successfully!\", { id: toastId });\r\n await loadEvents(); // Refetch canonical list\r\n } else {\r\n toast.error(result.error || \"Failed to create event\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\r\n error instanceof Error ? error.message : \"Failed to create event\",\r\n { id: toastId },\r\n );\r\n }\r\n },\r\n [teamId, loadEvents],\r\n );\r\n const handleUpdate = useCallback(\r\n async (data: UpdateEventInput) => {\r\n const toastId = toast.loading(\"Updating event...\");\r\n try {\r\n const result = await updateCalendarEvent(teamId, data);\r\n if (result.success) {\r\n toast.success(\"Event updated successfully!\", { id: toastId });\r\n await loadEvents(); // Refetch canonical list\r\n } else {\r\n toast.error(result.error || \"Failed to update event\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\r\n error instanceof Error ? error.message : \"Failed to update event\",\r\n { id: toastId },\r\n );\r\n }\r\n },\r\n [teamId, loadEvents],\r\n );\r\n const handleDelete = useCallback(\r\n async (id: string) => {\r\n const toastId = toast.loading(\"Deleting event...\");\r\n try {\r\n const result = await deleteCalendarEvent(teamId, id);\r\n if (result.success) {\r\n toast.success(\"Event deleted successfully!\", { id: toastId });\r\n await loadEvents(); // Refetch canonical list\r\n } else {\r\n toast.error(result.error || \"Failed to delete event\", {\r\n id: toastId,\r\n });\r\n }\r\n } catch (error) {\r\n toast.error(\r\n error instanceof Error ? error.message : \"Failed to delete event\",\r\n { id: toastId },\r\n );\r\n }\r\n },\r\n [teamId, loadEvents],\r\n );\r\n\r\n // 5. Actions (Scaffolded for User Implementation)\r\n const openCreate = useCallback(() => {\r\n setModal({ isOpen: true, mode: \"create\", selectedEvent: null });\r\n }, []);\r\n\r\n const openEdit = useCallback((event: CalendarEvent) => {\r\n setModal({ isOpen: true, mode: \"edit\", selectedEvent: event });\r\n }, []);\r\n\r\n const close = useCallback(() => {\r\n setModal((prev) => ({ ...prev, isOpen: false }));\r\n }, []);\r\n\r\n const submit = useCallback(\r\n async (data: CreateEventInput | UpdateEventInput) => {\r\n close(); // Close modal before showing toasts\r\n if (modal.mode === \"create\") {\r\n await handleCreate(data as CreateEventInput);\r\n } else if (modal.mode === \"edit\") {\r\n await handleUpdate(data as UpdateEventInput);\r\n }\r\n },\r\n [modal.mode, close, handleCreate, handleUpdate],\r\n );\r\n\r\n const remove = useCallback(\r\n async (id: string) => {\r\n close(); // Close modal before showing toasts\r\n await handleDelete(id);\r\n },\r\n [close, handleDelete],\r\n );\r\n\r\n return {\r\n // State\r\n events,\r\n isLoading,\r\n date,\r\n view,\r\n modal,\r\n\r\n // Setters\r\n setDate,\r\n setView,\r\n\r\n // Actions\r\n actions: {\r\n openCreate,\r\n openEdit,\r\n close,\r\n submit,\r\n remove,\r\n },\r\n };\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\CalendarToolbar.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'date' is assigned a value but never used.","line":17,"column":5,"nodeType":null,"messageId":"unusedVar","endLine":17,"endColumn":9},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'btnActive' is assigned a value but never used.","line":40,"column":9,"nodeType":null,"messageId":"unusedVar","endLine":40,"endColumn":18}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { View, ToolbarProps } from \"react-big-calendar\";\r\nimport { CalendarEvent } from \"@/lib/validations\";\r\nimport { ChevronDown, ChevronLeft, ChevronRight, Plus } from \"lucide-react\";\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from \"@/components/ui/DropdownMenu\";\r\n\r\ninterface CalendarToolbarProps extends ToolbarProps {\r\n onAddEvent?: () => void;\r\n}\r\n\r\nexport default function CalendarToolbar(props: CalendarToolbarProps) {\r\n const {\r\n date,\r\n view,\r\n views,\r\n label,\r\n onView,\r\n onNavigate,\r\n localizer,\r\n onAddEvent,\r\n } = props;\r\n\r\n const navigate = (action: \"PREV\" | \"NEXT\" | \"TODAY\") => {\r\n onNavigate(action);\r\n };\r\n\r\n const currentView = view;\r\n const availableViews = views as View[];\r\n\r\n // Shared button styles\r\n const btnBase =\r\n \"relative inline-flex items-center px-4 py-2 border border-[#e5e7eb] bg-white text-sm font-semibold text-[#111827] hover:bg-[#f9fafb] focus:z-10 focus:outline-none transition-colors\";\r\n const btnFirst = \"rounded-l-md\";\r\n const btnLast = \"rounded-r-md\";\r\n const btnMiddle = \"-ml-px\";\r\n const btnActive =\r\n \"bg-[#2563eb] text-white border-[#2563eb] hover:bg-[#1d4ed8] z-20\";\r\n\r\n return (\r\n
\r\n {/* Mobile Layout: Row 1 (Nav + View), Row 2 (Label) */}\r\n
\r\n
\r\n {/* Navigation Group - Connected Buttons */}\r\n
\r\n navigate(\"TODAY\")}\r\n >\r\n Today\r\n \r\n navigate(\"PREV\")}\r\n aria-label=\"Previous range\"\r\n >\r\n \r\n <\r\n \r\n \r\n navigate(\"NEXT\")}\r\n aria-label=\"Next range\"\r\n >\r\n \r\n >\r\n \r\n \r\n
\r\n\r\n {/* Mobile View Selector + Add Button */}\r\n
\r\n \r\n \r\n \r\n \r\n {localizer.messages[currentView] || currentView}\r\n \r\n \r\n \r\n \r\n \r\n {availableViews.map((v) => (\r\n onView(v)}\r\n className={`\r\n cursor-pointer px-3 py-2 font-medium capitalize rounded-sm text-sm\r\n hover:bg-[#f3f4f6] focus:bg-[#f3f4f6]\r\n ${currentView === v ? \"bg-[#eff6ff] text-[#2563eb]\" : \"text-[#111827]\"}\r\n `}\r\n >\r\n {localizer.messages[v] || v}\r\n \r\n ))}\r\n \r\n \r\n {onAddEvent && (\r\n \r\n \r\n Add Event\r\n \r\n )}\r\n
\r\n
\r\n\r\n {/* Label (Mobile) */}\r\n \r\n {label}\r\n \r\n
\r\n\r\n {/* Desktop Layout */}\r\n
\r\n {/* Navigation Group */}\r\n
\r\n navigate(\"TODAY\")}\r\n >\r\n Today\r\n \r\n navigate(\"PREV\")}\r\n aria-label=\"Previous range\"\r\n >\r\n \r\n \r\n navigate(\"NEXT\")}\r\n aria-label=\"Next range\"\r\n >\r\n \r\n \r\n
\r\n\r\n {/* Label (Desktop) */}\r\n \r\n {label}\r\n \r\n\r\n {/* View Selector Dropdown + Add Button (Desktop - matches mobile) */}\r\n
\r\n \r\n \r\n \r\n \r\n {localizer.messages[currentView] || currentView}\r\n \r\n \r\n \r\n \r\n \r\n {availableViews.map((v) => (\r\n onView(v)}\r\n className={`\r\n cursor-pointer px-3 py-2 font-medium capitalize rounded-sm text-sm\r\n hover:bg-[#f3f4f6] focus:bg-[#f3f4f6]\r\n ${currentView === v ? \"bg-[#eff6ff] text-[#2563eb]\" : \"text-[#111827]\"}\r\n `}\r\n >\r\n {localizer.messages[v] || v}\r\n \r\n ))}\r\n \r\n \r\n {onAddEvent && (\r\n \r\n \r\n Add Event\r\n \r\n )}\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\EventModal.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nC:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\EventModal.tsx:50:7\n 48 | useEffect(() => {\n 49 | if (mode === \"edit\" && event) {\n> 50 | setTitle(event.title);\n | ^^^^^^^^ Avoid calling setState() directly within an effect\n 51 | setStartStr(format(event.start, \"yyyy-MM-dd'T'HH:mm\"));\n 52 | setEndStr(format(event.end, \"yyyy-MM-dd'T'HH:mm\"));\n 53 | setDesc(event.desc || \"\");","line":50,"column":7,"nodeType":null,"endLine":50,"endColumn":15}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useState, useEffect } from \"react\";\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogDescription,\r\n DialogFooter,\r\n DialogHeader,\r\n DialogTitle,\r\n DialogOverlay,\r\n} from \"@/components/ui/Dialog\";\r\nimport { Input } from \"@/components/ui/Input\";\r\nimport { Button } from \"@/components/ui/Button\";\r\nimport {\r\n CalendarEvent,\r\n CreateEventInput,\r\n UpdateEventInput,\r\n CreateEventSchema,\r\n UpdateEventSchema,\r\n} from \"@/lib/validations\";\r\nimport { format } from \"date-fns\";\r\nimport { inter, instrumentSerif } from \"@/app/fonts\";\r\nimport toast from \"react-hot-toast\";\r\n\r\ninterface EventModalProps {\r\n isOpen: boolean;\r\n mode: \"create\" | \"edit\";\r\n event: CalendarEvent | null;\r\n onClose: () => void;\r\n onSubmit: (data: CreateEventInput | UpdateEventInput) => Promise;\r\n onDelete?: (id: string) => Promise;\r\n}\r\n\r\nexport default function EventModal({\r\n isOpen,\r\n mode,\r\n event,\r\n onClose,\r\n onSubmit,\r\n onDelete,\r\n}: EventModalProps) {\r\n const [title, setTitle] = useState(\"\");\r\n const [startStr, setStartStr] = useState(\"\");\r\n const [endStr, setEndStr] = useState(\"\");\r\n const [desc, setDesc] = useState(\"\");\r\n const [errors, setErrors] = useState>({});\r\n\r\n // Sync state with event data when editing\r\n useEffect(() => {\r\n if (mode === \"edit\" && event) {\r\n setTitle(event.title);\r\n setStartStr(format(event.start, \"yyyy-MM-dd'T'HH:mm\"));\r\n setEndStr(format(event.end, \"yyyy-MM-dd'T'HH:mm\"));\r\n setDesc(event.desc || \"\");\r\n } else {\r\n setTitle(\"\");\r\n setStartStr(\"\");\r\n setEndStr(\"\");\r\n setDesc(\"\");\r\n }\r\n setErrors({});\r\n }, [mode, event, isOpen]);\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setErrors({});\r\n\r\n if (mode === \"create\") {\r\n const data = {\r\n title,\r\n start: new Date(startStr),\r\n end: endStr ? new Date(endStr) : undefined, // Allow empty end date\r\n desc: desc || undefined,\r\n };\r\n\r\n // Validate with Zod\r\n const result = CreateEventSchema.safeParse(data);\r\n if (!result.success) {\r\n const fieldErrors: Record = {};\r\n result.error.issues.forEach((err) => {\r\n const path = err.path.join(\".\");\r\n fieldErrors[path] = err.message;\r\n });\r\n setErrors(fieldErrors);\r\n toast.error(\"Please fix the validation errors\");\r\n return;\r\n }\r\n\r\n await onSubmit(result.data);\r\n } else {\r\n const data = {\r\n id: event!.id,\r\n title,\r\n start: new Date(startStr),\r\n end: endStr ? new Date(endStr) : undefined, // Allow empty end date\r\n desc: desc || undefined,\r\n googleEventId: event!.googleEventId,\r\n };\r\n\r\n // Validate with Zod\r\n const result = UpdateEventSchema.safeParse(data);\r\n if (!result.success) {\r\n const fieldErrors: Record = {};\r\n result.error.issues.forEach((err) => {\r\n const path = err.path.join(\".\");\r\n fieldErrors[path] = err.message;\r\n });\r\n setErrors(fieldErrors);\r\n toast.error(\"Please fix the validation errors\");\r\n return;\r\n }\r\n\r\n await onSubmit(result.data);\r\n }\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n {mode === \"create\" ? \"📅 Add New Event\" : \"📝 Edit Event\"}\r\n \r\n \r\n {mode === \"create\"\r\n ? \"Enter the details for the new team event.\"\r\n : \"Update the details of this event.\"}\r\n \r\n \r\n\r\n
\r\n
\r\n \r\n setTitle(e.target.value)}\r\n placeholder=\"e.g. Design Sprint\"\r\n className={errors.title ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.title && (\r\n

{errors.title}

\r\n )}\r\n
\r\n
\r\n
\r\n \r\n setStartStr(e.target.value)}\r\n className={errors.start ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.start && (\r\n

{errors.start}

\r\n )}\r\n
\r\n
\r\n \r\n setEndStr(e.target.value)}\r\n className={errors.end ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.end && (\r\n

{errors.end}

\r\n )}\r\n
\r\n
\r\n
\r\n \r\n setDesc(e.target.value)}\r\n placeholder=\"Short description\"\r\n className={errors.desc ? \"border-red-500\" : \"\"}\r\n />\r\n {errors.desc && (\r\n

{errors.desc}

\r\n )}\r\n
\r\n\r\n \r\n {mode === \"edit\" && onDelete && (\r\n onDelete(event!.id)}\r\n >\r\n Delete\r\n \r\n )}\r\n
\r\n \r\n Cancel\r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\TeamCalendar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(calendar)\\components\\types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\AddMemberModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\EditMemberModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\ImportMembersModal.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'AlertDialogAction' is defined but never used.","line":5,"column":3,"nodeType":null,"messageId":"unusedVar","endLine":5,"endColumn":20},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":83,"column":18,"nodeType":null,"messageId":"unusedVar","endLine":83,"endColumn":23}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\nimport { useState, useRef } from \"react\";\r\nimport {\r\n AlertDialog,\r\n AlertDialogAction,\r\n AlertDialogCancel,\r\n AlertDialogContent,\r\n AlertDialogFooter,\r\n AlertDialogHeader,\r\n AlertDialogTitle,\r\n AlertDialogDescription,\r\n} from \"@/components/ui/AlertDialog\";\r\nimport { Upload, FileText, AlertCircle } from \"lucide-react\";\r\nimport { importMembersToTeam } from \"@/actions/members\";\r\nimport toast from \"react-hot-toast\";\r\n\r\ninterface ImportMembersModalProps {\r\n teamId: string;\r\n open: boolean;\r\n onOpenChange: (open: boolean) => void;\r\n onSuccess?: () => void;\r\n}\r\n\r\ninterface ImportResult {\r\n success: boolean;\r\n added: number;\r\n failed: number;\r\n errors?: string[];\r\n}\r\n\r\nfunction ImportMembersModal({\r\n teamId,\r\n open,\r\n onOpenChange,\r\n onSuccess,\r\n}: ImportMembersModalProps) {\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [selectedFile, setSelectedFile] = useState(null);\r\n const [fileError, setFileError] = useState(undefined);\r\n const fileInputRef = useRef(null);\r\n\r\n const handleFileSelect = (e: React.ChangeEvent) => {\r\n const file = e.target.files?.[0];\r\n if (file) {\r\n if (file.type !== \"text/csv\" && !file.name.endsWith(\".csv\")) {\r\n setFileError(\"Please select a CSV file\");\r\n setSelectedFile(null);\r\n return;\r\n }\r\n setSelectedFile(file);\r\n setFileError(undefined); // Clear error when valid file selected\r\n }\r\n };\r\n\r\n const parseCSV = async (\r\n file: File,\r\n ): Promise> => {\r\n return new Promise((resolve, reject) => {\r\n const reader = new FileReader();\r\n reader.onload = (e) => {\r\n try {\r\n const text = e.target?.result as string;\r\n const lines = text\r\n .split(\"\\n\")\r\n .map((line) => line.trim())\r\n .filter((line) => line.length > 0); // Remove empty lines\r\n\r\n if (lines.length <= 1) {\r\n // Only header or empty file\r\n resolve([]);\r\n return;\r\n }\r\n\r\n // Skip header row\r\n const dataLines = lines.slice(1);\r\n\r\n const members = dataLines.map((line) => {\r\n const [email, full_name] = line.split(\",\").map((s) => s.trim());\r\n return { email, full_name: full_name || undefined };\r\n });\r\n\r\n resolve(members);\r\n } catch (error) {\r\n reject(new Error(\"Failed to parse CSV file\"));\r\n }\r\n };\r\n reader.onerror = () => reject(new Error(\"Failed to read file\"));\r\n reader.readAsText(file);\r\n });\r\n };\r\n\r\n const handleImport = async () => {\r\n if (!selectedFile) {\r\n setFileError(\"Please select a file to import\");\r\n return; // Don't close modal\r\n }\r\n\r\n setFileError(undefined);\r\n setIsLoading(true);\r\n\r\n try {\r\n toast.loading(\"Parsing CSV file...\");\r\n const parsedMembers = await parseCSV(selectedFile);\r\n\r\n // Filter out invalid entries (empty email)\r\n const members = parsedMembers.filter((m) => m.email && m.email.trim());\r\n\r\n if (members.length === 0) {\r\n toast.dismiss();\r\n setFileError(\r\n \"No valid members found in CSV. Please check the file format.\",\r\n );\r\n setIsLoading(false);\r\n return; // Don't close modal\r\n }\r\n\r\n // Show warning if some entries were skipped\r\n if (members.length < parsedMembers.length) {\r\n const skipped = parsedMembers.length - members.length;\r\n toast.dismiss();\r\n toast(\r\n `Skipped ${skipped} invalid ${skipped === 1 ? \"entry\" : \"entries\"}. Importing ${members.length} members...`,\r\n { icon: \"⚠️\", duration: 3000 },\r\n );\r\n }\r\n\r\n toast.dismiss();\r\n toast.loading(`Importing ${members.length} members...`);\r\n\r\n const result = await importMembersToTeam(teamId, members);\r\n\r\n toast.dismiss();\r\n\r\n if (result.success) {\r\n const { added, failed } = result as ImportResult;\r\n if (failed > 0) {\r\n toast.success(\r\n `Imported ${added} members successfully. ${failed} failed.`,\r\n { duration: 5000 },\r\n );\r\n } else {\r\n toast.success(`Successfully imported ${added} members!`);\r\n }\r\n setSelectedFile(null);\r\n if (fileInputRef.current) fileInputRef.current.value = \"\";\r\n setFileError(undefined);\r\n onOpenChange(false); // Only close on success\r\n onSuccess?.();\r\n } else {\r\n setFileError(result.error || \"Failed to import members\");\r\n // Don't close modal - let user try again\r\n }\r\n } catch (error) {\r\n toast.dismiss();\r\n setFileError(\r\n error instanceof Error ? error.message : \"Failed to import members\",\r\n );\r\n // Don't close modal on error\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n Import Members\r\n \r\n \r\n Upload a CSV file with columns: email, full_name\r\n \r\n \r\n\r\n
\r\n
\r\n \r\n \r\n \r\n {selectedFile ? (\r\n <>\r\n \r\n

\r\n {selectedFile.name}\r\n

\r\n

\r\n Click to change file\r\n

\r\n \r\n ) : (\r\n <>\r\n \r\n

\r\n Click to upload CSV\r\n

\r\n

or drag and drop

\r\n \r\n )}\r\n \r\n
\r\n {fileError && (\r\n

{fileError}

\r\n )}\r\n
\r\n\r\n
\r\n \r\n
\r\n

CSV Format:

\r\n \r\n email,full_name\r\n
\r\n john@example.com,John Doe\r\n
\r\n jane@example.com,Jane Smith\r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n Cancel\r\n \r\n \r\n \r\n {isLoading ? \"Importing...\" : \"Import Members\"}\r\n \r\n \r\n \r\n \r\n );\r\n}\r\n\r\nexport default ImportMembersModal;\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\MemberActionsMenu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\MemberTable.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":82,"column":14,"nodeType":null,"messageId":"unusedVar","endLine":82,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport { DataTable, ColumnDef } from \"@/components/ui/DataTable\";\r\nimport { getMembersForTeam, removeMemberFromTeam } from \"@/actions/members\";\r\nimport { SearchInput } from \"@/app/dashboard/components/team-table/SearchInput\";\r\nimport { Pagination } from \"@/app/dashboard/components/team-table/Pagination\";\r\nimport { MemberActionsMenu } from \"./MemberActionsMenu\";\r\nimport ConfirmationModal from \"@/app/dashboard/components/team-table/ConfirmationModal\";\r\nimport EditMemberModal, { Member } from \"./EditMemberModal\";\r\nimport AddMemberModal from \"./AddMemberModal\";\r\nimport ImportMembersModal from \"./ImportMembersModal\";\r\nimport { MemberTableSkeleton } from \"./MemberTableSkeleton\";\r\nimport toast from \"react-hot-toast\";\r\n\r\nimport { UserPlus, Upload } from \"lucide-react\";\r\n\r\ninterface MemberTableProps {\r\n teamId: string;\r\n}\r\n\r\nexport default function MemberTable({ teamId }: MemberTableProps) {\r\n const [members, setMembers] = React.useState([]);\r\n const [isLoading, setIsLoading] = React.useState(true);\r\n const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);\r\n const [memberToRemove, setMemberToRemove] = React.useState(\r\n null,\r\n );\r\n const [editModalOpen, setEditModalOpen] = React.useState(false);\r\n const [memberToEdit, setMemberToEdit] = React.useState(null);\r\n const [addModalOpen, setAddModalOpen] = React.useState(false);\r\n const [importModalOpen, setImportModalOpen] = React.useState(false);\r\n\r\n // Fetch members on mount\r\n const fetchMembers = React.useCallback(async () => {\r\n setIsLoading(true);\r\n const result = await getMembersForTeam(teamId);\r\n if (result.success && result.members) {\r\n setMembers(result.members as Member[]);\r\n }\r\n setIsLoading(false);\r\n }, [teamId]);\r\n\r\n React.useEffect(() => {\r\n fetchMembers();\r\n }, [fetchMembers]);\r\n\r\n const handleRemoveClick = (member: Member) => {\r\n setMemberToRemove(member);\r\n setDeleteModalOpen(true);\r\n };\r\n\r\n const handleEditClick = (member: Member) => {\r\n setMemberToEdit(member);\r\n setEditModalOpen(true);\r\n };\r\n\r\n const handleConfirmRemove = async () => {\r\n if (!memberToRemove) return;\r\n\r\n // Immediate update\r\n const previousMembers = members;\r\n setMembers((prev) => prev.filter((m) => m.id !== memberToRemove.id));\r\n setDeleteModalOpen(false);\r\n setMemberToRemove(null);\r\n\r\n toast.loading(\r\n `Removing ${memberToRemove.full_name || memberToRemove.email}...`,\r\n );\r\n\r\n try {\r\n const result = await removeMemberFromTeam(teamId, memberToRemove.id);\r\n toast.dismiss();\r\n\r\n if (result.success) {\r\n toast.success(\"Member removed successfully!\");\r\n } else {\r\n // Rollback on error\r\n setMembers(previousMembers);\r\n toast.error(result.error || \"Failed to remove member\");\r\n }\r\n } catch (error) {\r\n // Rollback on error\r\n setMembers(previousMembers);\r\n toast.dismiss();\r\n toast.error(\"Failed to remove member\");\r\n }\r\n };\r\n\r\n const columns: ColumnDef[] = [\r\n {\r\n key: \"full_name\",\r\n header: \"Name\",\r\n className: \"w-[250px]\",\r\n render: (member) => (\r\n
\r\n \r\n {member.full_name || \"—\"}\r\n \r\n
\r\n ),\r\n },\r\n {\r\n key: \"email\",\r\n header: \"Email\",\r\n className: \"\",\r\n render: (member) =>
{member.email}
,\r\n },\r\n {\r\n key: \"added_at\",\r\n header: \"Added\",\r\n className: \"text-center\",\r\n render: (member) => (\r\n
\r\n {member.added_at\r\n ? new Date(member.added_at).toLocaleDateString()\r\n : \"—\"}\r\n
\r\n ),\r\n },\r\n ];\r\n\r\n return (\r\n <>\r\n {isLoading ? (\r\n \r\n ) : (\r\n
\r\n \r\n data={members}\r\n columns={columns}\r\n searchKey=\"full_name\"\r\n searchPlaceholder=\"Search members...\"\r\n emptyMessage=\"No members in this team yet.\"\r\n headerActions={\r\n <>\r\n setAddModalOpen(true)}\r\n className=\"px-3 md:px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium flex items-center gap-2\"\r\n >\r\n \r\n Add Member\r\n \r\n setImportModalOpen(true)}\r\n className=\"px-3 md:px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors font-medium flex items-center gap-2\"\r\n >\r\n \r\n Import\r\n \r\n \r\n }\r\n renderActions={(member) => (\r\n handleEditClick(member)}\r\n onRemove={() => handleRemoveClick(member)}\r\n />\r\n )}\r\n SearchComponent={SearchInput}\r\n PaginationComponent={Pagination}\r\n />\r\n
\r\n )}\r\n\r\n \r\n\r\n \r\n\r\n \r\n\r\n \r\n \r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\components\\MemberTableSkeleton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\(members)\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\TeamDashboardClient.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\TeamHeader.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\dashboard\\teams\\[teamsId]\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\fonts.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\hero\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\app\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\auth\\callback\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\email\\announcement.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\AlertDialog.tsx","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'buttonVariants' is defined but never used.","line":7,"column":10,"nodeType":null,"messageId":"unusedVar","endLine":7,"endColumn":24}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\r\n\r\nimport * as React from \"react\";\r\n\r\nimport { buttonVariants } from \"@/components/ui/Button\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nfunction AlertDialog({\r\n ...props\r\n}: React.ComponentProps) {\r\n return ;\r\n}\r\n\r\nfunction AlertDialogTrigger({\r\n ...props\r\n}: React.ComponentProps) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nfunction AlertDialogPortal({\r\n ...props\r\n}: React.ComponentProps) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nfunction AlertDialogOverlay({\r\n className,\r\n ...props\r\n}: React.ComponentProps) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nfunction AlertDialogContent({\r\n className,\r\n ...props\r\n}: React.ComponentProps) {\r\n return (\r\n \r\n \r\n \r\n \r\n );\r\n}\r\n\r\nfunction AlertDialogHeader({\r\n className,\r\n ...props\r\n}: React.ComponentProps<\"div\">) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nfunction AlertDialogFooter({\r\n className,\r\n ...props\r\n}: React.ComponentProps<\"div\">) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nfunction AlertDialogTitle({\r\n className,\r\n ...props\r\n}: React.ComponentProps) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nfunction AlertDialogDescription({\r\n className,\r\n ...props\r\n}: React.ComponentProps) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nfunction AlertDialogAction({\r\n className,\r\n ...props\r\n}: React.ComponentProps) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nfunction AlertDialogCancel({\r\n className,\r\n ...props\r\n}: React.ComponentProps) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nexport {\r\n AlertDialog,\r\n AlertDialogPortal,\r\n AlertDialogOverlay,\r\n AlertDialogTrigger,\r\n AlertDialogContent,\r\n AlertDialogHeader,\r\n AlertDialogFooter,\r\n AlertDialogTitle,\r\n AlertDialogDescription,\r\n AlertDialogAction,\r\n AlertDialogCancel,\r\n};\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\DataTable.tsx","messages":[{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":43,"column":52,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":43,"endColumn":55,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[969,972],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[969,972],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'searchPlaceholder' is assigned a value but never used.","line":47,"column":3,"nodeType":null,"messageId":"unusedVar","endLine":47,"endColumn":20}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from \"@/components/ui/Table\";\r\n\r\nexport interface ColumnDef {\r\n key: string;\r\n header: string;\r\n className?: string;\r\n render?: (item: T) => React.ReactNode;\r\n}\r\n\r\nexport interface DataTableProps {\r\n data: T[];\r\n columns: ColumnDef[];\r\n searchKey: keyof T;\r\n searchPlaceholder?: string;\r\n emptyMessage?: string;\r\n onRowClick?: (item: T) => void;\r\n renderActions?: (item: T) => React.ReactNode;\r\n itemsPerPage?: number;\r\n headerActions?: React.ReactNode;\r\n SearchComponent?: React.ComponentType<{\r\n value: string;\r\n onChange: (value: string) => void;\r\n }>;\r\n PaginationComponent?: React.ComponentType<{\r\n totalCount: number;\r\n canPrevious: boolean;\r\n canNext: boolean;\r\n onPrevious: () => void;\r\n onNext: () => void;\r\n }>;\r\n}\r\n\r\nexport function DataTable>({\r\n data,\r\n columns,\r\n searchKey,\r\n searchPlaceholder = \"Search...\",\r\n emptyMessage = \"No results found.\",\r\n onRowClick,\r\n renderActions,\r\n itemsPerPage = 5,\r\n headerActions,\r\n SearchComponent,\r\n PaginationComponent,\r\n}: DataTableProps) {\r\n const [searchValue, setSearchValue] = React.useState(\"\");\r\n const [currentPage, setCurrentPage] = React.useState(1);\r\n\r\n // Filter data based on search\r\n const filteredData = React.useMemo(() => {\r\n if (!searchValue) return data;\r\n return data.filter((item) => {\r\n const value = item[searchKey];\r\n if (typeof value === \"string\") {\r\n return value.toLowerCase().includes(searchValue.toLowerCase());\r\n }\r\n return false;\r\n });\r\n }, [data, searchValue, searchKey]);\r\n\r\n // Paginate filtered data\r\n const totalPages = Math.ceil(filteredData.length / itemsPerPage);\r\n const paginatedData = React.useMemo(() => {\r\n const start = (currentPage - 1) * itemsPerPage;\r\n return filteredData.slice(start, start + itemsPerPage);\r\n }, [filteredData, currentPage, itemsPerPage]);\r\n\r\n // Reset page when search changes\r\n React.useEffect(() => {\r\n setCurrentPage(1);\r\n }, [searchValue]);\r\n\r\n return (\r\n
\r\n
\r\n {SearchComponent && (\r\n
\r\n \r\n {headerActions && (\r\n
{headerActions}
\r\n )}\r\n
\r\n )}\r\n\r\n
\r\n \r\n \r\n \r\n {columns.map((column) => (\r\n \r\n \r\n {column.header}\r\n \r\n \r\n ))}\r\n {renderActions && (\r\n \r\n \r\n Actions\r\n \r\n \r\n )}\r\n \r\n \r\n \r\n {paginatedData.length ? (\r\n paginatedData.map((item, index) => (\r\n onRowClick(item) : undefined}\r\n className={onRowClick ? \"cursor-pointer\" : \"\"}\r\n >\r\n {columns.map((column) => (\r\n \r\n {column.render\r\n ? column.render(item)\r\n : String(item[column.key] || \"\")}\r\n \r\n ))}\r\n {renderActions && (\r\n \r\n {renderActions(item)}\r\n \r\n )}\r\n \r\n ))\r\n ) : (\r\n \r\n \r\n {emptyMessage}\r\n \r\n \r\n )}\r\n \r\n
\r\n
\r\n
\r\n\r\n {PaginationComponent && (\r\n 1}\r\n canNext={currentPage < totalPages}\r\n onPrevious={() => setCurrentPage((p) => Math.max(1, p - 1))}\r\n onNext={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}\r\n />\r\n )}\r\n
\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\DropdownMenu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Input.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\RichTextEditor.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Skeleton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Table.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\components\\ui\\Toast.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\eslint.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\calendar-utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\google-calendar.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\resend.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lib\\validations.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\lucide-react.d.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\next.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\postcss.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\postcss.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\prisma.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\proxy.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\public\\fonts.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'Archivo' is defined but never used.","line":1,"column":17,"nodeType":null,"messageId":"unusedVar","endLine":1,"endColumn":24}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Inter, Archivo, Instrument_Serif } from \"next/font/google\";\r\n\r\nexport const inter = Inter({\r\n subsets: [\"latin\"],\r\n variable: \"--font-inter\",\r\n display: \"swap\",\r\n});\r\n\r\nexport const instrumentSerif = Instrument_Serif({\r\n subsets: [\"latin\"],\r\n weight: \"400\",\r\n variable: \"--font-display\",\r\n display: \"swap\",\r\n});\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\supabase\\functions\\store-google-refresh\\index.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'serve' is defined but never used.","line":1,"column":10,"nodeType":null,"messageId":"unusedVar","endLine":1,"endColumn":15},{"ruleId":"@typescript-eslint/no-explicit-any","severity":2,"message":"Unexpected any. Specify a different type.","line":58,"column":27,"nodeType":"TSAnyKeyword","messageId":"unexpectedAny","endLine":58,"endColumn":30,"suggestions":[{"messageId":"suggestUnknown","fix":{"range":[2169,2172],"text":"unknown"},"desc":"Use `unknown` instead, this will force you to explicitly, and safely assert the type is correct."},{"messageId":"suggestNever","fix":{"range":[2169,2172],"text":"never"},"desc":"Use `never` instead, this is useful when instantiating generic type parameters that you don't need to know the type of."}]}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { serve } from \"https://deno.land/std@0.170.0/http/server.ts\";\r\n\r\n// Using Deno.serve per guidelines\r\nDeno.serve(async (req: Request) => {\r\n try {\r\n // Only allow POST\r\n if (req.method !== \"POST\") {\r\n return new Response(\r\n JSON.stringify({ success: false, message: \"Method not allowed\" }),\r\n { status: 405, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n const body = await req.json().catch(() => null);\r\n if (!body) {\r\n return new Response(\r\n JSON.stringify({ success: false, message: \"Invalid JSON body\" }),\r\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n const { userId, googleRefreshToken, email, fullName, avatarUrl } = body;\r\n if (!userId) {\r\n return new Response(\r\n JSON.stringify({ success: false, message: \"userId is required\" }),\r\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n // Build SQL parameters safely\r\n // Use Supabase DB URL to call Postgres directly\r\n const dbUrl = Deno.env.get(\"SUPABASE_DB_URL\");\r\n if (!dbUrl) {\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Database URL not configured\",\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n // Use fetch to call Postgres via connection string -- Use pg client would require npm; instead use RESTful SQL via Supabase REST? Not available. Instead use Postgres via Deno Postgres driver is not allowed. But Supabase provides REST admin via /rest/v1? We'll use the Supabase Admin REST endpoint using SUPABASE_URL and SERVICE_ROLE_KEY\r\n\r\n const supabaseUrl = Deno.env.get(\"SUPABASE_URL\");\r\n const serviceKey = Deno.env.get(\"SUPABASE_SERVICE_ROLE_KEY\");\r\n if (!supabaseUrl || !serviceKey) {\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Supabase env vars missing\",\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n // Upsert the profile\r\n const profilePayload: any = { id: userId };\r\n if (email !== undefined) profilePayload.email = email;\r\n if (fullName !== undefined) profilePayload.full_name = fullName;\r\n if (avatarUrl !== undefined) profilePayload.avatar_url = avatarUrl;\r\n if (googleRefreshToken !== undefined)\r\n profilePayload.google_refresh_token = googleRefreshToken;\r\n\r\n // Build PATCH (upsert) request to profiles table via Supabase REST\r\n // Use POST with Prefer: resolution=merge-duplicates for upsert on conflict\r\n\r\n const res = await fetch(`${supabaseUrl}/rest/v1/profiles?on_conflict=id`, {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n apikey: serviceKey,\r\n Authorization: `Bearer ${serviceKey}`,\r\n Prefer: \"resolution=merge-duplicates\",\r\n },\r\n body: JSON.stringify(profilePayload),\r\n });\r\n\r\n if (!res.ok) {\r\n const text = await res.text();\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Database request failed\",\r\n detail: text,\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n\r\n return new Response(\r\n JSON.stringify({ success: true, message: \"Token stored successfully\" }),\r\n { status: 200, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n } catch (err) {\r\n console.error(err);\r\n return new Response(\r\n JSON.stringify({\r\n success: false,\r\n message: \"Internal server error\",\r\n error: String(err),\r\n }),\r\n { status: 500, headers: { \"Content-Type\": \"application/json\" } }\r\n );\r\n }\r\n});\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\tailwind.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\auth-helpers.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\cn-helper.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\prisma\\prisma.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\supabase\\client.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\supabase\\proxy.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":56,"column":12,"nodeType":null,"messageId":"unusedVar","endLine":56,"endColumn":17}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { createServerClient } from \"@supabase/ssr\";\r\nimport { NextResponse, type NextRequest } from \"next/server\";\r\n\r\nexport async function updateSession(request: NextRequest) {\r\n let supabaseResponse = NextResponse.next({\r\n request,\r\n });\r\n\r\n // With Fluid compute, don't put this client in a global environment\r\n // variable. Always create a new one on each request.\r\n const supabase = createServerClient(\r\n process.env.NEXT_PUBLIC_SUPABASE_URL!,\r\n process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,\r\n {\r\n cookies: {\r\n getAll() {\r\n return request.cookies.getAll();\r\n },\r\n setAll(cookiesToSet) {\r\n cookiesToSet.forEach(({ name, value }) =>\r\n request.cookies.set(name, value)\r\n );\r\n supabaseResponse = NextResponse.next({\r\n request,\r\n });\r\n cookiesToSet.forEach(({ name, value, options }) =>\r\n supabaseResponse.cookies.set(name, value, options)\r\n );\r\n },\r\n },\r\n }\r\n );\r\n\r\n // Do not run code between createServerClient and\r\n // supabase.auth.getClaims(). A simple mistake could make it very hard to debug\r\n // issues with users being randomly logged out.\r\n\r\n // IMPORTANT: If you remove getClaims() and you use server-side rendering\r\n // with the Supabase client, your users may be randomly logged out.\r\n\r\n // Routes that don't require authentication\r\n const PUBLIC_ROUTES = [\"/hero\", \"/about\", \"/login\", \"/auth/callback\", \"/404\"];\r\n const isPublicRoute = PUBLIC_ROUTES.some((route) =>\r\n request.nextUrl.pathname.startsWith(route)\r\n );\r\n\r\n // Skip auth check for public routes to prevent redirect loops\r\n if (isPublicRoute) {\r\n return supabaseResponse;\r\n }\r\n\r\n let user;\r\n try {\r\n const { data } = await supabase.auth.getClaims();\r\n user = data?.claims;\r\n } catch (error) {\r\n //redirect login\r\n const url = request.nextUrl.clone();\r\n url.pathname = \"/login\";\r\n return NextResponse.redirect(url);\r\n }\r\n\r\n if (!user) {\r\n // no user, potentially respond by redirecting the user to the login page\r\n const url = request.nextUrl.clone();\r\n url.pathname = \"/login\";\r\n return NextResponse.redirect(url);\r\n }\r\n\r\n // IMPORTANT: You *must* return the supabaseResponse object as it is. If you're\r\n // creating a new response object with NextResponse.next() make sure to:\r\n // 1. Pass the request in it, like so:\r\n // const myNewResponse = NextResponse.next({ request })\r\n // 2. Copy over the cookies, like so:\r\n // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())\r\n // 3. Change the myNewResponse object to fit your needs, but avoid changing\r\n // the cookies!\r\n // 4. Finally:\r\n // return myNewResponse\r\n // If this is not done, you may be causing the browser and server to go out\r\n // of sync and terminate the user's session prematurely!\r\n supabaseResponse.headers.set(\"X-Content-Type-Options\", \"nosniff\");\r\n supabaseResponse.headers.set(\"X-Frame-Options\", \"DENY\");\r\n supabaseResponse.headers.set(\"X-XSS-Protection\", \"1; mode=block\");\r\n\r\n return supabaseResponse;\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\choco\\Downloads\\taskboard\\Taskboard\\utils\\supabase\\server.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]}] \ No newline at end of file diff --git a/public/fonts.ts b/public/fonts.ts index 8e259a2..338600c 100644 --- a/public/fonts.ts +++ b/public/fonts.ts @@ -1,4 +1,4 @@ -import { Inter, Archivo, Instrument_Serif } from "next/font/google"; +import { Inter, Instrument_Serif } from "next/font/google"; export const inter = Inter({ subsets: ["latin"], diff --git a/supabase/functions/store-google-refresh/index.ts b/supabase/functions/store-google-refresh/index.ts index 61b603c..20d9044 100644 --- a/supabase/functions/store-google-refresh/index.ts +++ b/supabase/functions/store-google-refresh/index.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line @typescript-eslint/no-unused-vars import { serve } from "https://deno.land/std@0.170.0/http/server.ts"; // Using Deno.serve per guidelines @@ -7,7 +8,7 @@ Deno.serve(async (req: Request) => { if (req.method !== "POST") { return new Response( JSON.stringify({ success: false, message: "Method not allowed" }), - { status: 405, headers: { "Content-Type": "application/json" } } + { status: 405, headers: { "Content-Type": "application/json" } }, ); } @@ -15,7 +16,7 @@ Deno.serve(async (req: Request) => { if (!body) { return new Response( JSON.stringify({ success: false, message: "Invalid JSON body" }), - { status: 400, headers: { "Content-Type": "application/json" } } + { status: 400, headers: { "Content-Type": "application/json" } }, ); } @@ -23,7 +24,7 @@ Deno.serve(async (req: Request) => { if (!userId) { return new Response( JSON.stringify({ success: false, message: "userId is required" }), - { status: 400, headers: { "Content-Type": "application/json" } } + { status: 400, headers: { "Content-Type": "application/json" } }, ); } @@ -36,7 +37,7 @@ Deno.serve(async (req: Request) => { success: false, message: "Database URL not configured", }), - { status: 500, headers: { "Content-Type": "application/json" } } + { status: 500, headers: { "Content-Type": "application/json" } }, ); } @@ -50,11 +51,12 @@ Deno.serve(async (req: Request) => { success: false, message: "Supabase env vars missing", }), - { status: 500, headers: { "Content-Type": "application/json" } } + { status: 500, headers: { "Content-Type": "application/json" } }, ); } // Upsert the profile + // eslint-disable-next-line @typescript-eslint/no-explicit-any const profilePayload: any = { id: userId }; if (email !== undefined) profilePayload.email = email; if (fullName !== undefined) profilePayload.full_name = fullName; @@ -84,13 +86,13 @@ Deno.serve(async (req: Request) => { message: "Database request failed", detail: text, }), - { status: 500, headers: { "Content-Type": "application/json" } } + { status: 500, headers: { "Content-Type": "application/json" } }, ); } return new Response( JSON.stringify({ success: true, message: "Token stored successfully" }), - { status: 200, headers: { "Content-Type": "application/json" } } + { status: 200, headers: { "Content-Type": "application/json" } }, ); } catch (err) { console.error(err); @@ -100,7 +102,7 @@ Deno.serve(async (req: Request) => { message: "Internal server error", error: String(err), }), - { status: 500, headers: { "Content-Type": "application/json" } } + { status: 500, headers: { "Content-Type": "application/json" } }, ); } }); diff --git a/utils/supabase/proxy.ts b/utils/supabase/proxy.ts index 13945f8..9e45a3d 100644 --- a/utils/supabase/proxy.ts +++ b/utils/supabase/proxy.ts @@ -18,17 +18,17 @@ export async function updateSession(request: NextRequest) { }, setAll(cookiesToSet) { cookiesToSet.forEach(({ name, value }) => - request.cookies.set(name, value) + request.cookies.set(name, value), ); supabaseResponse = NextResponse.next({ request, }); cookiesToSet.forEach(({ name, value, options }) => - supabaseResponse.cookies.set(name, value, options) + supabaseResponse.cookies.set(name, value, options), ); }, }, - } + }, ); // Do not run code between createServerClient and @@ -41,7 +41,7 @@ export async function updateSession(request: NextRequest) { // Routes that don't require authentication const PUBLIC_ROUTES = ["/hero", "/about", "/login", "/auth/callback", "/404"]; const isPublicRoute = PUBLIC_ROUTES.some((route) => - request.nextUrl.pathname.startsWith(route) + request.nextUrl.pathname.startsWith(route), ); // Skip auth check for public routes to prevent redirect loops @@ -53,7 +53,7 @@ export async function updateSession(request: NextRequest) { try { const { data } = await supabase.auth.getClaims(); user = data?.claims; - } catch (error) { + } catch { //redirect login const url = request.nextUrl.clone(); url.pathname = "/login"; From 4353a5b9f64eee28215ae9f328bf0b970ab2609a Mon Sep 17 00:00:00 2001 From: PC Date: Sun, 8 Feb 2026 18:40:25 +0800 Subject: [PATCH 2/6] test: added 4 test suites from jest --- __tests__/actions/auth.test.ts | 88 + __tests__/actions/members.test.ts | 162 ++ __tests__/actions/teams.test.ts | 159 ++ __tests__/api/webhooks/resend.test.ts | 104 + __tests__/helpers/fixtures.ts | 42 + __tests__/mocks/prisma.ts | 5 + __tests__/mocks/supabase.ts | 52 + __tests__/setup.ts | 18 + jest.config.mjs | 34 + package-lock.json | 3117 ++++++++++++++++++++++++- package.json | 11 +- utils/prisma/__mocks__/prisma.ts | 5 + 12 files changed, 3687 insertions(+), 110 deletions(-) create mode 100644 __tests__/actions/auth.test.ts create mode 100644 __tests__/actions/members.test.ts create mode 100644 __tests__/actions/teams.test.ts create mode 100644 __tests__/api/webhooks/resend.test.ts create mode 100644 __tests__/helpers/fixtures.ts create mode 100644 __tests__/mocks/prisma.ts create mode 100644 __tests__/mocks/supabase.ts create mode 100644 __tests__/setup.ts create mode 100644 jest.config.mjs create mode 100644 utils/prisma/__mocks__/prisma.ts diff --git a/__tests__/actions/auth.test.ts b/__tests__/actions/auth.test.ts new file mode 100644 index 0000000..c3f5f2e --- /dev/null +++ b/__tests__/actions/auth.test.ts @@ -0,0 +1,88 @@ +import { getUser, requireAuth, signOut } from "@/actions/auth"; +import { + mockAuthenticatedUser, + mockUnauthenticatedUser, + setupSupabaseMock, + mockSupabase, +} from "@/__tests__/mocks/supabase"; +import { redirect } from "next/navigation"; + +// Mock dependencies +jest.mock("@/utils/supabase/server"); +jest.mock("next/navigation", () => ({ + redirect: jest.fn(), +})); + +describe("Auth Actions", () => { + beforeEach(() => { + setupSupabaseMock(); + jest.clearAllMocks(); + }); + + describe("getUser", () => { + it("should return the user when authenticated", async () => { + mockAuthenticatedUser("user-123"); + const result = await getUser(); + expect(result.data.user?.id).toBe("user-123"); + }); + + it("should return null user when unauthenticated", async () => { + mockUnauthenticatedUser(); + const result = await getUser(); + expect(result.data.user).toBeNull(); + }); + }); + + describe("requireAuth", () => { + it("should return user if authenticated", async () => { + mockAuthenticatedUser("user-123"); + const user = await requireAuth(); + expect(user?.id).toBe("user-123"); + expect(redirect).not.toHaveBeenCalled(); + }); + + it("should redirect to /login if unauthenticated", async () => { + mockUnauthenticatedUser(); + + try { + await requireAuth(); + } catch (e) { + // redirect throws an error in Next.js, catch it + } + + expect(redirect).toHaveBeenCalledWith("/login"); + }); + }); + + describe("signOut", () => { + it("should sign out and redirect", async () => { + mockSupabase.auth.signOut.mockResolvedValue({ error: null }); + + try { + await signOut(); + } catch (e) { + // redirect throws + } + + expect(mockSupabase.auth.signOut).toHaveBeenCalled(); + expect(redirect).toHaveBeenCalledWith("/login"); + }); + + it("should log error if sign out fails", async () => { + const consoleSpy = jest + .spyOn(console, "error") + .mockImplementation(() => {}); + mockSupabase.auth.signOut.mockResolvedValue({ + error: { message: "Failed", name: "AuthError", status: 500 }, + }); + + await signOut(); + + expect(mockSupabase.auth.signOut).toHaveBeenCalled(); + expect(redirect).not.toHaveBeenCalled(); + expect(consoleSpy).toHaveBeenCalled(); + + consoleSpy.mockRestore(); + }); + }); +}); diff --git a/__tests__/actions/members.test.ts b/__tests__/actions/members.test.ts new file mode 100644 index 0000000..e2590af --- /dev/null +++ b/__tests__/actions/members.test.ts @@ -0,0 +1,162 @@ +import { + addMemberToTeam, + getMembersForTeam, + importMembersToTeam, + removeMemberFromTeam, + updateMember, +} from "@/actions/members"; +import { prismaMock } from "@/__tests__/mocks/prisma"; +import { + mockAuthenticatedUser, + mockSupabase, + setupSupabaseMock, +} from "@/__tests__/mocks/supabase"; +import { createMockMember, createMockTeam } from "@/__tests__/helpers/fixtures"; + +// Mock dependencies +jest.mock("@/utils/supabase/server"); + +describe("Member Actions", () => { + const userId = "user-123"; + const teamId = "team-123"; + + beforeEach(() => { + console.log("Prisma Mock:", prismaMock); // Debug log + setupSupabaseMock(); + mockAuthenticatedUser(userId); + jest.clearAllMocks(); + }); + + describe("addMemberToTeam", () => { + it("should successfully add a new member", async () => { + const email = "new@example.com"; + const fullName = "New Member"; + const mockTeam = createMockTeam({ id: teamId, leader_id: userId }); + + // Mock team ownership check + prismaMock.teams.findFirst.mockResolvedValue(mockTeam); + + // Mock existing member check (not found) + prismaMock.members.findUnique.mockResolvedValue(null); + // Mock create member + const newMember = createMockMember({ email, full_name: fullName }); + prismaMock.members.create.mockResolvedValue(newMember); + + // Mock existing membership + prismaMock.team_members.findUnique.mockResolvedValue(null); + + // Mock create membership + prismaMock.team_members.create.mockResolvedValue({} as any); + + const result = await addMemberToTeam(teamId, email, fullName); + + expect(result.success).toBe(true); + expect(prismaMock.teams.findFirst).toHaveBeenCalledWith({ + where: { id: teamId, leader_id: userId }, + }); + expect(prismaMock.team_members.create).toHaveBeenCalled(); + }); + + it("should fail if user is not authorized", async () => { + // Mock no user + mockSupabase.auth.getUser.mockResolvedValue({ + data: { user: null }, + error: null, + }); + + await expect( + addMemberToTeam(teamId, "test@test.com", "Test"), + ).rejects.toThrow("Unauthorized Action"); + }); + + it("should return error for invalid email", async () => { + const result = await addMemberToTeam(teamId, "invalid-email", "Name"); + expect(result.success).toBe(false); + expect(result.error).toContain("Must be a valid email address"); + }); + }); + + describe("getMembersForTeam", () => { + it("should return members for a team", async () => { + const mockTeam = createMockTeam({ id: teamId, leader_id: userId }); + prismaMock.teams.findFirst.mockResolvedValue(mockTeam); + + const members = [ + { + member: createMockMember({ email: "m1@test.com" }), + added_at: new Date(), + }, + { + member: createMockMember({ email: "m2@test.com" }), + added_at: new Date(), + }, + ]; + + // Mock prisma response structure roughly matching the include + prismaMock.team_members.findMany.mockResolvedValue(members as any); + + const result = await getMembersForTeam(teamId); + + // Verify object return structure + if (result.success) { + expect(result.members).toHaveLength(2); + expect(result.members![0].email).toBe("m1@test.com"); + } else { + fail("Expected success to be true"); + } + }); + }); + + describe("importMembersToTeam", () => { + it("should import valid members", async () => { + const membersToImport = [ + { email: "test1@example.com", full_name: "Test 1" }, + { email: "test2@example.com", full_name: "Test 2" }, + ]; + + const mockTeam = createMockTeam({ id: teamId, leader_id: userId }); + prismaMock.teams.findFirst.mockResolvedValue(mockTeam); + + // Mock member check/create + prismaMock.members.findUnique.mockResolvedValue(null); + prismaMock.members.create.mockImplementation( + (args) => createMockMember({ email: args.data.email }) as any, + ); + + // Mock team membership check/create + prismaMock.team_members.findUnique.mockResolvedValue(null); + prismaMock.team_members.create.mockResolvedValue({} as any); + + const result = await importMembersToTeam(teamId, membersToImport); + + expect(result.success).toBe(true); + // Verify added count + if (result.success) { + expect(result.added).toBe(2); + } + }); + + it("should handle partial failures or invalid data gracefully", async () => { + const invalidMembers = [{ email: "invalid", full_name: "Bad" }]; + const result = await importMembersToTeam(teamId, invalidMembers); + + // Validation failure returns early + expect(result.success).toBe(false); + }); + }); + + describe("removeMemberFromTeam", () => { + it("should remove a member", async () => { + const memberId = "member-1"; + const mockTeam = createMockTeam({ id: teamId, leader_id: userId }); + prismaMock.teams.findFirst.mockResolvedValue(mockTeam); + + prismaMock.team_members.delete.mockResolvedValue({} as any); + + const result = await removeMemberFromTeam(teamId, memberId); + + expect(result.success).toBe(true); + expect(prismaMock.team_members.delete).toHaveBeenCalled(); + }); + }); +}); diff --git a/__tests__/actions/teams.test.ts b/__tests__/actions/teams.test.ts new file mode 100644 index 0000000..8bee81f --- /dev/null +++ b/__tests__/actions/teams.test.ts @@ -0,0 +1,159 @@ +import { + createTeam, + deleteTeam, + updateTeam, + verifyTeamOwnership, +} from "@/actions/teams"; +import { prismaMock } from "@/__tests__/mocks/prisma"; +import { + mockAuthenticatedUser, + mockSupabase, + setupSupabaseMock, +} from "@/__tests__/mocks/supabase"; +import { createMockTeam } from "@/__tests__/helpers/fixtures"; +import { redirect } from "next/navigation"; + +// Mock dependencies +jest.mock("@/utils/supabase/server"); +jest.mock("next/navigation", () => ({ + redirect: jest.fn(), +})); +jest.mock("next/cache", () => ({ + revalidatePath: jest.fn(), +})); + +// Mock calendar actions +jest.mock("@/actions/(calendar)/calendar", () => ({ + createTeamCalendar: jest.fn(), + deleteTeamCalendar: jest.fn(), + updateTeamCalendar: jest.fn(), +})); + +import { + createTeamCalendar, + deleteTeamCalendar, + updateTeamCalendar, +} from "@/actions/(calendar)/calendar"; + +describe("Team Actions", () => { + const userId = "123e4567-e89b-12d3-a456-426614174000"; // Valid UUID + const teamId = "123e4567-e89b-12d3-a456-426614174001"; // Valid UUID + + beforeEach(() => { + setupSupabaseMock(); + mockAuthenticatedUser(userId); + jest.clearAllMocks(); + }); + + describe("createTeam", () => { + it("should successfully create a team and calendar", async () => { + const teamName = "New Team"; + const mockCalendarId = "calendar-123"; + + // Mock calendar creation success + (createTeamCalendar as jest.Mock).mockResolvedValue({ + success: true, + calendarId: mockCalendarId, + }); + + // Mock prisma create + const newTeam = createMockTeam({ + name: teamName, + leader_id: userId, + google_calendar_id: mockCalendarId, + }); + prismaMock.teams.create.mockResolvedValue(newTeam); + + const result = await createTeam(teamName); + + expect(result.success).toBe(true); + expect(result.team).toEqual(newTeam); + expect(createTeamCalendar).toHaveBeenCalledWith(teamName); + expect(prismaMock.teams.create).toHaveBeenCalledWith({ + data: { + name: teamName, + leader_id: userId, + google_calendar_id: mockCalendarId, + }, + }); + }); + + it("should handle calendar creation failure gracefully", async () => { + const teamName = "New Team"; + + // Mock calendar creation failure + (createTeamCalendar as jest.Mock).mockResolvedValue({ + success: false, + error: "Calendar Error", + }); + + const newTeam = createMockTeam({ + name: teamName, + leader_id: userId, + google_calendar_id: null, + }); + prismaMock.teams.create.mockResolvedValue(newTeam); + + const result = await createTeam(teamName); + + expect(result.success).toBe(true); + expect(result.calendarCreated).toBe(false); + // specific logic check: google_calendar_id should be null + }); + }); + + describe("verifyTeamOwnership", () => { + it("should return team if user is leader", async () => { + const mockTeam = createMockTeam({ id: teamId, leader_id: userId }); + prismaMock.teams.findFirst.mockResolvedValue(mockTeam); + + const result = await verifyTeamOwnership(teamId, userId); + expect(result).toEqual(mockTeam); + }); + + it("should redirect if team not found or not leader", async () => { + prismaMock.teams.findFirst.mockResolvedValue(null); + + try { + await verifyTeamOwnership(teamId, userId); + } catch (e) { + // redirect + } + expect(redirect).toHaveBeenCalledWith("/dashboard"); + }); + + it("should throw on invalid UUID", async () => { + await expect(verifyTeamOwnership("invalid-uuid", userId)).rejects.toThrow( + "Invalid team ID", + ); + }); + }); + + describe("deleteTeam", () => { + it("should delete team and calendar", async () => { + (deleteTeamCalendar as jest.Mock).mockResolvedValue({ success: true }); + prismaMock.teams.delete.mockResolvedValue({} as any); + + const result = await deleteTeam(teamId); + + expect(result.success).toBe(true); + expect(deleteTeamCalendar).toHaveBeenCalledWith(teamId); + expect(prismaMock.teams.delete).toHaveBeenCalledWith({ + where: { id: teamId }, + }); + }); + }); + + describe("updateTeam", () => { + it("should update team name and calendar", async () => { + const newName = "Updated Team"; + (updateTeamCalendar as jest.Mock).mockResolvedValue({ success: true }); + prismaMock.teams.update.mockResolvedValue({} as any); + + const result = await updateTeam(teamId, newName); + + expect(result.success).toBe(true); + expect(updateTeamCalendar).toHaveBeenCalledWith(teamId, newName); + }); + }); +}); diff --git a/__tests__/api/webhooks/resend.test.ts b/__tests__/api/webhooks/resend.test.ts new file mode 100644 index 0000000..50dc018 --- /dev/null +++ b/__tests__/api/webhooks/resend.test.ts @@ -0,0 +1,104 @@ +import { POST } from "@/app/api/webhooks/resend/route"; +import { prismaMock } from "@/__tests__/mocks/prisma"; +import { createMockAnnouncement } from "@/__tests__/helpers/fixtures"; +import { NextRequest } from "next/server"; +import { Webhook } from "svix"; + +// Mock Svix to avoid real signature verification +jest.mock("svix"); + +describe("Resend Webhook API", () => { + const mockWebhookSecret = "whsec_test_secret"; + + beforeEach(() => { + process.env.RESEND_WEBHOOK_SECRET = mockWebhookSecret; + jest.clearAllMocks(); + + // Mock Webhook verify method + (Webhook as unknown as jest.Mock).mockImplementation(() => ({ + verify: jest.fn().mockReturnValue({ + type: "email.delivered", + data: { + created_at: "2024-01-01T00:00:00.000Z", + email_id: "email_123", + to: ["test@example.com"], + batch_id: "batch_123", + }, + }), + })); + }); + + afterEach(() => { + delete process.env.RESEND_WEBHOOK_SECRET; + }); + + const createRequest = (body: string) => { + return new NextRequest("http://localhost/api/webhooks/resend", { + method: "POST", + headers: { + "svix-id": "msg_123", + "svix-timestamp": "1234567890", + "svix-signature": "v1,signature_123", + "content-type": "application/json", + }, + body, + }); + }; + + it("should process delivered event and update counts", async () => { + const payload = JSON.stringify({ type: "email.delivered" }); + const req = createRequest(payload); + + // Mock finding announcement + const announcements = [ + createMockAnnouncement({ + id: BigInt(1), + resend_batch_id: "batch_123", + delivered_count: 0, + recipient_count: 10, + }), + ]; + + // Mock findMany with the OR condition we fixed + prismaMock.announcements.findMany.mockResolvedValue(announcements as any); + + // Mock update + prismaMock.announcements.update.mockResolvedValue({} as any); + // Mock findUnique for status check + prismaMock.announcements.findUnique.mockResolvedValue( + announcements[0] as any, + ); + + const response = await POST(req); + + expect(response.status).toBe(200); + expect(prismaMock.announcements.update).toHaveBeenCalled(); + expect(prismaMock.announcements.findMany).toHaveBeenCalledWith( + expect.objectContaining({ + where: { + OR: [ + { resend_batch_id: "batch_123" }, + { resend_batch_id: { contains: "batch_123" } }, + ], + }, + }), + ); + }); + + it("should return 200 but warn if batch ID matches nothing", async () => { + const payload = JSON.stringify({ type: "email.delivered" }); + const req = createRequest(payload); + + prismaMock.announcements.findMany.mockResolvedValue([]); + + const response = await POST(req); + expect(response.status).toBe(200); // Idempotent success + }); + + it("should return 500 if webhook secret is missing", async () => { + delete process.env.RESEND_WEBHOOK_SECRET; + const req = createRequest("{}"); + const response = await POST(req); + expect(response.status).toBe(500); + }); +}); diff --git a/__tests__/helpers/fixtures.ts b/__tests__/helpers/fixtures.ts new file mode 100644 index 0000000..ae8d383 --- /dev/null +++ b/__tests__/helpers/fixtures.ts @@ -0,0 +1,42 @@ +import { v4 as uuidv4 } from "uuid"; + +export const createMockUser = (overrides = {}) => ({ + id: uuidv4(), + email: "test@example.com", + ...overrides, +}); + +export const createMockTeam = (overrides = {}) => ({ + id: uuidv4(), + name: "Test Team", + leader_id: uuidv4(), + created_at: new Date(), + google_calendar_id: null, + _count: { + teamMembers: 0, + }, + ...overrides, +}); + +export const createMockMember = (overrides = {}) => ({ + id: uuidv4(), + email: "member@example.com", + full_name: "Test Member", + created_at: new Date(), + ...overrides, +}); + +export const createMockAnnouncement = (overrides = {}) => ({ + id: BigInt(1), + title: "Test Announcement", + content: "Test Content", + team_id: uuidv4(), + created_at: new Date(), + sent_at: null, + email_status: "PENDING", + recipient_count: 0, + delivered_count: 0, + error_message: null, + resend_batch_id: null, + ...overrides, +}); diff --git a/__tests__/mocks/prisma.ts b/__tests__/mocks/prisma.ts new file mode 100644 index 0000000..12b77c9 --- /dev/null +++ b/__tests__/mocks/prisma.ts @@ -0,0 +1,5 @@ +import { PrismaClient } from "@prisma/client"; +import { DeepMockProxy } from "jest-mock-extended"; +import prisma from "@/utils/prisma/prisma"; + +export const prismaMock = prisma as unknown as DeepMockProxy; diff --git a/__tests__/mocks/supabase.ts b/__tests__/mocks/supabase.ts new file mode 100644 index 0000000..125cbe6 --- /dev/null +++ b/__tests__/mocks/supabase.ts @@ -0,0 +1,52 @@ +import { createClient } from "@/utils/supabase/server"; + +// Mock the server client creator +jest.mock("@/utils/supabase/server", () => ({ + createClient: jest.fn(), +})); + +export const mockSupabase = { + auth: { + getUser: jest.fn(), + signOut: jest.fn(), + signInWithPassword: jest.fn(), + }, +}; + +// Helper to setup the mock implementation +export const setupSupabaseMock = () => { + (createClient as jest.Mock).mockResolvedValue(mockSupabase); + + // Reset default behaviors + mockSupabase.auth.getUser.mockReset(); + mockSupabase.auth.signOut.mockReset(); +}; + +// Helper to mock an authenticated user +export const mockAuthenticatedUser = ( + userId: string, + email: string = "test@example.com", +) => { + mockSupabase.auth.getUser.mockResolvedValue({ + data: { + user: { + id: userId, + email, + aud: "authenticated", + role: "authenticated", + app_metadata: {}, + user_metadata: {}, + created_at: new Date().toISOString(), + }, + }, + error: null, + }); +}; + +// Helper to mock unauthenticated state +export const mockUnauthenticatedUser = () => { + mockSupabase.auth.getUser.mockResolvedValue({ + data: { user: null }, + error: { message: "Not authenticated", name: "AuthError", status: 401 }, + }); +}; diff --git a/__tests__/setup.ts b/__tests__/setup.ts new file mode 100644 index 0000000..c0def73 --- /dev/null +++ b/__tests__/setup.ts @@ -0,0 +1,18 @@ +// Mock process.env variables if needed/global +process.env.RESEND_API_KEY = "re_mock_key_123"; +process.env.DATABASE_URL = + "postgresql://postgres:postgres@localhost:5432/taskboard_test"; +process.env.NEXT_PUBLIC_SUPABASE_URL = "https://mock.supabase.co"; + +// This will use the manual mock in utils/prisma/__mocks__/prisma.ts +jest.mock("@/utils/prisma/prisma"); +process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = "mock-anon-key"; +process.env.SUPABASE_SERVICE_ROLE_KEY = "mock-service-role-key"; + +// Global console mock to reduce noise during tests if desired +// global.console = { +// ...console, +// log: jest.fn(), +// warn: jest.fn(), +// error: jest.fn(), +// }; diff --git a/jest.config.mjs b/jest.config.mjs new file mode 100644 index 0000000..859bec3 --- /dev/null +++ b/jest.config.mjs @@ -0,0 +1,34 @@ +import nextJest from 'next/jest.js' + +const createJestConfig = nextJest({ + // Provide the path to your Next.js app to load next.config.js and .env files in your test environment + dir: './', +}) + +// Add any custom config to be passed to Jest +/** @type {import('jest').Config} */ +const config = { + coverageProvider: 'v8', + testEnvironment: 'node', + setupFilesAfterEnv: ['/__tests__/setup.ts'], + moduleNameMapper: { + '^@/(.*)$': '/$1', + }, + testPathIgnorePatterns: [ + '/node_modules/', + '/.next/', + '/__tests__/mocks/', + '/__tests__/helpers/', + '/__tests__/setup.ts', + ], + collectCoverageFrom: [ + 'actions/**/*.{ts,tsx}', + 'lib/**/*.{ts,tsx}', + 'utils/**/*.{ts,tsx}', + '!**/*.d.ts', + '!**/node_modules/**', + ], +} + +// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async +export default createJestConfig(config) diff --git a/package-lock.json b/package-lock.json index 014fd06..f35b86e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,16 +48,22 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", + "@types/jest": "^29.5.14", "@types/node": "^20.19.27", "@types/pg": "^8.16.0", "@types/react": "^19", "@types/react-big-calendar": "^1.16.3", "@types/react-dom": "^19", + "@types/uuid": "^10.0.0", "eslint": "^9", "eslint-config-next": "16.1.0", + "jest": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-mock-extended": "^4.0.0", "postcss-load-config": "^6.0.1", "prisma": "^6.19.2", "tailwindcss": "^4", + "ts-jest": "^29.2.5", "typescript": "^5" } }, @@ -206,6 +212,16 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -266,6 +282,245 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/runtime": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", @@ -323,6 +578,13 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, "node_modules/@emnapi/core": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", @@ -1083,112 +1345,521 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "sprintf-js": "~1.0.2" } }, - "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==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@next/env": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.0.tgz", - "integrity": "sha512-Dd23XQeFHmhf3KBW76leYVkejHlCdB7erakC2At2apL1N08Bm+dLYNP+nNHh0tzUXfPQcNcXiQyacw0PG4Fcpw==", - "license": "MIT" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.0.tgz", - "integrity": "sha512-sooC/k0LCF4/jLXYHpgfzJot04lZQqsttn8XJpTguP8N3GhqXN3wSkh68no2OcZzS/qeGwKDFTqhZ8WofdXmmQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", "dependencies": { - "fast-glob": "3.3.1" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.0.tgz", - "integrity": "sha512-onHq8dl8KjDb8taANQdzs3XmIqQWV3fYdslkGENuvVInFQzZnuBYYOG2HGHqqtvgmEU7xWzhgndXXxnhk4Z3fQ==", - "cpu": [ - "arm64" - ], + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">= 10" + "node": ">=8" } }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.0.tgz", - "integrity": "sha512-Am6VJTp8KhLuAH13tPrAoVIXzuComlZlMwGr++o2KDjWiKPe3VwpxYhgV6I4gKls2EnsIMggL4y7GdXyDdJcFA==", - "cpu": [ - "x64" - ], + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.0.tgz", + "integrity": "sha512-Dd23XQeFHmhf3KBW76leYVkejHlCdB7erakC2At2apL1N08Bm+dLYNP+nNHh0tzUXfPQcNcXiQyacw0PG4Fcpw==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.0.tgz", + "integrity": "sha512-sooC/k0LCF4/jLXYHpgfzJot04lZQqsttn8XJpTguP8N3GhqXN3wSkh68no2OcZzS/qeGwKDFTqhZ8WofdXmmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.0.tgz", + "integrity": "sha512-onHq8dl8KjDb8taANQdzs3XmIqQWV3fYdslkGENuvVInFQzZnuBYYOG2HGHqqtvgmEU7xWzhgndXXxnhk4Z3fQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.0.tgz", + "integrity": "sha512-Am6VJTp8KhLuAH13tPrAoVIXzuComlZlMwGr++o2KDjWiKPe3VwpxYhgV6I4gKls2EnsIMggL4y7GdXyDdJcFA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" ], "engines": { "node": ">= 10" @@ -2527,6 +3198,33 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, "node_modules/@stablelib/base64": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", @@ -3409,21 +4107,114 @@ "tslib": "^2.4.0" } }, - "node_modules/@types/date-arithmetic": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@types/date-arithmetic/-/date-arithmetic-4.1.4.tgz", - "integrity": "sha512-p9eZ2X9B80iKiTW4ukVj8B4K6q9/+xFtQ5MGYA5HWToY9nL4EkhV9+6ftT2VHpVMEZb5Tv00Iel516bVdO+yRw==", + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/date-arithmetic": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/date-arithmetic/-/date-arithmetic-4.1.4.tgz", + "integrity": "sha512-p9eZ2X9B80iKiTW4ukVj8B4K6q9/+xFtQ5MGYA5HWToY9nL4EkhV9+6ftT2VHpVMEZb5Tv00Iel516bVdO+yRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", @@ -3524,12 +4315,26 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/warning": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", @@ -3545,6 +4350,23 @@ "@types/node": "*" } }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.50.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.0.tgz", @@ -4146,6 +4968,32 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -4162,6 +5010,20 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "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==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4412,6 +5274,122 @@ "node": ">= 0.4" } }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4515,12 +5493,42 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^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==", "license": "BSD-3-Clause" }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, "node_modules/c12": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", @@ -4621,6 +5629,16 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001761", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", @@ -4658,6 +5676,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -4674,6 +5702,22 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/citty": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", @@ -4684,6 +5728,13 @@ "consola": "^3.2.3" } }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -4702,6 +5753,21 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -4711,6 +5777,24 @@ "node": ">=6" } }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4775,6 +5859,28 @@ "url": "https://opencollective.com/express" } }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/crelt": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", @@ -4902,6 +6008,21 @@ } } }, + "node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4997,12 +6118,32 @@ "node": ">=8" } }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "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==", "license": "MIT" }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -5134,6 +6275,19 @@ "dev": true, "license": "ISC" }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -5186,6 +6340,16 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", @@ -5760,6 +6924,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -5806,6 +6984,56 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/exsolve": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", @@ -5924,6 +7152,16 @@ "reusify": "^1.0.4" } }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -6004,6 +7242,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "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, + "license": "MIT", + "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", @@ -6092,6 +7352,16 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -6125,6 +7395,16 @@ "node": ">=6" } }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -6138,6 +7418,19 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", @@ -6187,6 +7480,28 @@ "giget": "dist/cli.mjs" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -6361,6 +7676,28 @@ "node": ">=12.0.0" } }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -6479,6 +7816,13 @@ "hermes-estree": "0.25.1" } }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/html-to-text": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", @@ -6527,6 +7871,16 @@ "node": ">= 6" } }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, "node_modules/iceberg-js": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", @@ -6579,6 +7933,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6589,6 +7963,25 @@ "node": ">=0.8.19" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -6631,6 +8024,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -6812,6 +8212,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/is-generator-function": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", @@ -7061,42 +8481,747 @@ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "license": "MIT", "dependencies": { - "is-docker": "^2.0.0" + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock-extended": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jest-mock-extended/-/jest-mock-extended-4.0.0.tgz", + "integrity": "sha512-7BZpfuvLam+/HC+NxifIi9b+5VXj/utUDMPUqrDJehGWVuXPtLS9Jqlob2mJLrI/pg2k1S8DMfKDvEB88QNjaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ts-essentials": "^10.0.2" + }, + "peerDependencies": { + "@jest/globals": "^28.0.0 || ^29.0.0 || ^30.0.0", + "jest": "^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 || ^30.0.0", + "typescript": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/jiti": { @@ -7157,6 +9282,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -7231,6 +9363,16 @@ "json-buffer": "3.0.1" } }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -7260,6 +9402,16 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -7584,6 +9736,13 @@ "url": "https://github.com/sponsors/antonk52" } }, + "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, + "license": "MIT" + }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -7627,6 +9786,13 @@ "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", "license": "MIT" }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -7702,6 +9868,52 @@ "tlds": "1.261.0" } }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, "node_modules/markdown-it": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", @@ -7752,6 +9964,13 @@ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", "license": "MIT" }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -7776,6 +9995,16 @@ "node": ">=8.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -7867,6 +10096,13 @@ "dev": true, "license": "MIT" }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/next": { "version": "16.1.0", "resolved": "https://registry.npmjs.org/next/-/next-16.1.0.tgz", @@ -7984,6 +10220,13 @@ "node": ">= 6.13.0" } }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -8000,6 +10243,29 @@ "node": ">=6.0.0" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/nypm": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", @@ -8148,6 +10414,32 @@ "dev": true, "license": "MIT" }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", @@ -8238,6 +10530,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8251,6 +10553,25 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parseley": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", @@ -8274,6 +10595,16 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -8422,6 +10753,85 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/pkg-types": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", @@ -8580,6 +10990,41 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/prisma": { "version": "6.19.2", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.2.tgz", @@ -8615,6 +11060,20 @@ "node": ">=6" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -9163,6 +11622,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resend": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/resend/-/resend-6.9.1.tgz", @@ -9205,6 +11674,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -9225,6 +11717,16 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -9589,6 +12091,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -9598,6 +12134,17 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -9607,6 +12154,13 @@ "node": ">= 10.x" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", @@ -9614,6 +12168,29 @@ "dev": true, "license": "MIT" }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/standardwebhooks": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", @@ -9638,6 +12215,42 @@ "node": ">= 0.4" } }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/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, + "license": "MIT" + }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", @@ -9751,6 +12364,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -9761,6 +12387,16 @@ "node": ">=4" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -9885,6 +12521,21 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tinyexec": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", @@ -9952,6 +12603,13 @@ "tlds": "bin.js" } }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -9984,6 +12642,100 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-essentials": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-10.1.1.tgz", + "integrity": "sha512-4aTB7KLHKmUvkjNj8V+EdnmuVTiECzn3K+zIbRthumvHu+j44x3w63xpfs0JL3NGIzGXqoQ7AV591xHO+XrOTw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ts-jest": { + "version": "29.4.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", + "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -10029,6 +12781,29 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -10151,6 +12926,20 @@ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", "license": "MIT" }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -10338,12 +13127,37 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "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/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", @@ -10484,6 +13298,52 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", @@ -10514,6 +13374,16 @@ "node": ">=0.4" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -10521,6 +13391,35 @@ "dev": true, "license": "ISC" }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 4f7a1ac..900f793 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,10 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "eslint" + "lint": "eslint", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage" }, "dependencies": { "@google-cloud/local-auth": "^2.1.0", @@ -50,16 +53,22 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", + "@types/jest": "^29.5.14", "@types/node": "^20.19.27", "@types/pg": "^8.16.0", "@types/react": "^19", "@types/react-big-calendar": "^1.16.3", "@types/react-dom": "^19", + "@types/uuid": "^10.0.0", "eslint": "^9", "eslint-config-next": "16.1.0", + "jest": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-mock-extended": "^4.0.0", "postcss-load-config": "^6.0.1", "prisma": "^6.19.2", "tailwindcss": "^4", + "ts-jest": "^29.2.5", "typescript": "^5" } } diff --git a/utils/prisma/__mocks__/prisma.ts b/utils/prisma/__mocks__/prisma.ts new file mode 100644 index 0000000..cbf58f8 --- /dev/null +++ b/utils/prisma/__mocks__/prisma.ts @@ -0,0 +1,5 @@ +import { mockDeep } from "jest-mock-extended"; +import { PrismaClient } from "@prisma/client"; + +const prisma = mockDeep(); +export default prisma; From 210b9d31aaa1c412422895acb0c888e5eca75d5b Mon Sep 17 00:00:00 2001 From: PC Date: Sun, 8 Feb 2026 18:56:28 +0800 Subject: [PATCH 3/6] chore: setup ci/cd and sentry --- .github/workflows/ci.yml | 34 + .gitignore | 3 + app/api/sentry-example-api/route.ts | 17 + app/global-error.tsx | 27 + app/sentry-example-page/page.tsx | 237 ++ instrumentation-client.ts | 31 + instrumentation.ts | 13 + next.config.ts | 39 +- package-lock.json | 3825 ++++++++++++++++++++------- package.json | 1 + sentry.client.config.ts | 27 + sentry.edge.config.ts | 20 + sentry.server.config.ts | 19 + 13 files changed, 3352 insertions(+), 941 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 app/api/sentry-example-api/route.ts create mode 100644 app/global-error.tsx create mode 100644 app/sentry-example-page/page.tsx create mode 100644 instrumentation-client.ts create mode 100644 instrumentation.ts create mode 100644 sentry.client.config.ts create mode 100644 sentry.edge.config.ts create mode 100644 sentry.server.config.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..55e8f6e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x] + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Run Linting + run: npm run lint + + - name: Run Tests + run: npm test diff --git a/.gitignore b/.gitignore index 0f4a1ec..931f27a 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ yarn-error.log* next-env.d.ts /generated/prisma + +# Sentry Config File +.env.sentry-build-plugin diff --git a/app/api/sentry-example-api/route.ts b/app/api/sentry-example-api/route.ts new file mode 100644 index 0000000..aa23929 --- /dev/null +++ b/app/api/sentry-example-api/route.ts @@ -0,0 +1,17 @@ +import * as Sentry from "@sentry/nextjs"; +export const dynamic = "force-dynamic"; + +class SentryExampleAPIError extends Error { + constructor(message: string | undefined) { + super(message); + this.name = "SentryExampleAPIError"; + } +} + +// A faulty API route to test Sentry's error monitoring +export function GET() { + Sentry.logger.info("Sentry example API called"); + throw new SentryExampleAPIError( + "This error is raised on the backend called by the example page.", + ); +} diff --git a/app/global-error.tsx b/app/global-error.tsx new file mode 100644 index 0000000..4f9c8a9 --- /dev/null +++ b/app/global-error.tsx @@ -0,0 +1,27 @@ +"use client"; + +import * as Sentry from "@sentry/nextjs"; +import NextError from "next/error"; +import { useEffect } from "react"; + +export default function GlobalError({ + error, +}: { + error: Error & { digest?: string }; +}) { + useEffect(() => { + Sentry.captureException(error); + }, [error]); + + return ( + + + {/* `NextError` is the default Next.js error page component. Its type + definition requires a `statusCode` prop. However, since the App Router + does not expose status codes for errors, we simply pass 0 to render a + generic error message. */} + + + + ); +} diff --git a/app/sentry-example-page/page.tsx b/app/sentry-example-page/page.tsx new file mode 100644 index 0000000..a0410aa --- /dev/null +++ b/app/sentry-example-page/page.tsx @@ -0,0 +1,237 @@ +"use client"; + +import * as Sentry from "@sentry/nextjs"; +import Head from "next/head"; +import { useEffect, useState } from "react"; + +class SentryExampleFrontendError extends Error { + constructor(message: string | undefined) { + super(message); + this.name = "SentryExampleFrontendError"; + } +} + +export default function Page() { + const [hasSentError, setHasSentError] = useState(false); + const [isConnected, setIsConnected] = useState(true); + + useEffect(() => { + Sentry.logger.info("Sentry example page loaded"); + async function checkConnectivity() { + const result = await Sentry.diagnoseSdkConnectivity(); + setIsConnected(result !== "sentry-unreachable"); + } + checkConnectivity(); + }, []); + + return ( +
+ + sentry-example-page + + + +
+
+ + + +

sentry-example-page

+ +

+ Click the button below, and view the sample error on the Sentry{" "} + + Issues Page + + . For more details about setting up Sentry,{" "} + + read our docs + + . +

+ + + + {hasSentError ? ( +

Error sent to Sentry.

+ ) : !isConnected ? ( +
+

+ It looks like network requests to Sentry are being blocked, which + will prevent errors from being captured. Try disabling your + ad-blocker to complete the test. +

+
+ ) : ( +
+ )} + +
+
+ + +
+ ); +} diff --git a/instrumentation-client.ts b/instrumentation-client.ts new file mode 100644 index 0000000..2b0889b --- /dev/null +++ b/instrumentation-client.ts @@ -0,0 +1,31 @@ +// This file configures the initialization of Sentry on the client. +// The added config here will be used whenever a users loads a page in their browser. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +Sentry.init({ + dsn: "https://8389f17c4ece4d169ecc4660a26a7a18@o4510849772486656.ingest.us.sentry.io/4510849774518272", + + // Add optional integrations for additional features + integrations: [Sentry.replayIntegration()], + + // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. + tracesSampleRate: 1, + // Enable logs to be sent to Sentry + enableLogs: true, + + // Define how likely Replay events are sampled. + // This sets the sample rate to be 10%. You may want this to be 100% while + // in development and sample at a lower rate in production + replaysSessionSampleRate: 0.1, + + // Define how likely Replay events are sampled when an error occurs. + replaysOnErrorSampleRate: 1.0, + + // Enable sending user PII (Personally Identifiable Information) + // https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#sendDefaultPii + sendDefaultPii: true, +}); + +export const onRouterTransitionStart = Sentry.captureRouterTransitionStart; diff --git a/instrumentation.ts b/instrumentation.ts new file mode 100644 index 0000000..7cbe93c --- /dev/null +++ b/instrumentation.ts @@ -0,0 +1,13 @@ +import * as Sentry from "@sentry/nextjs"; + +export async function register() { + if (process.env.NEXT_RUNTIME === "nodejs") { + await import("./sentry.server.config"); + } + + if (process.env.NEXT_RUNTIME === "edge") { + await import("./sentry.edge.config"); + } +} + +export const onRequestError = Sentry.captureRequestError; diff --git a/next.config.ts b/next.config.ts index 50dcac7..0d5628b 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,3 +1,4 @@ +import { withSentryConfig } from "@sentry/nextjs"; import type { NextConfig } from "next"; const nextConfig: NextConfig = { @@ -12,4 +13,40 @@ const nextConfig: NextConfig = { }, }; -export default nextConfig; +export default withSentryConfig(nextConfig, { + // For all available options, see: + // https://www.npmjs.com/package/@sentry/webpack-plugin#options + + org: "jacob-5i", + + project: "javascript-nextjs", + + // Only print logs for uploading source maps in CI + silent: !process.env.CI, + + // For all available options, see: + // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ + + // Upload a larger set of source maps for prettier stack traces (increases build time) + widenClientFileUpload: true, + + // Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. + // This can increase your server load as well as your hosting bill. + // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- + // side errors will fail. + // tunnelRoute: "/monitoring", + + webpack: { + // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.) + // See the following for more information: + // https://docs.sentry.io/product/crons/ + // https://vercel.com/docs/cron-jobs + automaticVercelMonitors: true, + + // Tree-shaking options for reducing bundle size + treeshake: { + // Automatically tree-shake Sentry logger statements to reduce bundle size + removeDebugLogging: true, + }, + }, +}); diff --git a/package-lock.json b/package-lock.json index f35b86e..d44613a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@react-email/components": "^1.0.7", "@react-email/render": "^2.0.4", "@react-oauth/google": "^0.13.0", + "@sentry/nextjs": "^10.38.0", "@supabase/ssr": "^0.8.0", "@supabase/supabase-js": "^2.89.0", "@tanstack/react-table": "^8.21.3", @@ -80,11 +81,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@apm-js-collab/code-transformer": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.8.2.tgz", + "integrity": "sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==", + "license": "Apache-2.0" + }, + "node_modules/@apm-js-collab/tracing-hooks": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.3.1.tgz", + "integrity": "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==", + "license": "Apache-2.0", + "dependencies": { + "@apm-js-collab/code-transformer": "^0.8.0", + "debug": "^4.4.1", + "module-details-from-path": "^1.0.4" + } + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", @@ -99,7 +116,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -109,7 +125,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", @@ -140,7 +155,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", @@ -157,7 +171,6 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.27.2", @@ -174,7 +187,6 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -184,7 +196,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", @@ -198,7 +209,6 @@ "version": "7.28.3", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", @@ -226,7 +236,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -236,7 +245,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -246,7 +254,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -256,7 +263,6 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", - "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", @@ -270,7 +276,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.28.5" @@ -534,7 +539,6 @@ "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", @@ -549,7 +553,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", @@ -568,7 +571,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -1345,6 +1347,96 @@ "url": "https://opencollective.com/libvips" } }, + "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==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1758,7 +1850,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1769,7 +1860,6 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1780,7 +1870,6 @@ "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, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1790,14 +1879,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2009,694 +2096,731 @@ "node": ">=12.4.0" } }, - "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/@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/@prisma/adapter-pg": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-6.19.2.tgz", - "integrity": "sha512-t80uX4u4+V/F/OO220LVFY2nsXRGvt8x7PDDveQJJUrGAq4yKHiHiwxOb/PusUSioKkJrgLZjzLwGrpPYG4arw==", + "node_modules/@opentelemetry/api-logs": { + "version": "0.211.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", + "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", "license": "Apache-2.0", "dependencies": { - "@prisma/driver-adapter-utils": "6.19.2", - "pg": "^8.11.3", - "postgres-array": "3.0.4" + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@prisma/adapter-pg/node_modules/postgres-array": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", - "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", - "license": "MIT", + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.5.0.tgz", + "integrity": "sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw==", + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@prisma/client": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.2.tgz", - "integrity": "sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==", - "hasInstallScript": true, + "node_modules/@opentelemetry/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz", + "integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==", "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, "engines": { - "node": ">=18.18" + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "prisma": "*", - "typescript": ">=5.1.0" - }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - }, - "typescript": { - "optional": true - } + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@prisma/config": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.2.tgz", - "integrity": "sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==", - "dev": true, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.211.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", + "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", "license": "Apache-2.0", "dependencies": { - "c12": "3.1.0", - "deepmerge-ts": "7.1.5", - "effect": "3.18.4", - "empathic": "2.0.0" + "@opentelemetry/api-logs": "0.211.0", + "import-in-the-middle": "^2.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@prisma/debug": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.2.tgz", - "integrity": "sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==", - "license": "Apache-2.0" - }, - "node_modules/@prisma/driver-adapter-utils": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-6.19.2.tgz", - "integrity": "sha512-tkHsL3jhx81eXg2oqtJH/1IEs8uEeUb1RpqHtwYqdNb176u9D0mnHRZM1/cKca/XhLpq49Nnd9XDxdMfWcKAYA==", + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.58.0.tgz", + "integrity": "sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==", "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.19.2" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@prisma/engines": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.2.tgz", - "integrity": "sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==", - "dev": true, - "hasInstallScript": true, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.54.0.tgz", + "integrity": "sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==", "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.19.2", - "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "@prisma/fetch-engine": "6.19.2", - "@prisma/get-platform": "6.19.2" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/connect": "3.4.38" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@prisma/engines-version": { - "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", - "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@prisma/fetch-engine": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.2.tgz", - "integrity": "sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==", - "dev": true, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.28.0.tgz", + "integrity": "sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==", "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.19.2", - "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "@prisma/get-platform": "6.19.2" + "@opentelemetry/instrumentation": "^0.211.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@prisma/get-platform": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.2.tgz", - "integrity": "sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==", - "dev": true, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.59.0.tgz", + "integrity": "sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==", "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.19.2" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", - "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", - "license": "MIT", + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.30.0.tgz", + "integrity": "sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dialog": "1.1.15", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-alert-dialog/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", + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.54.0.tgz", + "integrity": "sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@opentelemetry/instrumentation": "^0.211.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "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", + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.58.0.tgz", + "integrity": "sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-primitive": "2.1.3" + "@opentelemetry/instrumentation": "^0.211.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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "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", + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.57.0.tgz", + "integrity": "sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==", + "license": "Apache-2.0", "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" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.27.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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-collection/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", + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.211.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.211.0.tgz", + "integrity": "sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@opentelemetry/core": "2.5.0", + "@opentelemetry/instrumentation": "0.211.0", + "@opentelemetry/semantic-conventions": "^1.29.0", + "forwarded-parse": "2.1.2" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "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/@opentelemetry/instrumentation-ioredis": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.59.0.tgz", + "integrity": "sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/redis-common": "^0.38.2", + "@opentelemetry/semantic-conventions": "^1.33.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "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" + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.20.0.tgz", + "integrity": "sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.30.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", - "license": "MIT", + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.55.0.tgz", + "integrity": "sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.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": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-dialog/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", + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.59.0.tgz", + "integrity": "sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.36.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" } }, - "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" + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.55.0.tgz", + "integrity": "sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.211.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", - "license": "MIT", + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.64.0.tgz", + "integrity": "sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@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" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", - "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", - "license": "MIT", + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.57.0.tgz", + "integrity": "sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@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.16", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.57.0.tgz", + "integrity": "sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/mysql": "2.15.27" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "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", + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.57.0.tgz", + "integrity": "sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==", + "license": "Apache-2.0", "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" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@opentelemetry/sql-common": "^0.41.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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "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/@opentelemetry/instrumentation-pg": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.63.0.tgz", + "integrity": "sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/sql-common": "^0.41.2", + "@types/pg": "8.15.6", + "@types/pg-pool": "2.0.7" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", - "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "node_modules/@opentelemetry/instrumentation-pg/node_modules/@types/pg": { + "version": "8.15.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", + "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@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.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@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 - }, - "@types/react-dom": { - "optional": true - } + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" } }, - "node_modules/@radix-ui/react-menu/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", + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.59.0.tgz", + "integrity": "sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/redis-common": "^0.38.2", + "@opentelemetry/semantic-conventions": "^1.27.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", - "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", - "license": "MIT", + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.30.0.tgz", + "integrity": "sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==", + "license": "Apache-2.0", "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" + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/tedious": "^4.0.14" }, - "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": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "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", + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.21.0.tgz", + "integrity": "sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/semantic-conventions": "^1.24.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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" } }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", + "node_modules/@opentelemetry/redis-common": { + "version": "0.38.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.2.tgz", + "integrity": "sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.5.0.tgz", + "integrity": "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@opentelemetry/core": "2.5.0", + "@opentelemetry/semantic-conventions": "^1.29.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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "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", + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.0.tgz", + "integrity": "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-slot": "1.2.3" + "@opentelemetry/core": "2.5.0", + "@opentelemetry/resources": "2.5.0", + "@opentelemetry/semantic-conventions": "^1.29.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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@radix-ui/react-primitive/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", + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz", + "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.2.tgz", + "integrity": "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@opentelemetry/core": "^2.0.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" } }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", - "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "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==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "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/@prisma/adapter-pg": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-6.19.2.tgz", + "integrity": "sha512-t80uX4u4+V/F/OO220LVFY2nsXRGvt8x7PDDveQJJUrGAq4yKHiHiwxOb/PusUSioKkJrgLZjzLwGrpPYG4arw==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@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" + "@prisma/driver-adapter-utils": "6.19.2", + "pg": "^8.11.3", + "postgres-array": "3.0.4" + } + }, + "node_modules/@prisma/adapter-pg/node_modules/postgres-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", + "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@prisma/client": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.2.tgz", + "integrity": "sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" }, "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" + "prisma": "*", + "typescript": ">=5.1.0" }, "peerDependenciesMeta": { - "@types/react": { + "prisma": { "optional": true }, - "@types/react-dom": { + "typescript": { "optional": true } } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { + "node_modules/@prisma/config": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.2.tgz", + "integrity": "sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.2.tgz", + "integrity": "sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/driver-adapter-utils": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-6.19.2.tgz", + "integrity": "sha512-tkHsL3jhx81eXg2oqtJH/1IEs8uEeUb1RpqHtwYqdNb176u9D0mnHRZM1/cKca/XhLpq49Nnd9XDxdMfWcKAYA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2" + } + }, + "node_modules/@prisma/engines": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.2.tgz", + "integrity": "sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/fetch-engine": "6.19.2", + "@prisma/get-platform": "6.19.2" + } + }, + "node_modules/@prisma/engines-version": { + "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", + "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.2.tgz", + "integrity": "sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/get-platform": "6.19.2" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.2.tgz", + "integrity": "sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2" + } + }, + "node_modules/@prisma/instrumentation": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-7.2.0.tgz", + "integrity": "sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.207.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.8" + } + }, + "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/api-logs": { + "version": "0.207.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.207.0.tgz", + "integrity": "sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/instrumentation": { + "version": "0.207.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.207.0.tgz", + "integrity": "sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.207.0", + "import-in-the-middle": "^2.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", + "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.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-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==", + "node_modules/@radix-ui/react-alert-dialog/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" @@ -2707,50 +2831,62 @@ } } }, - "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==", + "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-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/react-primitive": "2.1.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-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-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-use-layout-effect": "1.1.1" + "@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": "*", - "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-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==", + "node_modules/@radix-ui/react-collection/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-use-callback-ref": "1.1.1" + "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -2762,10 +2898,10 @@ } } }, - "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==", + "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": "*", @@ -2777,13 +2913,64 @@ } } }, - "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==", + "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 + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", "license": "MIT", "dependencies": { - "@radix-ui/rect": "1.1.1" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "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/@radix-ui/react-dialog/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": "*", @@ -2795,407 +2982,1788 @@ } } }, - "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==", + "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-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@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 + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@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.16", + "@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 + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "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-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 + }, + "@types/react-dom": { + "optional": true + } + } + }, + "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-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-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@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.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@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 + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/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/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "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 + }, + "@types/react-dom": { + "optional": true + } + } + }, + "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 + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "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 + }, + "@types/react-dom": { + "optional": true + } + } + }, + "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 + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/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/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@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 + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "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/@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-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" + }, + "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-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/@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-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": { + "@radix-ui/rect": "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-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/@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/@react-email/body": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.2.1.tgz", + "integrity": "sha512-ljDiQiJDu/Fq//vSIIP0z5Nuvt4+DX1RqGasstChDGJB/14ogd4VdNS9aacoede/ZjGy3o3Qb+cxyS+XgM6SwQ==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/button": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.2.1.tgz", + "integrity": "sha512-qXyj7RZLE7POy9BMKSoqQ00tOXThjOZSUnI2Yu9i29IHngPlmrNayIWBoVKtElES7OWwypUcpiajwi1mUWx6/A==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/code-block": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.2.1.tgz", + "integrity": "sha512-M3B7JpVH4ytgn83/ujRR1k1DQHvTeABiDM61OvAbjLRPhC/5KLHU5KkzIbbuGIrjWwxAbL1kSQzU8MhLEtSxyw==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/code-inline": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.6.tgz", + "integrity": "sha512-jfhebvv3dVsp3OdPgKXnk8+e2pBiDVZejDOBFzBa/IblrAJ9cQDkN6rBD5IyEg8hTOxwbw3iaI/yZFmDmIguIA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/column": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.14.tgz", + "integrity": "sha512-f+W+Bk2AjNO77zynE33rHuQhyqVICx4RYtGX9NKsGUg0wWjdGP0qAuIkhx9Rnmk4/hFMo1fUrtYNqca9fwJdHg==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/components": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-1.0.7.tgz", + "integrity": "sha512-mY+v4C1SMaGOKuKp0QWDQLGK+3fvH06ZE10EVavv+T6tQneDHq9cpQ9NdCrvuO1nWZnWrA/0tRpvyqyF0uo93w==", + "license": "MIT", + "dependencies": { + "@react-email/body": "0.2.1", + "@react-email/button": "0.2.1", + "@react-email/code-block": "0.2.1", + "@react-email/code-inline": "0.0.6", + "@react-email/column": "0.0.14", + "@react-email/container": "0.0.16", + "@react-email/font": "0.0.10", + "@react-email/head": "0.0.13", + "@react-email/heading": "0.0.16", + "@react-email/hr": "0.0.12", + "@react-email/html": "0.0.12", + "@react-email/img": "0.0.12", + "@react-email/link": "0.0.13", + "@react-email/markdown": "0.0.18", + "@react-email/preview": "0.0.14", + "@react-email/render": "2.0.4", + "@react-email/row": "0.0.13", + "@react-email/section": "0.0.17", + "@react-email/tailwind": "2.0.4", + "@react-email/text": "0.1.6" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/container": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.16.tgz", + "integrity": "sha512-QWBB56RkkU0AJ9h+qy33gfT5iuZknPC7Un/IjZv9B0QmMIK+WWacc0cH6y2SV5Cv/b99hU94fjEMOOO4enpkbQ==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/font": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.10.tgz", + "integrity": "sha512-0urVSgCmQIfx5r7Xc586miBnQUVnGp3OTYUm8m5pwtQRdTRO5XrTtEfNJ3JhYhSOruV0nD8fd+dXtKXobum6tA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/head": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.13.tgz", + "integrity": "sha512-AJg6le/08Gz4tm+6MtKXqtNNyKHzmooOCdmtqmWxD7FxoAdU1eVcizhtQ0gcnVaY6ethEyE/hnEzQxt1zu5Kog==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/heading": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.16.tgz", + "integrity": "sha512-jmsKnQm1ykpBzw4hCYHwBkt5pW2jScXffPeEH5ZRF5tZeF5b1pvlFTO9han7C0pCkZYo1kEvWiRtx69yfCIwuw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/hr": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.12.tgz", + "integrity": "sha512-TwmOmBDibavUQpXBxpmZYi2Iks/yeZOzFYh+di9EltMSnEabH8dMZXrl+pxNXzCgZ2XE8HY7VmUL65Lenfu5PA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/html": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.12.tgz", + "integrity": "sha512-KTShZesan+UsreU7PDUV90afrZwU5TLwYlALuCSU0OT+/U8lULNNbAUekg+tGwCnOfIKYtpDPKkAMRdYlqUznw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/img": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.12.tgz", + "integrity": "sha512-sRCpEARNVTf3FQhZOC+JTvu5r6ubiYWkT0ucYXg8ctkyi4G8QG+jgYPiNUqVeTLA2STOfmPM/nrk1nb84y6CPQ==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/link": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.13.tgz", + "integrity": "sha512-lkWc/NjOcefRZMkQoSDDbuKBEBDES9aXnFEOuPH845wD3TxPwh+QTf0fStuzjoRLUZWpHnio4z7qGGRYusn/sw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/markdown": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.18.tgz", + "integrity": "sha512-gSuYK5fsMbGk87jDebqQ6fa2fKcWlkf2Dkva8kMONqLgGCq8/0d+ZQYMEJsdidIeBo3kmsnHZPrwdFB4HgjUXg==", + "license": "MIT", + "dependencies": { + "marked": "^15.0.12" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/preview": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.14.tgz", + "integrity": "sha512-aYK8q0IPkBXyMsbpMXgxazwHxYJxTrXrV95GFuu2HbEiIToMwSyUgb8HDFYwPqqfV03/jbwqlsXmFxsOd+VNaw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/render": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@react-email/render/-/render-2.0.4.tgz", + "integrity": "sha512-kht2oTFQ1SwrLpd882ahTvUtNa9s53CERHstiTbzhm6aR2Hbykp/mQ4tpPvsBGkKAEvKRlDEoooh60Uk6nHK1g==", + "license": "MIT", + "dependencies": { + "html-to-text": "^9.0.5", + "prettier": "^3.5.3" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/row": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.13.tgz", + "integrity": "sha512-bYnOac40vIKCId7IkwuLAAsa3fKfSfqCvv6epJKmPE0JBuu5qI4FHFCl9o9dVpIIS08s/ub+Y/txoMt0dYziGw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/section": { + "version": "0.0.17", + "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.17.tgz", + "integrity": "sha512-qNl65ye3W0Rd5udhdORzTV9ezjb+GFqQQSae03NDzXtmJq6sqVXNWNiVolAjvJNypim+zGXmv6J9TcV5aNtE/w==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-email/tailwind": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-2.0.4.tgz", + "integrity": "sha512-cDp8Ss6LJKI8zBLKE+tsXFurn6I2nnQNg1qqjfZuNPNoToN1Uyx3egW0bwSVk1JjrNWx/Xnme7ZxvNLRrU9K0Q==", + "license": "MIT", + "dependencies": { + "tailwindcss": "^4.1.18" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@react-email/body": "0.2.1", + "@react-email/button": "0.2.1", + "@react-email/code-block": "0.2.1", + "@react-email/code-inline": "0.0.6", + "@react-email/container": "0.0.16", + "@react-email/heading": "0.0.16", + "@react-email/hr": "0.0.12", + "@react-email/img": "0.0.12", + "@react-email/link": "0.0.13", + "@react-email/preview": "0.0.14", + "@react-email/text": "0.1.6", + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@react-email/body": { + "optional": true + }, + "@react-email/button": { + "optional": true + }, + "@react-email/code-block": { + "optional": true + }, + "@react-email/code-inline": { + "optional": true + }, + "@react-email/container": { + "optional": true + }, + "@react-email/heading": { + "optional": true + }, + "@react-email/hr": { + "optional": true + }, + "@react-email/img": { + "optional": true + }, + "@react-email/link": { + "optional": true + }, + "@react-email/preview": { + "optional": true + } + } + }, + "node_modules/@react-email/text": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.6.tgz", + "integrity": "sha512-TYqkioRS45wTR5il3dYk/SbUjjEdhSwh9BtRNB99qNH1pXAwA45H7rAuxehiu8iJQJH0IyIr+6n62gBz9ezmsw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || ^19.0.0-rc" + } + }, + "node_modules/@react-oauth/google": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.13.0.tgz", + "integrity": "sha512-ukoKALR0VX9TmEGVHTDcRiCtJuNp+Zm8zxR+hm5KN+yON/lGHVzFlUIW1go0L8qHQZobbVZbDv40Eg+MVrWtnQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "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/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "28.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz", + "integrity": "sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "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==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "selderee": "^0.11.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/@sentry-internal/browser-utils": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.38.0.tgz", + "integrity": "sha512-UOJtYmdcxHCcV0NPfXFff/a95iXl/E0EhuQ1y0uE0BuZDMupWSF5t2BgC4HaE5Aw3RTjDF3XkSHWoIF6ohy7eA==", + "license": "MIT", + "dependencies": { + "@sentry/core": "10.38.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/feedback": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.38.0.tgz", + "integrity": "sha512-JXneg9zRftyfy1Fyfc39bBlF/Qd8g4UDublFFkVvdc1S6JQPlK+P6q22DKz3Pc8w3ySby+xlIq/eTu9Pzqi4KA==", + "license": "MIT", + "dependencies": { + "@sentry/core": "10.38.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/replay": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.38.0.tgz", + "integrity": "sha512-YWIkL6/dnaiQyFiZXJ/nN+NXGv/15z45ia86bE/TMq01CubX/DUOilgsFz0pk2v/pg3tp/U2MskLO9Hz0cnqeg==", + "license": "MIT", + "dependencies": { + "@sentry-internal/browser-utils": "10.38.0", + "@sentry/core": "10.38.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry-internal/replay-canvas": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.38.0.tgz", + "integrity": "sha512-OXWM9jEqNYh4VTvrMu7v+z1anz+QKQ/fZXIZdsO7JTT2lGNZe58UUMeoq386M+Saxen8F9SUH7yTORy/8KI5qw==", "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" + "@sentry-internal/replay": "10.38.0", + "@sentry/core": "10.38.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=18" } }, - "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/@react-email/body": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@react-email/body/-/body-0.2.1.tgz", - "integrity": "sha512-ljDiQiJDu/Fq//vSIIP0z5Nuvt4+DX1RqGasstChDGJB/14ogd4VdNS9aacoede/ZjGy3o3Qb+cxyS+XgM6SwQ==", + "node_modules/@sentry/babel-plugin-component-annotate": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.9.0.tgz", + "integrity": "sha512-TJ7sVoa2Bf36lpJjBAzpNDC5Hg+evjsQnqUPeDx9Nz/YFw0u9rK1cwvi95gVWpx7PJSDCkljIv3aw0m4RatHpQ==", "license": "MIT", "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "node": ">= 14" } }, - "node_modules/@react-email/button": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@react-email/button/-/button-0.2.1.tgz", - "integrity": "sha512-qXyj7RZLE7POy9BMKSoqQ00tOXThjOZSUnI2Yu9i29IHngPlmrNayIWBoVKtElES7OWwypUcpiajwi1mUWx6/A==", + "node_modules/@sentry/browser": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.38.0.tgz", + "integrity": "sha512-3phzp1YX4wcQr9mocGWKbjv0jwtuoDBv7+Y6Yfrys/kwyaL84mDLjjQhRf4gL5SX7JdYkhBp4WaiNlR0UC4kTA==", "license": "MIT", - "engines": { - "node": ">=20.0.0" + "dependencies": { + "@sentry-internal/browser-utils": "10.38.0", + "@sentry-internal/feedback": "10.38.0", + "@sentry-internal/replay": "10.38.0", + "@sentry-internal/replay-canvas": "10.38.0", + "@sentry/core": "10.38.0" }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": ">=18" } }, - "node_modules/@react-email/code-block": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@react-email/code-block/-/code-block-0.2.1.tgz", - "integrity": "sha512-M3B7JpVH4ytgn83/ujRR1k1DQHvTeABiDM61OvAbjLRPhC/5KLHU5KkzIbbuGIrjWwxAbL1kSQzU8MhLEtSxyw==", + "node_modules/@sentry/bundler-plugin-core": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-4.9.0.tgz", + "integrity": "sha512-gOVgHG5BrxCFmZow1XovlDr1FH/gO/LfD8OKci1rryeqHVBLr3+S4yS4ACl+E5lfQPym8Ve1BKh793d1rZ0dyA==", "license": "MIT", "dependencies": { - "prismjs": "^1.30.0" + "@babel/core": "^7.18.5", + "@sentry/babel-plugin-component-annotate": "4.9.0", + "@sentry/cli": "^2.57.0", + "dotenv": "^16.3.1", + "find-up": "^5.0.0", + "glob": "^10.5.0", + "magic-string": "0.30.8", + "unplugin": "1.0.1" }, "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "node": ">= 14" } }, - "node_modules/@react-email/code-inline": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@react-email/code-inline/-/code-inline-0.0.6.tgz", - "integrity": "sha512-jfhebvv3dVsp3OdPgKXnk8+e2pBiDVZejDOBFzBa/IblrAJ9cQDkN6rBD5IyEg8hTOxwbw3iaI/yZFmDmIguIA==", + "node_modules/@sentry/bundler-plugin-core/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@sentry/bundler-plugin-core/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", "engines": { - "node": ">=20.0.0" + "node": ">=12" }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/@react-email/column": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/@react-email/column/-/column-0.0.14.tgz", - "integrity": "sha512-f+W+Bk2AjNO77zynE33rHuQhyqVICx4RYtGX9NKsGUg0wWjdGP0qAuIkhx9Rnmk4/hFMo1fUrtYNqca9fwJdHg==", - "license": "MIT", - "engines": { - "node": ">=20.0.0" + "node_modules/@sentry/bundler-plugin-core/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "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" }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@react-email/components": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-1.0.7.tgz", - "integrity": "sha512-mY+v4C1SMaGOKuKp0QWDQLGK+3fvH06ZE10EVavv+T6tQneDHq9cpQ9NdCrvuO1nWZnWrA/0tRpvyqyF0uo93w==", + "node_modules/@sentry/bundler-plugin-core/node_modules/magic-string": { + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "license": "MIT", "dependencies": { - "@react-email/body": "0.2.1", - "@react-email/button": "0.2.1", - "@react-email/code-block": "0.2.1", - "@react-email/code-inline": "0.0.6", - "@react-email/column": "0.0.14", - "@react-email/container": "0.0.16", - "@react-email/font": "0.0.10", - "@react-email/head": "0.0.13", - "@react-email/heading": "0.0.16", - "@react-email/hr": "0.0.12", - "@react-email/html": "0.0.12", - "@react-email/img": "0.0.12", - "@react-email/link": "0.0.13", - "@react-email/markdown": "0.0.18", - "@react-email/preview": "0.0.14", - "@react-email/render": "2.0.4", - "@react-email/row": "0.0.13", - "@react-email/section": "0.0.17", - "@react-email/tailwind": "2.0.4", - "@react-email/text": "0.1.6" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "node": ">=12" } }, - "node_modules/@react-email/container": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@react-email/container/-/container-0.0.16.tgz", - "integrity": "sha512-QWBB56RkkU0AJ9h+qy33gfT5iuZknPC7Un/IjZv9B0QmMIK+WWacc0cH6y2SV5Cv/b99hU94fjEMOOO4enpkbQ==", - "license": "MIT", + "node_modules/@sentry/bundler-plugin-core/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=20.0.0" + "node": ">=16 || 14 >=14.17" }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@react-email/font": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@react-email/font/-/font-0.0.10.tgz", - "integrity": "sha512-0urVSgCmQIfx5r7Xc586miBnQUVnGp3OTYUm8m5pwtQRdTRO5XrTtEfNJ3JhYhSOruV0nD8fd+dXtKXobum6tA==", - "license": "MIT", + "node_modules/@sentry/cli": { + "version": "2.58.4", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.58.4.tgz", + "integrity": "sha512-ArDrpuS8JtDYEvwGleVE+FgR+qHaOp77IgdGSacz6SZy6Lv90uX0Nu4UrHCQJz8/xwIcNxSqnN22lq0dH4IqTg==", + "hasInstallScript": true, + "license": "FSL-1.1-MIT", + "dependencies": { + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.7", + "progress": "^2.0.3", + "proxy-from-env": "^1.1.0", + "which": "^2.0.2" + }, + "bin": { + "sentry-cli": "bin/sentry-cli" + }, "engines": { - "node": ">=20.0.0" + "node": ">= 10" }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "optionalDependencies": { + "@sentry/cli-darwin": "2.58.4", + "@sentry/cli-linux-arm": "2.58.4", + "@sentry/cli-linux-arm64": "2.58.4", + "@sentry/cli-linux-i686": "2.58.4", + "@sentry/cli-linux-x64": "2.58.4", + "@sentry/cli-win32-arm64": "2.58.4", + "@sentry/cli-win32-i686": "2.58.4", + "@sentry/cli-win32-x64": "2.58.4" + } + }, + "node_modules/@sentry/cli-darwin": { + "version": "2.58.4", + "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.58.4.tgz", + "integrity": "sha512-kbTD+P4X8O+nsNwPxCywtj3q22ecyRHWff98rdcmtRrvwz8CKi/T4Jxn/fnn2i4VEchy08OWBuZAqaA5Kh2hRQ==", + "license": "FSL-1.1-MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" } }, - "node_modules/@react-email/head": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/head/-/head-0.0.13.tgz", - "integrity": "sha512-AJg6le/08Gz4tm+6MtKXqtNNyKHzmooOCdmtqmWxD7FxoAdU1eVcizhtQ0gcnVaY6ethEyE/hnEzQxt1zu5Kog==", - "license": "MIT", + "node_modules/@sentry/cli-linux-arm": { + "version": "2.58.4", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.58.4.tgz", + "integrity": "sha512-rdQ8beTwnN48hv7iV7e7ZKucPec5NJkRdrrycMJMZlzGBPi56LqnclgsHySJ6Kfq506A2MNuQnKGaf/sBC9REA==", + "cpu": [ + "arm" + ], + "license": "FSL-1.1-MIT", + "optional": true, + "os": [ + "linux", + "freebsd", + "android" + ], "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "node": ">=10" } }, - "node_modules/@react-email/heading": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@react-email/heading/-/heading-0.0.16.tgz", - "integrity": "sha512-jmsKnQm1ykpBzw4hCYHwBkt5pW2jScXffPeEH5ZRF5tZeF5b1pvlFTO9han7C0pCkZYo1kEvWiRtx69yfCIwuw==", - "license": "MIT", + "node_modules/@sentry/cli-linux-arm64": { + "version": "2.58.4", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.58.4.tgz", + "integrity": "sha512-0g0KwsOozkLtzN8/0+oMZoOuQ0o7W6O+hx+ydVU1bktaMGKEJLMAWxOQNjsh1TcBbNIXVOKM/I8l0ROhaAb8Ig==", + "cpu": [ + "arm64" + ], + "license": "FSL-1.1-MIT", + "optional": true, + "os": [ + "linux", + "freebsd", + "android" + ], "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "node": ">=10" } }, - "node_modules/@react-email/hr": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/hr/-/hr-0.0.12.tgz", - "integrity": "sha512-TwmOmBDibavUQpXBxpmZYi2Iks/yeZOzFYh+di9EltMSnEabH8dMZXrl+pxNXzCgZ2XE8HY7VmUL65Lenfu5PA==", - "license": "MIT", + "node_modules/@sentry/cli-linux-i686": { + "version": "2.58.4", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.58.4.tgz", + "integrity": "sha512-NseoIQAFtkziHyjZNPTu1Gm1opeQHt7Wm1LbLrGWVIRvUOzlslO9/8i6wETUZ6TjlQxBVRgd3Q0lRBG2A8rFYA==", + "cpu": [ + "x86", + "ia32" + ], + "license": "FSL-1.1-MIT", + "optional": true, + "os": [ + "linux", + "freebsd", + "android" + ], "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "node": ">=10" } }, - "node_modules/@react-email/html": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/html/-/html-0.0.12.tgz", - "integrity": "sha512-KTShZesan+UsreU7PDUV90afrZwU5TLwYlALuCSU0OT+/U8lULNNbAUekg+tGwCnOfIKYtpDPKkAMRdYlqUznw==", - "license": "MIT", + "node_modules/@sentry/cli-linux-x64": { + "version": "2.58.4", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.58.4.tgz", + "integrity": "sha512-d3Arz+OO/wJYTqCYlSN3Ktm+W8rynQ/IMtSZLK8nu0ryh5mJOh+9XlXY6oDXw4YlsM8qCRrNquR8iEI1Y/IH+Q==", + "cpu": [ + "x64" + ], + "license": "FSL-1.1-MIT", + "optional": true, + "os": [ + "linux", + "freebsd", + "android" + ], "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "node": ">=10" } }, - "node_modules/@react-email/img": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@react-email/img/-/img-0.0.12.tgz", - "integrity": "sha512-sRCpEARNVTf3FQhZOC+JTvu5r6ubiYWkT0ucYXg8ctkyi4G8QG+jgYPiNUqVeTLA2STOfmPM/nrk1nb84y6CPQ==", - "license": "MIT", + "node_modules/@sentry/cli-win32-arm64": { + "version": "2.58.4", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.58.4.tgz", + "integrity": "sha512-bqYrF43+jXdDBh0f8HIJU3tbvlOFtGyRjHB8AoRuMQv9TEDUfENZyCelhdjA+KwDKYl48R1Yasb4EHNzsoO83w==", + "cpu": [ + "arm64" + ], + "license": "FSL-1.1-MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-win32-i686": { + "version": "2.58.4", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.58.4.tgz", + "integrity": "sha512-3triFD6jyvhVcXOmGyttf+deKZcC1tURdhnmDUIBkiDPJKGT/N5xa4qAtHJlAB/h8L9jgYih9bvJnvvFVM7yug==", + "cpu": [ + "x86", + "ia32" + ], + "license": "FSL-1.1-MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-win32-x64": { + "version": "2.58.4", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.58.4.tgz", + "integrity": "sha512-cSzN4PjM1RsCZ4pxMjI0VI7yNCkxiJ5jmWncyiwHXGiXrV1eXYdQ3n1LhUYLZ91CafyprR0OhDcE+RVZ26Qb5w==", + "cpu": [ + "x64" + ], + "license": "FSL-1.1-MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "node": ">=10" } }, - "node_modules/@react-email/link": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/link/-/link-0.0.13.tgz", - "integrity": "sha512-lkWc/NjOcefRZMkQoSDDbuKBEBDES9aXnFEOuPH845wD3TxPwh+QTf0fStuzjoRLUZWpHnio4z7qGGRYusn/sw==", + "node_modules/@sentry/core": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.38.0.tgz", + "integrity": "sha512-1pubWDZE5y5HZEPMAZERP4fVl2NH3Ihp1A+vMoVkb3Qc66Diqj1WierAnStlZP7tCx0TBa0dK85GTW/ZFYyB9g==", "license": "MIT", "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "node": ">=18" } }, - "node_modules/@react-email/markdown": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/@react-email/markdown/-/markdown-0.0.18.tgz", - "integrity": "sha512-gSuYK5fsMbGk87jDebqQ6fa2fKcWlkf2Dkva8kMONqLgGCq8/0d+ZQYMEJsdidIeBo3kmsnHZPrwdFB4HgjUXg==", + "node_modules/@sentry/nextjs": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/nextjs/-/nextjs-10.38.0.tgz", + "integrity": "sha512-MW2f6mK54jFyS/lmJxT7GWr5d12E+3qvIhR5EdjdyzMX8udSOCGyFJaFIwUfMyEMuggPEvNQVFFpjIrvWXCSGA==", "license": "MIT", "dependencies": { - "marked": "^15.0.12" + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.37.0", + "@rollup/plugin-commonjs": "28.0.1", + "@sentry-internal/browser-utils": "10.38.0", + "@sentry/bundler-plugin-core": "^4.8.0", + "@sentry/core": "10.38.0", + "@sentry/node": "10.38.0", + "@sentry/opentelemetry": "10.38.0", + "@sentry/react": "10.38.0", + "@sentry/vercel-edge": "10.38.0", + "@sentry/webpack-plugin": "^4.8.0", + "rollup": "^4.35.0", + "stacktrace-parser": "^0.1.10" }, "engines": { - "node": ">=20.0.0" + "node": ">=18" }, "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-email/preview": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/@react-email/preview/-/preview-0.0.14.tgz", - "integrity": "sha512-aYK8q0IPkBXyMsbpMXgxazwHxYJxTrXrV95GFuu2HbEiIToMwSyUgb8HDFYwPqqfV03/jbwqlsXmFxsOd+VNaw==", - "license": "MIT", - "engines": { - "node": ">=20.0.0" + "next": "^13.2.0 || ^14.0 || ^15.0.0-rc.0 || ^16.0.0-0" + } + }, + "node_modules/@sentry/node": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.38.0.tgz", + "integrity": "sha512-wriyDtWDAoatn8EhOj0U4PJR1WufiijTsCGALqakOHbFiadtBJANLe6aSkXoXT4tegw59cz1wY4NlzHjYksaPw==", + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^2.5.0", + "@opentelemetry/core": "^2.5.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/instrumentation-amqplib": "0.58.0", + "@opentelemetry/instrumentation-connect": "0.54.0", + "@opentelemetry/instrumentation-dataloader": "0.28.0", + "@opentelemetry/instrumentation-express": "0.59.0", + "@opentelemetry/instrumentation-fs": "0.30.0", + "@opentelemetry/instrumentation-generic-pool": "0.54.0", + "@opentelemetry/instrumentation-graphql": "0.58.0", + "@opentelemetry/instrumentation-hapi": "0.57.0", + "@opentelemetry/instrumentation-http": "0.211.0", + "@opentelemetry/instrumentation-ioredis": "0.59.0", + "@opentelemetry/instrumentation-kafkajs": "0.20.0", + "@opentelemetry/instrumentation-knex": "0.55.0", + "@opentelemetry/instrumentation-koa": "0.59.0", + "@opentelemetry/instrumentation-lru-memoizer": "0.55.0", + "@opentelemetry/instrumentation-mongodb": "0.64.0", + "@opentelemetry/instrumentation-mongoose": "0.57.0", + "@opentelemetry/instrumentation-mysql": "0.57.0", + "@opentelemetry/instrumentation-mysql2": "0.57.0", + "@opentelemetry/instrumentation-pg": "0.63.0", + "@opentelemetry/instrumentation-redis": "0.59.0", + "@opentelemetry/instrumentation-tedious": "0.30.0", + "@opentelemetry/instrumentation-undici": "0.21.0", + "@opentelemetry/resources": "^2.5.0", + "@opentelemetry/sdk-trace-base": "^2.5.0", + "@opentelemetry/semantic-conventions": "^1.39.0", + "@prisma/instrumentation": "7.2.0", + "@sentry/core": "10.38.0", + "@sentry/node-core": "10.38.0", + "@sentry/opentelemetry": "10.38.0", + "import-in-the-middle": "^2.0.6", + "minimatch": "^9.0.0" }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": ">=18" } }, - "node_modules/@react-email/render": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@react-email/render/-/render-2.0.4.tgz", - "integrity": "sha512-kht2oTFQ1SwrLpd882ahTvUtNa9s53CERHstiTbzhm6aR2Hbykp/mQ4tpPvsBGkKAEvKRlDEoooh60Uk6nHK1g==", + "node_modules/@sentry/node-core": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.38.0.tgz", + "integrity": "sha512-ErXtpedrY1HghgwM6AliilZPcUCoNNP1NThdO4YpeMq04wMX9/GMmFCu46TnCcg6b7IFIOSr2S4yD086PxLlHQ==", "license": "MIT", "dependencies": { - "html-to-text": "^9.0.5", - "prettier": "^3.5.3" + "@apm-js-collab/tracing-hooks": "^0.3.1", + "@sentry/core": "10.38.0", + "@sentry/opentelemetry": "10.38.0", + "import-in-the-middle": "^2.0.6" }, "engines": { - "node": ">=20.0.0" + "node": ">=18" }, "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/instrumentation": ">=0.57.1 <1", + "@opentelemetry/resources": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", + "@opentelemetry/semantic-conventions": "^1.39.0" } }, - "node_modules/@react-email/row": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@react-email/row/-/row-0.0.13.tgz", - "integrity": "sha512-bYnOac40vIKCId7IkwuLAAsa3fKfSfqCvv6epJKmPE0JBuu5qI4FHFCl9o9dVpIIS08s/ub+Y/txoMt0dYziGw==", + "node_modules/@sentry/node/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/@react-email/section": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/@react-email/section/-/section-0.0.17.tgz", - "integrity": "sha512-qNl65ye3W0Rd5udhdORzTV9ezjb+GFqQQSae03NDzXtmJq6sqVXNWNiVolAjvJNypim+zGXmv6J9TcV5aNtE/w==", - "license": "MIT", + "node_modules/@sentry/node/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=20.0.0" + "node": ">=16 || 14 >=14.17" }, - "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@react-email/tailwind": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-2.0.4.tgz", - "integrity": "sha512-cDp8Ss6LJKI8zBLKE+tsXFurn6I2nnQNg1qqjfZuNPNoToN1Uyx3egW0bwSVk1JjrNWx/Xnme7ZxvNLRrU9K0Q==", + "node_modules/@sentry/opentelemetry": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.38.0.tgz", + "integrity": "sha512-YPVhWfYmC7nD3EJqEHGtjp4fp5LwtAbE5rt9egQ4hqJlYFvr8YEz9sdoqSZxO0cZzgs2v97HFl/nmWAXe52G2Q==", "license": "MIT", "dependencies": { - "tailwindcss": "^4.1.18" + "@sentry/core": "10.38.0" }, "engines": { - "node": ">=20.0.0" + "node": ">=18" }, "peerDependencies": { - "@react-email/body": "0.2.1", - "@react-email/button": "0.2.1", - "@react-email/code-block": "0.2.1", - "@react-email/code-inline": "0.0.6", - "@react-email/container": "0.0.16", - "@react-email/heading": "0.0.16", - "@react-email/hr": "0.0.12", - "@react-email/img": "0.0.12", - "@react-email/link": "0.0.13", - "@react-email/preview": "0.0.14", - "@react-email/text": "0.1.6", - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@react-email/body": { - "optional": true - }, - "@react-email/button": { - "optional": true - }, - "@react-email/code-block": { - "optional": true - }, - "@react-email/code-inline": { - "optional": true - }, - "@react-email/container": { - "optional": true - }, - "@react-email/heading": { - "optional": true - }, - "@react-email/hr": { - "optional": true - }, - "@react-email/img": { - "optional": true - }, - "@react-email/link": { - "optional": true - }, - "@react-email/preview": { - "optional": true - } + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", + "@opentelemetry/semantic-conventions": "^1.39.0" } }, - "node_modules/@react-email/text": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@react-email/text/-/text-0.1.6.tgz", - "integrity": "sha512-TYqkioRS45wTR5il3dYk/SbUjjEdhSwh9BtRNB99qNH1pXAwA45H7rAuxehiu8iJQJH0IyIr+6n62gBz9ezmsw==", + "node_modules/@sentry/react": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-10.38.0.tgz", + "integrity": "sha512-3UiKo6QsqTyPGUt0XWRY9KLaxc/cs6Kz4vlldBSOXEL6qPDL/EfpwNJT61osRo81VFWu8pKu7ZY2bvLPryrnBQ==", "license": "MIT", + "dependencies": { + "@sentry/browser": "10.38.0", + "@sentry/core": "10.38.0" + }, "engines": { - "node": ">=20.0.0" + "node": ">=18" }, "peerDependencies": { - "react": "^18.0 || ^19.0 || ^19.0.0-rc" - } - }, - "node_modules/@react-oauth/google": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.13.0.tgz", - "integrity": "sha512-ukoKALR0VX9TmEGVHTDcRiCtJuNp+Zm8zxR+hm5KN+yON/lGHVzFlUIW1go0L8qHQZobbVZbDv40Eg+MVrWtnQ==", - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "react": "^16.14.0 || 17.x || 18.x || 19.x" } }, - "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/@restart/hooks": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", - "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "node_modules/@sentry/vercel-edge": { + "version": "10.38.0", + "resolved": "https://registry.npmjs.org/@sentry/vercel-edge/-/vercel-edge-10.38.0.tgz", + "integrity": "sha512-lElDFktj/PyRC/LDHejPFhQmHVMCB9Celj+IHi36aw96a/LekqF6/7vmp26hDtH58QtuiPO3h5voqEAMUOkSlw==", "license": "MIT", "dependencies": { - "dequal": "^2.0.3" + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/resources": "^2.5.0", + "@sentry/core": "10.38.0" }, - "peerDependencies": { - "react": ">=16.8.0" + "engines": { + "node": ">=18" } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "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==", + "node_modules/@sentry/webpack-plugin": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-4.9.0.tgz", + "integrity": "sha512-2usiAS8vVBb24DXMYHtHsuCasnxo5uJMO6tpGPCMpyLYVooq5ypNvV+egiwlO6Dmyp9/BFK2hcK1vPRL5K5Trw==", "license": "MIT", "dependencies": { - "domhandler": "^5.0.3", - "selderee": "^0.11.0" + "@sentry/bundler-plugin-core": "4.9.0", + "unplugin": "1.0.1", + "uuid": "^9.0.0" }, - "funding": { - "url": "https://ko-fi.com/killymxi" + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "webpack": ">=4.40.0" } }, "node_modules/@sinclair/typebox": { @@ -4152,6 +5720,15 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/date-arithmetic": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@types/date-arithmetic/-/date-arithmetic-4.1.4.tgz", @@ -4163,7 +5740,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, "node_modules/@types/graceful-fs": { @@ -4250,6 +5826,15 @@ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "license": "MIT" }, + "node_modules/@types/mysql": { + "version": "2.15.27", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", + "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.19.27", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.27.tgz", @@ -4263,7 +5848,6 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.16.0.tgz", "integrity": "sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -4271,6 +5855,15 @@ "pg-types": "^2.2.0" } }, + "node_modules/@types/pg-pool": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.7.tgz", + "integrity": "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==", + "license": "MIT", + "dependencies": { + "@types/pg": "*" + } + }, "node_modules/@types/phoenix": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", @@ -4322,6 +5915,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "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", @@ -4920,7 +6522,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -4929,6 +6530,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -4988,7 +6598,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4998,7 +6607,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -5014,7 +6622,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -5394,7 +7001,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -5435,6 +7041,18 @@ "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==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -5450,7 +7068,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -5463,7 +7080,6 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, "funding": [ { "type": "opencollective", @@ -5799,7 +7415,6 @@ "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, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -5812,7 +7427,12 @@ "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, + "license": "MIT" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "license": "MIT" }, "node_modules/concat-map": { @@ -5843,7 +7463,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, "license": "MIT" }, "node_modules/cookie": { @@ -5891,7 +7510,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -6248,6 +7866,12 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -6272,7 +7896,6 @@ "version": "1.5.267", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", - "dev": true, "license": "ISC" }, "node_modules/emittery": { @@ -6292,7 +7915,6 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/empathic": { @@ -6528,7 +8150,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6974,6 +8595,12 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -7162,6 +8789,23 @@ "bser": "2.1.1" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -7179,7 +8823,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -7192,7 +8835,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", @@ -7242,6 +8884,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", + "license": "MIT" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7253,7 +8929,6 @@ "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, "license": "MIT", "optional": true, @@ -7346,7 +9021,6 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -7933,6 +9607,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-in-the-middle": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", + "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.15.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + } + }, + "node_modules/import-in-the-middle/node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "license": "MIT" + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -8067,6 +9759,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", @@ -8190,7 +9894,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -8216,7 +9919,6 @@ "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, "license": "MIT", "engines": { "node": ">=8" @@ -8256,7 +9958,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -8295,7 +9996,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -8318,6 +10018,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -8498,7 +10207,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { @@ -8603,6 +10311,21 @@ "node": ">= 0.4" } }, + "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==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -9257,7 +10980,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -9307,7 +11029,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -9762,7 +11483,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" @@ -9816,7 +11536,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" @@ -9844,7 +11563,6 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -10028,6 +11746,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT" + }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -10231,7 +11964,6 @@ "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, "license": "MIT" }, "node_modules/nodemailer": { @@ -10247,7 +11979,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10502,7 +12233,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -10518,7 +12248,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" @@ -10540,6 +12269,12 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -10589,7 +12324,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10609,7 +12343,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10622,6 +12355,28 @@ "dev": true, "license": "MIT" }, + "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==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "license": "ISC" + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -10744,7 +12499,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -11060,6 +12814,15 @@ "node": ">=6" } }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -11280,6 +13043,12 @@ "prosemirror-transform": "^1.1.0" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -11632,6 +13401,19 @@ "node": ">=0.10.0" } }, + "node_modules/require-in-the-middle": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3" + }, + "engines": { + "node": ">=9.3.0 || >=8.10.0 <9.0.0" + } + }, "node_modules/resend": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/resend/-/resend-6.9.1.tgz", @@ -11738,6 +13520,50 @@ "node": ">=0.10.0" } }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, "node_modules/rope-sequence": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", @@ -11877,7 +13703,6 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -12000,7 +13825,6 @@ "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, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -12013,7 +13837,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12191,6 +14014,27 @@ "node": ">=8" } }, + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, "node_modules/standardwebhooks": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", @@ -12233,7 +14077,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, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -12244,11 +14087,31 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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==", + "license": "MIT" + }, "node_modules/string-width/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, "license": "MIT" }, "node_modules/string.prototype.includes": { @@ -12368,7 +14231,19 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12563,24 +14438,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/tinyglobby/node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", @@ -12614,7 +14471,6 @@ "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, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -12980,6 +14836,66 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, + "node_modules/unplugin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.0.1.tgz", + "integrity": "sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==", + "license": "MIT", + "dependencies": { + "acorn": "^8.8.1", + "chokidar": "^3.5.3", + "webpack-sources": "^3.2.3", + "webpack-virtual-modules": "^0.5.0" + } + }, + "node_modules/unplugin/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "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/unplugin/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==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/unplugin/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/unrs-resolver": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", @@ -13019,7 +14935,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, "funding": [ { "type": "opencollective", @@ -13173,6 +15088,21 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, + "node_modules/webpack-sources": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz", + "integrity": "sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==", + "license": "MIT" + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -13187,7 +15117,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -13323,6 +15252,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -13388,7 +15335,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, "license": "ISC" }, "node_modules/yargs": { @@ -13424,7 +15370,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" diff --git a/package.json b/package.json index 900f793..b73d7b4 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@react-email/components": "^1.0.7", "@react-email/render": "^2.0.4", "@react-oauth/google": "^0.13.0", + "@sentry/nextjs": "^10.38.0", "@supabase/ssr": "^0.8.0", "@supabase/supabase-js": "^2.89.0", "@tanstack/react-table": "^8.21.3", diff --git a/sentry.client.config.ts b/sentry.client.config.ts new file mode 100644 index 0000000..0e52d8e --- /dev/null +++ b/sentry.client.config.ts @@ -0,0 +1,27 @@ +// This file configures the initialization of Sentry on the client. +// The config you add here will be used whenever a users loads a page in their browser. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +Sentry.init({ + dsn: "https://8389f17c4ece4d169ecc4660a26a7a18@o4510849772486656.ingest.us.sentry.io/4510849774518272", + + // Add optional integrations for additional features + integrations: [Sentry.replayIntegration()], + + // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. + tracesSampleRate: 1, + + // Define how likely Replay events are sampled. + // This sets the sample rate to be 10%. You may want this to be 100% while + // in development and sample at a lower rate in production + replaysSessionSampleRate: 0.1, + + // Define how likely Replay events are sampled when an error occurs. + replaysOnErrorSampleRate: 1.0, + + // Enable sending user PII (Personally Identifiable Information) + // https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#sendDefaultPii + sendDefaultPii: true, +}); diff --git a/sentry.edge.config.ts b/sentry.edge.config.ts new file mode 100644 index 0000000..805507a --- /dev/null +++ b/sentry.edge.config.ts @@ -0,0 +1,20 @@ +// This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on). +// The config you add here will be used whenever one of the edge features is loaded. +// Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +Sentry.init({ + dsn: "https://8389f17c4ece4d169ecc4660a26a7a18@o4510849772486656.ingest.us.sentry.io/4510849774518272", + + // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. + tracesSampleRate: 1, + + // Enable logs to be sent to Sentry + enableLogs: true, + + // Enable sending user PII (Personally Identifiable Information) + // https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#sendDefaultPii + sendDefaultPii: true, +}); diff --git a/sentry.server.config.ts b/sentry.server.config.ts new file mode 100644 index 0000000..5622f71 --- /dev/null +++ b/sentry.server.config.ts @@ -0,0 +1,19 @@ +// This file configures the initialization of Sentry on the server. +// The config you add here will be used whenever the server handles a request. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +Sentry.init({ + dsn: "https://8389f17c4ece4d169ecc4660a26a7a18@o4510849772486656.ingest.us.sentry.io/4510849774518272", + + // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. + tracesSampleRate: 1, + + // Enable logs to be sent to Sentry + enableLogs: true, + + // Enable sending user PII (Personally Identifiable Information) + // https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/options/#sendDefaultPii + sendDefaultPii: true, +}); From 3184e4c7b4114c08224dae8e80692688e20e4845 Mon Sep 17 00:00:00 2001 From: PC Date: Sun, 8 Feb 2026 18:59:46 +0800 Subject: [PATCH 4/6] ci: allow workflow to run on all branches --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55e8f6e..05789f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: ["main"] + branches: ["**"] pull_request: branches: ["main"] From bef5ac209b1e4700b3af7723444040393d623bde Mon Sep 17 00:00:00 2001 From: PC Date: Sun, 8 Feb 2026 19:03:29 +0800 Subject: [PATCH 5/6] fix: update lock file so it matches package.json --- package-lock.json | 80 +++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index d44613a..9d01d0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "dotenv": "^17.2.3", "googleapis": "^105.0.0", "lucide-react": "^0.562.0", - "next": "16.1.0", + "next": "^16.1.6", "pg": "^8.16.3", "react": "19.2.3", "react-big-calendar": "^1.19.4", @@ -1905,9 +1905,9 @@ } }, "node_modules/@next/env": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.0.tgz", - "integrity": "sha512-Dd23XQeFHmhf3KBW76leYVkejHlCdB7erakC2At2apL1N08Bm+dLYNP+nNHh0tzUXfPQcNcXiQyacw0PG4Fcpw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz", + "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -1921,9 +1921,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.0.tgz", - "integrity": "sha512-onHq8dl8KjDb8taANQdzs3XmIqQWV3fYdslkGENuvVInFQzZnuBYYOG2HGHqqtvgmEU7xWzhgndXXxnhk4Z3fQ==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz", + "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==", "cpu": [ "arm64" ], @@ -1937,9 +1937,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.0.tgz", - "integrity": "sha512-Am6VJTp8KhLuAH13tPrAoVIXzuComlZlMwGr++o2KDjWiKPe3VwpxYhgV6I4gKls2EnsIMggL4y7GdXyDdJcFA==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz", + "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==", "cpu": [ "x64" ], @@ -1953,9 +1953,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.0.tgz", - "integrity": "sha512-fVicfaJT6QfghNyg8JErZ+EMNQ812IS0lmKfbmC01LF1nFBcKfcs4Q75Yy8IqnsCqH/hZwGhqzj3IGVfWV6vpA==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz", + "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==", "cpu": [ "arm64" ], @@ -1969,9 +1969,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.0.tgz", - "integrity": "sha512-TojQnDRoX7wJWXEEwdfuJtakMDW64Q7NrxQPviUnfYJvAx5/5wcGE+1vZzQ9F17m+SdpFeeXuOr6v3jbyusYMQ==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz", + "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==", "cpu": [ "arm64" ], @@ -1985,9 +1985,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.0.tgz", - "integrity": "sha512-quhNFVySW4QwXiZkZ34SbfzNBm27vLrxZ2HwTfFFO1BBP0OY1+pI0nbyewKeq1FriqU+LZrob/cm26lwsiAi8Q==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz", + "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==", "cpu": [ "x64" ], @@ -2001,9 +2001,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.0.tgz", - "integrity": "sha512-6JW0z2FZUK5iOVhUIWqE4RblAhUj1EwhZ/MwteGb//SpFTOHydnhbp3868gxalwea+mbOLWO6xgxj9wA9wNvNw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz", + "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==", "cpu": [ "x64" ], @@ -2017,9 +2017,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.0.tgz", - "integrity": "sha512-+DK/akkAvvXn5RdYN84IOmLkSy87SCmpofJPdB8vbLmf01BzntPBSYXnMvnEEv/Vcf3HYJwt24QZ/s6sWAwOMQ==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz", + "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==", "cpu": [ "arm64" ], @@ -2033,9 +2033,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.0.tgz", - "integrity": "sha512-Tr0j94MphimCCks+1rtYPzQFK+faJuhHWCegU9S9gDlgyOk8Y3kPmO64UcjyzZAlligeBtYZ/2bEyrKq0d2wqQ==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz", + "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==", "cpu": [ "x64" ], @@ -11837,12 +11837,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/next/-/next-16.1.0.tgz", - "integrity": "sha512-Y+KbmDbefYtHDDQKLNrmzE/YYzG2msqo2VXhzh5yrJ54tx/6TmGdkR5+kP9ma7i7LwZpZMfoY3m/AoPPPKxtVw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz", + "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==", "license": "MIT", "dependencies": { - "@next/env": "16.1.0", + "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", @@ -11856,14 +11856,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.1.0", - "@next/swc-darwin-x64": "16.1.0", - "@next/swc-linux-arm64-gnu": "16.1.0", - "@next/swc-linux-arm64-musl": "16.1.0", - "@next/swc-linux-x64-gnu": "16.1.0", - "@next/swc-linux-x64-musl": "16.1.0", - "@next/swc-win32-arm64-msvc": "16.1.0", - "@next/swc-win32-x64-msvc": "16.1.0", + "@next/swc-darwin-arm64": "16.1.6", + "@next/swc-darwin-x64": "16.1.6", + "@next/swc-linux-arm64-gnu": "16.1.6", + "@next/swc-linux-arm64-musl": "16.1.6", + "@next/swc-linux-x64-gnu": "16.1.6", + "@next/swc-linux-x64-musl": "16.1.6", + "@next/swc-win32-arm64-msvc": "16.1.6", + "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { From 749b034257876adeed19d0a9283b705ad24928a2 Mon Sep 17 00:00:00 2001 From: PC Date: Sun, 8 Feb 2026 19:04:42 +0800 Subject: [PATCH 6/6] fix: update lock file so it matches package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b73d7b4..8bf7a67 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "dotenv": "^17.2.3", "googleapis": "^105.0.0", "lucide-react": "^0.562.0", - "next": "16.1.0", + "next": "^16.1.6", "pg": "^8.16.3", "react": "19.2.3", "react-big-calendar": "^1.19.4",