From d8fe3c43bd7a6e02b76fe7cb7e02da698ed85c1c Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Tue, 2 Dec 2025 23:09:02 -0500 Subject: [PATCH 01/41] docs: fix incorrect subscription URLs in url-subscribe documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update all references from feedsmith.dev to feed.tuvix.app in the URL-based subscription documentation. This corrects the domain used in example URLs, bookmarklets, and integration examples. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docs/features/url-subscribe.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/features/url-subscribe.md b/docs/features/url-subscribe.md index b6b190e0..7965d9f3 100644 --- a/docs/features/url-subscribe.md +++ b/docs/features/url-subscribe.md @@ -9,17 +9,17 @@ Tuvix supports multiple ways to subscribe to RSS feeds and import OPML files, in ### Basic URL Format ``` -https://feedsmith.dev/app/subscriptions?subscribe= +https://feed.tuvix.app/app/subscriptions?subscribe= ``` ### Examples ``` # Subscribe to a blog feed -https://feedsmith.dev/app/subscriptions?subscribe=https%3A%2F%2Fblog.example.com%2Ffeed.xml +https://feed.tuvix.app/app/subscriptions?subscribe=https%3A%2F%2Fblog.example.com%2Ffeed.xml # Subscribe from a website URL (auto-discovery) -https://feedsmith.dev/app/subscriptions?subscribe=https%3A%2F%2Fblog.example.com +https://feed.tuvix.app/app/subscriptions?subscribe=https%3A%2F%2Fblog.example.com ``` ## How It Works @@ -57,7 +57,7 @@ Create a browser bookmarklet for one-click subscriptions: ```javascript javascript:(function(){ - const tuvixUrl = 'https://feedsmith.dev/app/subscriptions'; + const tuvixUrl = 'https://feed.tuvix.app/app/subscriptions'; const currentUrl = encodeURIComponent(window.location.href); window.open(`${tuvixUrl}?subscribe=${currentUrl}`, '_blank'); })(); @@ -68,7 +68,7 @@ javascript:(function(){ Add "Subscribe in Tuvix" buttons to your blog: ```html - + Subscribe in Tuvix ``` @@ -81,12 +81,12 @@ Use the URL format in browser extensions to detect feeds on pages and provide qu ```html - + ๐Ÿ“ฐ Follow our blog in Tuvix - + ๐Ÿ”” Get changelog updates ``` From fc684bb873fe827ee2c5a23b109fd86659299624 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 00:15:07 -0500 Subject: [PATCH 02/41] docs: add browser extension section to README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add mention of the Tuvix Tricorder Extension for Chrome and Firefox in the Hosted section of the README. This helps users discover the companion extension for easy RSS feed subscription. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 94a023a8..fdbdad01 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,10 @@ Head over to **[tuvix.app](https://tuvix.app)** to create your free account and Immediately begin subscribing to your favorite feeds. Remember, you can always export your data to OPML and migrate to your own self-hosted instance, or any other RSS reader. +### Browser Extension + +Install the **[Tuvix Tricorder Extension](https://github.com/TechSquidTV/Tuvix-Tricorder-Extension)** for Chrome and Firefox to easily discover and subscribe to RSS feeds on any website with one click. + --- ## ๐Ÿš€ Deployment From 0579bbc37cb70018662d06ce619eff70307a3912 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 16:13:23 -0500 Subject: [PATCH 03/41] refactor(auth): extract header parsing utility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add extractHeaders() utility function to normalize request header extraction across authentication flows. Handles both Headers object and plain object formats, reducing code duplication by ~100 lines. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/auth/security.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/api/src/auth/security.ts b/packages/api/src/auth/security.ts index 01d9eb64..0e42ff3e 100644 --- a/packages/api/src/auth/security.ts +++ b/packages/api/src/auth/security.ts @@ -88,3 +88,34 @@ export function getUserAgent( ): string | undefined { return headers["user-agent"]; } + +/** + * Extract headers from request into a normalized Record + * Handles both Headers object and plain object formats + */ +export function extractHeaders( + reqHeaders: + | Headers + | Record + | undefined +): Record { + const headers: Record = {}; + + if (!reqHeaders) { + return headers; + } + + if (reqHeaders instanceof Headers) { + reqHeaders.forEach((value, key) => { + headers[key.toLowerCase()] = value; + }); + } else { + Object.entries(reqHeaders).forEach(([key, value]) => { + headers[key.toLowerCase()] = Array.isArray(value) + ? value[0] + : String(value); + }); + } + + return headers; +} From 03fdb0f2aaf8ee83644ab7528d121a37f4f2ec7c Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 16:13:36 -0500 Subject: [PATCH 04/41] fix(auth): add Sentry breadcrumbs to email flows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix silent email failures by adding comprehensive breadcrumbs and proper error checking throughout email sending flows. - Add breadcrumbs at email service level (attempt, API call, result) - Change Better Auth callbacks from .catch() to .then() to check emailResult.success (email service returns errors, doesn't throw) - Add breadcrumbs for verification, welcome, and password reset emails - Add Sentry capture for settings check failures - Ensures all email failures are tracked for debugging ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/auth/better-auth.ts | 418 ++++++++++++++++++--------- packages/api/src/services/email.ts | 64 ++++ 2 files changed, 347 insertions(+), 135 deletions(-) diff --git a/packages/api/src/auth/better-auth.ts b/packages/api/src/auth/better-auth.ts index d0227bce..d7383bce 100644 --- a/packages/api/src/auth/better-auth.ts +++ b/packages/api/src/auth/better-auth.ts @@ -19,7 +19,7 @@ import { } from "@/services/email"; import { logSecurityEvent } from "@/auth/security"; import { getGlobalSettings } from "@/services/global-settings"; -import { getClientIp, getUserAgent } from "@/auth/security"; +import { getClientIp, getUserAgent, extractHeaders } from "@/auth/security"; import { eq } from "drizzle-orm"; import * as schema from "@/db/schema"; import type { Env } from "@/types"; @@ -151,6 +151,18 @@ export function createAuth(env: Env, db?: ReturnType) { }, }, async (span) => { + // Add breadcrumb for email sending attempt + await Sentry.addBreadcrumb({ + category: "email", + message: "Sending password reset email", + level: "info", + data: { + userEmail: user.email, + userId: user.id, + emailType: "password_reset", + }, + }); + const userWithPlugins = user as BetterAuthUser; const emailResult = await sendPasswordResetEmail(env, { to: user.email, @@ -170,6 +182,31 @@ export function createAuth(env: Env, db?: ReturnType) { "email.error", emailResult.error || "Unknown error" ); + + // Add breadcrumb for failure + await Sentry.addBreadcrumb({ + category: "email", + message: "Password reset email failed", + level: "error", + data: { + userEmail: user.email, + userId: user.id, + error: emailResult.error, + emailType: "password_reset", + }, + }); + } else { + // Add breadcrumb for success + await Sentry.addBreadcrumb({ + category: "email", + message: "Password reset email sent successfully", + level: "info", + data: { + userEmail: user.email, + userId: user.id, + emailType: "password_reset", + }, + }); } // Log email result to security audit log @@ -311,6 +348,18 @@ export function createAuth(env: Env, db?: ReturnType) { // This ensures proper logo loading and post-verification redirect const frontendVerificationUrl = `${frontendUrl}/verify-email?token=${token}`; + // Add breadcrumb for email sending attempt + Sentry.addBreadcrumb({ + category: "email", + message: "Sending verification email", + level: "info", + data: { + userEmail: user.email, + userId: user.id, + emailType: "verification", + }, + }); + // Send verification email (fire-and-forget) sendVerificationEmail(env, { to: user.email, @@ -320,33 +369,114 @@ export function createAuth(env: Env, db?: ReturnType) { "User", verificationToken: token, verificationUrl: frontendVerificationUrl, // Frontend URL instead of backend URL - }).catch((error) => { - // Log critical email failures to Sentry - Sentry.captureException(error, { - tags: { - component: "better-auth", - operation: "email-verification", - email_type: "verification", - }, - extra: { + }) + .then((emailResult) => { + // Check if email sending failed + if (!emailResult.success) { + // Add breadcrumb for failure + Sentry.addBreadcrumb({ + category: "email", + message: "Verification email failed", + level: "error", + data: { + userEmail: user.email, + userId: user.id, + error: emailResult.error, + emailType: "verification", + }, + }); + + // Log critical email failures to Sentry + Sentry.captureException( + new Error( + emailResult.error || "Failed to send verification email" + ), + { + tags: { + component: "better-auth", + operation: "email-verification", + email_type: "verification", + }, + extra: { + userEmail: user.email, + userId: user.id, + errorMessage: emailResult.error, + }, + level: "error", + } + ); + + console.error("Failed to send verification email:", { + userEmail: user.email, + userId: user.id, + error: emailResult.error, + }); + } else { + // Add breadcrumb for success + Sentry.addBreadcrumb({ + category: "email", + message: "Verification email sent successfully", + level: "info", + data: { + userEmail: user.email, + userId: user.id, + emailType: "verification", + }, + }); + } + }) + .catch((error) => { + // Add breadcrumb for unexpected error + Sentry.addBreadcrumb({ + category: "email", + message: "Unexpected error sending verification email", + level: "error", + data: { + userEmail: user.email, + userId: user.id, + error: + error instanceof Error ? error.message : String(error), + emailType: "verification", + }, + }); + + // Log unexpected exceptions (e.g., network errors, timeouts) + Sentry.captureException(error, { + tags: { + component: "better-auth", + operation: "email-verification", + email_type: "verification", + }, + extra: { + userEmail: user.email, + userId: user.id, + }, + level: "error", + }); + + console.error("Unexpected error sending verification email:", { userEmail: user.email, userId: user.id, - }, - level: "error", - }); - - console.error("Failed to send verification email:", { - userEmail: user.email, - userId: user.id, - error: error instanceof Error ? error.message : String(error), + error: error instanceof Error ? error.message : String(error), + }); }); - }); }) - .catch((error) => { + .catch(async (error) => { console.error( "Failed to check email verification settings:", error ); + await Sentry.captureException(error, { + tags: { + component: "better-auth", + operation: "check-verification-settings", + }, + extra: { + userId: user.id, + userEmail: user.email, + }, + level: "warning", + }); }); // Return immediately without awaiting @@ -391,20 +521,6 @@ export function createAuth(env: Env, db?: ReturnType) { if (newSession?.user) { const user = newSession.user; - // Get IP and user agent from headers - const headers: Record = {}; - if (ctx.headers) { - if (ctx.headers instanceof Headers) { - ctx.headers.forEach((value, key) => { - headers[key.toLowerCase()] = value; - }); - } else { - Object.entries(ctx.headers).forEach(([key, value]) => { - headers[key.toLowerCase()] = String(value); - }); - } - } - // Note: Registration event logging is handled in the register endpoint // after the user is created in the user table, to avoid foreign key constraint issues @@ -424,6 +540,18 @@ export function createAuth(env: Env, db?: ReturnType) { const appUrl = frontendUrl; const userWithPlugins = user as BetterAuthUser; + // Add breadcrumb for email sending attempt + Sentry.addBreadcrumb({ + category: "email", + message: "Sending welcome email", + level: "info", + data: { + userEmail: user.email, + userId: user.id, + emailType: "welcome", + }, + }); + // Send welcome email (fire-and-forget) sendWelcomeEmail(env, { to: user.email, @@ -432,134 +560,164 @@ export function createAuth(env: Env, db?: ReturnType) { user.name || "User", appUrl, - }).catch((error) => { - // Log email failures to Sentry - Sentry.captureException(error, { - tags: { - component: "better-auth", - operation: "welcome-email", - email_type: "welcome", - }, - extra: { - userEmail: user.email, - userId: user.id, - }, - level: "error", - }); - - console.error( - `Failed to send welcome email to ${user.email}:`, - { - error: - error instanceof Error - ? error.message - : String(error), + }) + .then((emailResult) => { + // Check if email sending failed + if (!emailResult.success) { + // Add breadcrumb for failure + Sentry.addBreadcrumb({ + category: "email", + message: "Welcome email failed", + level: "error", + data: { + userEmail: user.email, + userId: user.id, + error: emailResult.error, + emailType: "welcome", + }, + }); + + // Log email failures to Sentry + Sentry.captureException( + new Error( + emailResult.error || "Failed to send welcome email" + ), + { + tags: { + component: "better-auth", + operation: "welcome-email", + email_type: "welcome", + }, + extra: { + userEmail: user.email, + userId: user.id, + errorMessage: emailResult.error, + }, + level: "error", + } + ); + + console.error( + `Failed to send welcome email to ${user.email}:`, + { + error: emailResult.error, + } + ); + } else { + // Add breadcrumb for success + Sentry.addBreadcrumb({ + category: "email", + message: "Welcome email sent successfully", + level: "info", + data: { + userEmail: user.email, + userId: user.id, + emailType: "welcome", + }, + }); } - ); - }); + }) + .catch((error) => { + // Add breadcrumb for unexpected error + Sentry.addBreadcrumb({ + category: "email", + message: "Unexpected error sending welcome email", + level: "error", + data: { + userEmail: user.email, + userId: user.id, + error: + error instanceof Error + ? error.message + : String(error), + emailType: "welcome", + }, + }); + + // Log unexpected exceptions (e.g., network errors, timeouts) + Sentry.captureException(error, { + tags: { + component: "better-auth", + operation: "welcome-email", + email_type: "welcome", + }, + extra: { + userEmail: user.email, + userId: user.id, + }, + level: "error", + }); + + console.error( + `Unexpected error sending welcome email to ${user.email}:`, + { + error: + error instanceof Error + ? error.message + : String(error), + } + ); + }); } }) - .catch((error) => { + .catch(async (error) => { console.error( `Error checking welcome email requirements:`, error ); + await Sentry.captureException(error, { + tags: { + component: "better-auth", + operation: "check-welcome-settings", + }, + extra: { + userId: user.id, + userEmail: user.email, + }, + level: "warning", + }); }); } } // Handle sign-in events - if (ctx.path.startsWith("/sign-in")) { + // Note: Login audit logging is handled in auth.ts router for consistency with signup flow + // and to have access to more request context + + // Handle sign-out events + if (ctx.path.startsWith("/sign-out")) { const session = ctx.context.session; if (session?.user) { const user = session.user; // Get IP and user agent from headers - const headers: Record = {}; - if (ctx.headers) { - if (ctx.headers instanceof Headers) { - ctx.headers.forEach((value, key) => { - headers[key.toLowerCase()] = value; - }); - } else { - Object.entries(ctx.headers).forEach(([key, value]) => { - headers[key.toLowerCase()] = String(value); - }); - } - } - + const headers = extractHeaders(ctx.headers); const ipAddress = getClientIp(headers); const userAgent = getUserAgent(headers); - // Log successful login (non-blocking - don't fail login if logging fails) + // Log logout event (non-blocking - don't fail logout if logging fails) try { await logSecurityEvent(database, { userId: Number(user.id), - action: "login_success", + action: "logout", ipAddress, userAgent, success: true, }); } catch (error) { console.error( - `Failed to log login event for user ${user.id}:`, + `Failed to log logout event for user ${user.id}:`, error instanceof Error ? error.message : "Unknown error" ); - // Don't throw - logging failures shouldn't prevent login + // Don't throw - logging failures shouldn't prevent logout } } } - // Handle sign-out events - if (ctx.path.startsWith("/sign-out")) { - const session = ctx.context.session; - if (session?.user) { - const user = session.user; - - // Get IP and user agent from headers - const headers: Record = {}; - if (ctx.headers) { - if (ctx.headers instanceof Headers) { - ctx.headers.forEach((value, key) => { - headers[key.toLowerCase()] = value; - }); - } else { - Object.entries(ctx.headers).forEach(([key, value]) => { - headers[key.toLowerCase()] = String(value); - }); - } - } - - const ipAddress = getClientIp(headers); - const userAgent = getUserAgent(headers); - - // Log logout event - await logSecurityEvent(database, { - userId: Number(user.id), - action: "logout", - ipAddress, - userAgent, - success: true, - }); - } - } - // Handle password reset request events if (ctx.path.startsWith("/request-password-reset")) { // Get IP and user agent from headers - const headers: Record = {}; - if (ctx.headers) { - if (ctx.headers instanceof Headers) { - ctx.headers.forEach((value, key) => { - headers[key.toLowerCase()] = value; - }); - } else { - Object.entries(ctx.headers).forEach(([key, value]) => { - headers[key.toLowerCase()] = String(value); - }); - } - } - + const headers = extractHeaders(ctx.headers); const ipAddress = getClientIp(headers); const userAgent = getUserAgent(headers); @@ -576,6 +734,7 @@ export function createAuth(env: Env, db?: ReturnType) { if (userRecord) { // Log password reset request + // Note: Email sent status is logged in sendResetPassword callback with actual result await logSecurityEvent(database, { userId: Number(userRecord.id), action: "password_reset_request", @@ -583,17 +742,6 @@ export function createAuth(env: Env, db?: ReturnType) { userAgent, success: true, }); - - // Log password reset email sent (if email was sent successfully) - // Note: We can't determine email success here, so we'll log it as success - // The actual email result is handled in sendResetPassword callback - await logSecurityEvent(database, { - userId: Number(userRecord.id), - action: "password_reset_email_sent", - ipAddress, - userAgent, - success: true, - }); } } } catch (error) { diff --git a/packages/api/src/services/email.ts b/packages/api/src/services/email.ts index a2427cdf..39d8909c 100644 --- a/packages/api/src/services/email.ts +++ b/packages/api/src/services/email.ts @@ -106,6 +106,19 @@ async function sendEmail(options: SendEmailOptions): Promise { return { success: true }; // Return success in dev mode } + // Add breadcrumb at service level for debugging + await Sentry.addBreadcrumb({ + category: "email.service", + message: `Attempting to send ${type} email`, + level: "info", + data: { + emailType: type, + recipient: to, + subject, + configured: isEmailConfigured(env), + }, + }); + // Wrap email sending in Sentry span for observability and timing const sendEmailWithSentry = async (): Promise => { return await withTiming( @@ -115,6 +128,17 @@ async function sendEmail(options: SendEmailOptions): Promise { // Initialize Resend client const resend = new Resend(env.RESEND_API_KEY); + // Add breadcrumb before API call + await Sentry.addBreadcrumb({ + category: "email.service", + message: "Calling Resend API", + level: "info", + data: { + emailType: type, + recipient: to, + }, + }); + // Send email via Resend (using React Email component directly) const { data, error } = await resend.emails.send({ from: env.EMAIL_FROM!, @@ -125,6 +149,21 @@ async function sendEmail(options: SendEmailOptions): Promise { if (error) { const errorMessage = `Failed to send ${type} email: ${error.message || "Unknown error"}`; + + // Add breadcrumb for Resend API error + await Sentry.addBreadcrumb({ + category: "email.service", + message: "Resend API returned error", + level: "error", + data: { + emailType: type, + recipient: to, + errorMessage: error.message, + errorCode: (error as { code?: string })?.code, + errorStatus: (error as { status?: number })?.status, + }, + }); + console.error(errorMessage, { type, to, @@ -161,6 +200,18 @@ async function sendEmail(options: SendEmailOptions): Promise { }; } + // Add breadcrumb for successful send + await Sentry.addBreadcrumb({ + category: "email.service", + message: `${type} email sent successfully`, + level: "info", + data: { + emailType: type, + recipient: to, + emailId: data?.id, + }, + }); + // Log success (in development) if (process.env.NODE_ENV !== "production") { console.log(`โœ… ${type} email sent to ${to} (ID: ${data?.id})`); @@ -178,6 +229,19 @@ async function sendEmail(options: SendEmailOptions): Promise { error instanceof Error ? error.message : "Unknown error"; const errorStack = error instanceof Error ? error.stack : undefined; + // Add breadcrumb for exception + await Sentry.addBreadcrumb({ + category: "email.service", + message: "Exception thrown while sending email", + level: "error", + data: { + emailType: type, + recipient: to, + errorMessage, + errorType: error instanceof Error ? error.name : "Unknown", + }, + }); + console.error(`Error sending ${type} email:`, { errorMessage, errorStack, From 16a897c48538e4805e4df59f58fe9230d29327b3 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 16:13:56 -0500 Subject: [PATCH 05/41] feat(auth): add comprehensive observability to authentication flows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add complete tracking and security logging throughout authentication endpoints to prevent silent failures and enable monitoring. Login flow enhancements: - Wrap in Sentry span for performance tracking - Add breadcrumbs for login attempts and results - Add security audit logging for successful and failed logins - Add metrics for monitoring login success rates and duration - Use extractHeaders() utility for consistency Password operation security: - Add security audit logging to password change endpoint - Query verification token before reset to capture userId - Add security audit logging to password reset completion - Non-blocking audit logging to prevent operation failures All audit logging wrapped in try/catch to ensure authentication operations succeed even if logging fails. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/routers/auth.ts | 426 ++++++++++++++++++++++++++----- 1 file changed, 361 insertions(+), 65 deletions(-) diff --git a/packages/api/src/routers/auth.ts b/packages/api/src/routers/auth.ts index 2e246378..2ec9947a 100644 --- a/packages/api/src/routers/auth.ts +++ b/packages/api/src/routers/auth.ts @@ -421,78 +421,253 @@ export const authRouter = router({ }) ) .mutation(async ({ ctx, input }) => { - const auth = createAuth(ctx.env, ctx.db); - - // Convert headers for Better Auth - const authHeaders = - ctx.req.headers instanceof Headers - ? ctx.req.headers - : fromNodeHeaders( - Object.fromEntries( - Object.entries(ctx.req.headers || {}).map(([k, v]) => [ - k, - Array.isArray(v) ? v[0] : v, - ]) - ) as Record - ); - - try { - // Use Better Auth's signIn with username plugin - // Note: Username plugin adds signInUsername method - const result: SignInUsernameResult = await auth.api.signInUsername({ - body: { - username: input.username, - password: input.password, + // Wrap entire login in Sentry span + return Sentry.startSpan( + { + name: "auth.login", + op: "auth.signin", + attributes: { + "auth.method": "username_password", + "auth.username": input.username, }, - headers: authHeaders, - }); + }, + async (parentSpan) => { + const startTime = Date.now(); + let userId: number | undefined; - if (!result || !result.user) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Invalid credentials", + // Add breadcrumb for login attempt + await Sentry.addBreadcrumb({ + category: "auth", + message: "Login attempt", + level: "info", + data: { + username: input.username, + method: "username_password", + }, }); - } - const resultUser = result.user as Partial; + const auth = createAuth(ctx.env, ctx.db); + + // Convert headers for Better Auth + const authHeaders = + ctx.req.headers instanceof Headers + ? ctx.req.headers + : fromNodeHeaders( + Object.fromEntries( + Object.entries(ctx.req.headers || {}).map(([k, v]) => [ + k, + Array.isArray(v) ? v[0] : v, + ]) + ) as Record + ); - // Get user from Better Auth user table for role/plan info - const [dbUser] = await ctx.db - .select() - .from(schema.user) - .where(eq(schema.user.id, Number(result.user.id))) - .limit(1); + try { + // Use Better Auth's signIn with username plugin + // Note: Username plugin adds signInUsername method + const result: SignInUsernameResult = await auth.api.signInUsername({ + body: { + username: input.username, + password: input.password, + }, + headers: authHeaders, + }); - if (!dbUser) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "User not found", - }); - } + if (!result || !result.user) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Invalid credentials", + }); + } - // Return user info (session is handled by Better Auth via cookies) - return { - user: { - id: Number(result.user.id), - username: - (resultUser.username as string | undefined) || - result.user.name || - "", - email: result.user.email, - role: (dbUser.role as "user" | "admin") || "user", - plan: dbUser.plan || DEFAULT_USER_PLAN, - banned: dbUser.banned || false, - }, - }; - } catch (error) { - // Better Auth errors are already logged - const authError = error as { status?: number; message?: string }; - throw new TRPCError({ - code: - authError.status === 401 ? "UNAUTHORIZED" : "INTERNAL_SERVER_ERROR", - message: authError.message || "Login failed", - }); - } + userId = Number(result.user.id); + const resultUser = result.user as Partial; + + // Get user from Better Auth user table for role/plan info + const [dbUser] = await ctx.db + .select() + .from(schema.user) + .where(eq(schema.user.id, userId)) + .limit(1); + + if (!dbUser) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "User not found", + }); + } + + // Log successful login to security audit + const { + logSecurityEvent, + getClientIp, + getUserAgent, + extractHeaders, + } = await import("@/auth/security"); + + const headers = extractHeaders(ctx.req.headers); + const ipAddress = getClientIp(headers); + const userAgent = getUserAgent(headers); + + try { + await logSecurityEvent(ctx.db, { + userId, + action: "login_success", + ipAddress, + userAgent, + success: true, + }); + } catch (auditError) { + // Don't fail login if audit logging fails + console.error("Failed to log login event:", auditError); + await Sentry.captureException(auditError, { + tags: { + flow: "login", + step: "audit_log", + }, + level: "warning", + }); + } + + // Add success breadcrumb + await Sentry.addBreadcrumb({ + category: "auth", + message: "Login successful", + level: "info", + data: { + userId: userId.toString(), + username: input.username, + role: dbUser.role, + }, + }); + + const totalDuration = Date.now() - startTime; + + // Set success attributes on span + parentSpan?.setAttributes({ + "auth.login_success": true, + "auth.user_id": userId.toString(), + "auth.role": dbUser.role || "user", + "auth.duration_ms": totalDuration, + }); + + // Emit metrics + emitMetrics([ + { + type: "counter", + name: "auth.login_success", + value: 1, + attributes: { + role: dbUser.role || "user", + }, + }, + { + type: "distribution", + name: "auth.login_duration", + value: totalDuration, + unit: "millisecond", + }, + ]); + + // Return user info (session is handled by Better Auth via cookies) + return { + user: { + id: userId, + username: + (resultUser.username as string | undefined) || + result.user.name || + "", + email: result.user.email, + role: (dbUser.role as "user" | "admin") || "user", + plan: dbUser.plan || DEFAULT_USER_PLAN, + banned: dbUser.banned || false, + }, + }; + } catch (error) { + const totalDuration = Date.now() - startTime; + + // Add failure breadcrumb + await Sentry.addBreadcrumb({ + category: "auth", + message: "Login failed", + level: "error", + data: { + username: input.username, + error: (error as Error).message, + errorType: (error as Error).constructor.name, + }, + }); + + // Log failed login attempt to security audit (if not a generic auth error) + const { + logSecurityEvent, + getClientIp, + getUserAgent, + extractHeaders, + } = await import("@/auth/security"); + + const headers = extractHeaders(ctx.req.headers); + const ipAddress = getClientIp(headers); + const userAgent = getUserAgent(headers); + + try { + await logSecurityEvent(ctx.db, { + userId, + action: "login_failed", + ipAddress, + userAgent, + success: false, + metadata: { + username: input.username, + error: (error as Error).message, + }, + }); + } catch (auditError) { + // Don't fail login if audit logging fails + console.error("Failed to log failed login event:", auditError); + } + + // Set error attributes on span + parentSpan?.setAttributes({ + "auth.login_success": false, + "auth.error": (error as Error).message, + "auth.error_code": (error as TRPCError).code || "unknown", + "auth.duration_ms": totalDuration, + }); + + // Capture in Sentry + await Sentry.captureException(error, { + tags: { + flow: "login", + step: "overall", + }, + contexts: { + login: { + username: input.username, + user_id: userId?.toString(), + duration_ms: totalDuration, + }, + }, + user: userId ? { id: userId.toString() } : undefined, + }); + + // Emit failure metrics + emitCounter("auth.login_failed", 1, { + error_code: (error as TRPCError).code || "unknown", + error_type: (error as Error).constructor.name, + }); + + // Better Auth errors are already logged + const authError = error as { status?: number; message?: string }; + throw new TRPCError({ + code: + authError.status === 401 + ? "UNAUTHORIZED" + : "INTERNAL_SERVER_ERROR", + message: authError.message || "Login failed", + }); + } + } + ); }), /** @@ -689,6 +864,8 @@ export const authRouter = router({ .output(z.object({ success: z.boolean() })) .mutation(async ({ ctx, input }) => { const auth = createAuth(ctx.env, ctx.db); + const { logSecurityEvent, getClientIp, getUserAgent, extractHeaders } = + await import("@/auth/security"); // Convert headers for Better Auth const authHeaders = @@ -703,6 +880,11 @@ export const authRouter = router({ ) as Record ); + // Extract IP and user agent for audit logging + const headers = extractHeaders(ctx.req.headers); + const ipAddress = getClientIp(headers); + const userAgent = getUserAgent(headers); + try { await auth.api.changePassword({ body: { @@ -712,8 +894,48 @@ export const authRouter = router({ headers: authHeaders, }); + // Log successful password change + try { + await logSecurityEvent(ctx.db, { + userId: ctx.user.userId, + action: "password_change", + ipAddress, + userAgent, + success: true, + }); + } catch (auditError) { + // Don't fail password change if audit logging fails + console.error("Failed to log password change event:", auditError); + await Sentry.captureException(auditError, { + tags: { + flow: "password_change", + step: "audit_log", + }, + level: "warning", + }); + } + return { success: true }; } catch (error) { + // Log failed password change attempt + try { + await logSecurityEvent(ctx.db, { + userId: ctx.user.userId, + action: "password_change", + ipAddress, + userAgent, + success: false, + metadata: { + error: (error as Error).message, + }, + }); + } catch (auditError) { + console.error( + "Failed to log failed password change event:", + auditError + ); + } + const authError = error as { status?: number; message?: string }; throw new TRPCError({ code: @@ -836,6 +1058,8 @@ export const authRouter = router({ .output(z.object({ success: z.boolean() })) .mutation(async ({ ctx, input }) => { const auth = createAuth(ctx.env, ctx.db); + const { logSecurityEvent, getClientIp, getUserAgent, extractHeaders } = + await import("@/auth/security"); // Convert headers for Better Auth const authHeaders = @@ -850,6 +1074,37 @@ export const authRouter = router({ ) as Record ); + // Extract IP and user agent for audit logging + const headers = extractHeaders(ctx.req.headers); + const ipAddress = getClientIp(headers); + const userAgent = getUserAgent(headers); + + // Find user by verification token BEFORE resetting (token is deleted after use) + let userId: number | undefined; + try { + const [verification] = await ctx.db + .select() + .from(schema.verification) + .where(eq(schema.verification.value, input.token)) + .limit(1); + + if (verification) { + // identifier is the user's email + const [userRecord] = await ctx.db + .select() + .from(schema.user) + .where(eq(schema.user.email, verification.identifier)) + .limit(1); + + if (userRecord) { + userId = Number(userRecord.id); + } + } + } catch (error) { + // Continue even if we can't find the user + console.error("Failed to find user for password reset logging:", error); + } + try { await auth.api.resetPassword({ body: { @@ -859,8 +1114,49 @@ export const authRouter = router({ headers: authHeaders, }); + // Log successful password reset with actual user ID + try { + await logSecurityEvent(ctx.db, { + userId, + action: "password_reset_success", + ipAddress, + userAgent, + success: true, + }); + } catch (auditError) { + // Don't fail password reset if audit logging fails + console.error("Failed to log password reset event:", auditError); + await Sentry.captureException(auditError, { + tags: { + flow: "password_reset", + step: "audit_log", + }, + level: "warning", + }); + } + return { success: true }; } catch (error) { + // Log failed password reset attempt + try { + await logSecurityEvent(ctx.db, { + userId, + action: "password_reset_success", + ipAddress, + userAgent, + success: false, + metadata: { + error: (error as Error).message, + note: "Invalid or expired token", + }, + }); + } catch (auditError) { + console.error( + "Failed to log failed password reset event:", + auditError + ); + } + const authError = error as { status?: number; message?: string }; throw new TRPCError({ code: From 567a1845ab4dd647e66a7b0d4e7e7355e06dfcbd Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 19:58:00 -0500 Subject: [PATCH 06/41] feat(auth): add Sentry spans to fire-and-forget email operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap verification and welcome email operations in Sentry spans to track success/failure even though emails are sent in a fire-and-forget pattern. - Add span tracking for verification email with status codes - Add span tracking for welcome email with status codes - Track email success/failure as span attributes - Maintain existing breadcrumbs for debugging - Preserve fire-and-forget behavior (no await on email send) This provides visibility into email delivery without blocking user registration/verification flows. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/auth/better-auth.ts | 481 ++++++++++++++++----------- 1 file changed, 280 insertions(+), 201 deletions(-) diff --git a/packages/api/src/auth/better-auth.ts b/packages/api/src/auth/better-auth.ts index d7383bce..4cf80a7e 100644 --- a/packages/api/src/auth/better-auth.ts +++ b/packages/api/src/auth/better-auth.ts @@ -348,50 +348,132 @@ export function createAuth(env: Env, db?: ReturnType) { // This ensures proper logo loading and post-verification redirect const frontendVerificationUrl = `${frontendUrl}/verify-email?token=${token}`; - // Add breadcrumb for email sending attempt - Sentry.addBreadcrumb({ - category: "email", - message: "Sending verification email", - level: "info", - data: { - userEmail: user.email, - userId: user.id, - emailType: "verification", + // Send verification email (fire-and-forget with Sentry tracking) + // Wrap in Sentry span for monitoring even though we don't await + Sentry.startSpan( + { + op: "email.verification", + name: "Send Verification Email (Fire-and-Forget)", + attributes: { + user_id: user.id, + user_email: user.email, + fire_and_forget: true, + }, }, - }); + async (span) => { + // Add breadcrumb for email sending attempt + await Sentry.addBreadcrumb({ + category: "email", + message: "Sending verification email", + level: "info", + data: { + userEmail: user.email, + userId: user.id, + emailType: "verification", + }, + }); - // Send verification email (fire-and-forget) - sendVerificationEmail(env, { - to: user.email, - username: - (userWithPlugins.username as string | undefined) || - user.name || - "User", - verificationToken: token, - verificationUrl: frontendVerificationUrl, // Frontend URL instead of backend URL - }) - .then((emailResult) => { - // Check if email sending failed - if (!emailResult.success) { - // Add breadcrumb for failure - Sentry.addBreadcrumb({ - category: "email", - message: "Verification email failed", - level: "error", - data: { - userEmail: user.email, - userId: user.id, - error: emailResult.error, - emailType: "verification", - }, - }); + sendVerificationEmail(env, { + to: user.email, + username: + (userWithPlugins.username as string | undefined) || + user.name || + "User", + verificationToken: token, + verificationUrl: frontendVerificationUrl, // Frontend URL instead of backend URL + }) + .then(async (emailResult) => { + // Track result in span + span?.setAttribute("email.success", emailResult.success); + + // Check if email sending failed + if (!emailResult.success) { + span?.setAttribute( + "email.error", + emailResult.error || "Unknown error" + ); + span?.setStatus({ code: 2, message: "email failed" }); - // Log critical email failures to Sentry - Sentry.captureException( - new Error( - emailResult.error || "Failed to send verification email" - ), - { + // Add breadcrumb for failure + await Sentry.addBreadcrumb({ + category: "email", + message: "Verification email failed", + level: "error", + data: { + userEmail: user.email, + userId: user.id, + error: emailResult.error, + emailType: "verification", + }, + }); + + // Log critical email failures to Sentry + await Sentry.captureException( + new Error( + emailResult.error || + "Failed to send verification email" + ), + { + tags: { + component: "better-auth", + operation: "email-verification", + email_type: "verification", + }, + extra: { + userEmail: user.email, + userId: user.id, + errorMessage: emailResult.error, + }, + level: "error", + } + ); + + console.error("Failed to send verification email:", { + userEmail: user.email, + userId: user.id, + error: emailResult.error, + }); + } else { + span?.setStatus({ code: 1, message: "ok" }); + + // Add breadcrumb for success + await Sentry.addBreadcrumb({ + category: "email", + message: "Verification email sent successfully", + level: "info", + data: { + userEmail: user.email, + userId: user.id, + emailType: "verification", + }, + }); + } + }) + .catch(async (error) => { + span?.setAttribute( + "email.exception", + error instanceof Error ? error.message : String(error) + ); + span?.setStatus({ code: 2, message: "exception" }); + + // Add breadcrumb for unexpected error + await Sentry.addBreadcrumb({ + category: "email", + message: "Unexpected error sending verification email", + level: "error", + data: { + userEmail: user.email, + userId: user.id, + error: + error instanceof Error + ? error.message + : String(error), + emailType: "verification", + }, + }); + + // Log unexpected exceptions (e.g., network errors, timeouts) + await Sentry.captureException(error, { tags: { component: "better-auth", operation: "email-verification", @@ -400,66 +482,24 @@ export function createAuth(env: Env, db?: ReturnType) { extra: { userEmail: user.email, userId: user.id, - errorMessage: emailResult.error, }, level: "error", - } - ); + }); - console.error("Failed to send verification email:", { - userEmail: user.email, - userId: user.id, - error: emailResult.error, - }); - } else { - // Add breadcrumb for success - Sentry.addBreadcrumb({ - category: "email", - message: "Verification email sent successfully", - level: "info", - data: { - userEmail: user.email, - userId: user.id, - emailType: "verification", - }, + console.error( + "Unexpected error sending verification email:", + { + userEmail: user.email, + userId: user.id, + error: + error instanceof Error + ? error.message + : String(error), + } + ); }); - } - }) - .catch((error) => { - // Add breadcrumb for unexpected error - Sentry.addBreadcrumb({ - category: "email", - message: "Unexpected error sending verification email", - level: "error", - data: { - userEmail: user.email, - userId: user.id, - error: - error instanceof Error ? error.message : String(error), - emailType: "verification", - }, - }); - - // Log unexpected exceptions (e.g., network errors, timeouts) - Sentry.captureException(error, { - tags: { - component: "better-auth", - operation: "email-verification", - email_type: "verification", - }, - extra: { - userEmail: user.email, - userId: user.id, - }, - level: "error", - }); - - console.error("Unexpected error sending verification email:", { - userEmail: user.email, - userId: user.id, - error: error instanceof Error ? error.message : String(error), - }); - }); + } + ); }) .catch(async (error) => { console.error( @@ -540,49 +580,139 @@ export function createAuth(env: Env, db?: ReturnType) { const appUrl = frontendUrl; const userWithPlugins = user as BetterAuthUser; - // Add breadcrumb for email sending attempt - Sentry.addBreadcrumb({ - category: "email", - message: "Sending welcome email", - level: "info", - data: { - userEmail: user.email, - userId: user.id, - emailType: "welcome", + // Send welcome email (fire-and-forget with Sentry tracking) + Sentry.startSpan( + { + op: "email.welcome", + name: "Send Welcome Email (Fire-and-Forget)", + attributes: { + user_id: user.id, + user_email: user.email, + fire_and_forget: true, + }, }, - }); + async (span) => { + // Add breadcrumb for email sending attempt + await Sentry.addBreadcrumb({ + category: "email", + message: "Sending welcome email", + level: "info", + data: { + userEmail: user.email, + userId: user.id, + emailType: "welcome", + }, + }); - // Send welcome email (fire-and-forget) - sendWelcomeEmail(env, { - to: user.email, - username: - (userWithPlugins.username as string | undefined) || - user.name || - "User", - appUrl, - }) - .then((emailResult) => { - // Check if email sending failed - if (!emailResult.success) { - // Add breadcrumb for failure - Sentry.addBreadcrumb({ - category: "email", - message: "Welcome email failed", - level: "error", - data: { - userEmail: user.email, - userId: user.id, - error: emailResult.error, - emailType: "welcome", - }, - }); + sendWelcomeEmail(env, { + to: user.email, + username: + (userWithPlugins.username as string | undefined) || + user.name || + "User", + appUrl, + }) + .then(async (emailResult) => { + // Track result in span + span?.setAttribute( + "email.success", + emailResult.success + ); + + // Check if email sending failed + if (!emailResult.success) { + span?.setAttribute( + "email.error", + emailResult.error || "Unknown error" + ); + span?.setStatus({ + code: 2, + message: "email failed", + }); + + // Add breadcrumb for failure + await Sentry.addBreadcrumb({ + category: "email", + message: "Welcome email failed", + level: "error", + data: { + userEmail: user.email, + userId: user.id, + error: emailResult.error, + emailType: "welcome", + }, + }); + + // Log email failures to Sentry + await Sentry.captureException( + new Error( + emailResult.error || + "Failed to send welcome email" + ), + { + tags: { + component: "better-auth", + operation: "welcome-email", + email_type: "welcome", + }, + extra: { + userEmail: user.email, + userId: user.id, + errorMessage: emailResult.error, + }, + level: "error", + } + ); + + console.error( + `Failed to send welcome email to ${user.email}:`, + { + error: emailResult.error, + } + ); + } else { + span?.setStatus({ code: 1, message: "ok" }); + + // Add breadcrumb for success + await Sentry.addBreadcrumb({ + category: "email", + message: "Welcome email sent successfully", + level: "info", + data: { + userEmail: user.email, + userId: user.id, + emailType: "welcome", + }, + }); + } + }) + .catch(async (error) => { + span?.setAttribute( + "email.exception", + error instanceof Error + ? error.message + : String(error) + ); + span?.setStatus({ code: 2, message: "exception" }); + + // Add breadcrumb for unexpected error + await Sentry.addBreadcrumb({ + category: "email", + message: "Unexpected error sending welcome email", + level: "error", + data: { + userEmail: user.email, + userId: user.id, + error: + error instanceof Error + ? error.message + : String(error), + emailType: "welcome", + }, + }); - // Log email failures to Sentry - Sentry.captureException( - new Error( - emailResult.error || "Failed to send welcome email" - ), - { + // Log unexpected exceptions (e.g., network errors, timeouts) + await Sentry.captureException(error, { tags: { component: "better-auth", operation: "welcome-email", @@ -591,73 +721,22 @@ export function createAuth(env: Env, db?: ReturnType) { extra: { userEmail: user.email, userId: user.id, - errorMessage: emailResult.error, }, level: "error", - } - ); - - console.error( - `Failed to send welcome email to ${user.email}:`, - { - error: emailResult.error, - } - ); - } else { - // Add breadcrumb for success - Sentry.addBreadcrumb({ - category: "email", - message: "Welcome email sent successfully", - level: "info", - data: { - userEmail: user.email, - userId: user.id, - emailType: "welcome", - }, + }); + + console.error( + `Unexpected error sending welcome email to ${user.email}:`, + { + error: + error instanceof Error + ? error.message + : String(error), + } + ); }); - } - }) - .catch((error) => { - // Add breadcrumb for unexpected error - Sentry.addBreadcrumb({ - category: "email", - message: "Unexpected error sending welcome email", - level: "error", - data: { - userEmail: user.email, - userId: user.id, - error: - error instanceof Error - ? error.message - : String(error), - emailType: "welcome", - }, - }); - - // Log unexpected exceptions (e.g., network errors, timeouts) - Sentry.captureException(error, { - tags: { - component: "better-auth", - operation: "welcome-email", - email_type: "welcome", - }, - extra: { - userEmail: user.email, - userId: user.id, - }, - level: "error", - }); - - console.error( - `Unexpected error sending welcome email to ${user.email}:`, - { - error: - error instanceof Error - ? error.message - : String(error), - } - ); - }); + } + ); } }) .catch(async (error) => { From a99803d860c08c90833e626c39a1d34f89d134f1 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 19:58:11 -0500 Subject: [PATCH 07/41] feat(articles): add Sentry monitoring to database batch operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive Sentry span tracking around batch database operations to monitor performance and catch failures. - Wrap mark articles read/unread batch in Sentry span - Wrap mark all articles read batch in Sentry span - Track batch size, operation type, and user ID as attributes - Capture exceptions with full context (batch size, user, article count) - Set appropriate span status codes for success/failure Batch operations are critical for user experience - this ensures we're alerted to failures and can identify performance patterns. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/routers/articles.ts | 67 +++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/packages/api/src/routers/articles.ts b/packages/api/src/routers/articles.ts index f9e7598e..2311f744 100644 --- a/packages/api/src/routers/articles.ts +++ b/packages/api/src/routers/articles.ts @@ -812,7 +812,38 @@ export const articlesRouter = router({ }) ); - await executeBatch(ctx.db, statements); + // Wrap batch execution in Sentry span for monitoring + await Sentry.startSpan( + { + op: "db.batch", + name: "Mark Articles Read/Unread", + attributes: { + "db.batch_size": statements.length, + "db.operation": input.read ? "mark_read" : "mark_unread", + "db.user_id": userId, + }, + }, + async (span) => { + try { + await executeBatch(ctx.db, statements); + span.setStatus({ code: 1, message: "ok" }); + } catch (error) { + span.setStatus({ code: 2, message: "batch failed" }); + await Sentry.captureException(error, { + tags: { + operation: "mark_articles_read", + batch_size: statements.length.toString(), + }, + extra: { + userId, + articleCount: input.articleIds.length, + read: input.read, + }, + }); + throw error; + } + } + ); return { updated: input.articleIds.length }; }), @@ -914,7 +945,39 @@ export const articlesRouter = router({ }) ); - await executeBatch(ctx.db, statements); + // Wrap batch execution in Sentry span for monitoring + await Sentry.startSpan( + { + op: "db.batch", + name: "Mark All Articles Read", + attributes: { + "db.batch_size": statements.length, + "db.operation": "mark_all_read", + "db.user_id": userId, + "filter.older_than_days": input.olderThanDays, + }, + }, + async (span) => { + try { + await executeBatch(ctx.db, statements); + span.setStatus({ code: 1, message: "ok" }); + } catch (error) { + span.setStatus({ code: 2, message: "batch failed" }); + await Sentry.captureException(error, { + tags: { + operation: "mark_all_read", + batch_size: statements.length.toString(), + }, + extra: { + userId, + articleCount: articleIds.length, + olderThanDays: input.olderThanDays, + }, + }); + throw error; + } + } + ); return { updated: articleIds.length }; }), From 61f112c806eeaedf3b9bd3617035c70c87fb4284 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 19:58:22 -0500 Subject: [PATCH 08/41] feat(rss): add monitoring for OpenGraph image fetching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Sentry span tracking for OpenGraph image extraction with metrics emission for pattern analysis. - Wrap OG image fetch in Sentry span with domain tracking - Track success (found/not found) and failures separately - Emit metrics counters for error patterns (by domain and error type) - Set appropriate span status for found/not found/error cases - Avoid spamming Sentry with every failure (use metrics instead) This allows us to identify problematic domains and error patterns without creating excessive noise in Sentry. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/services/rss-fetcher.ts | 42 ++++++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/api/src/services/rss-fetcher.ts b/packages/api/src/services/rss-fetcher.ts index b78690e5..40362608 100644 --- a/packages/api/src/services/rss-fetcher.ts +++ b/packages/api/src/services/rss-fetcher.ts @@ -870,14 +870,42 @@ async function extractArticleData( // Final fallback: OpenGraph image from article URL if (!imageUrl && link) { - try { - const ogImage = await extractOgImage(link); - if (ogImage) { - imageUrl = ogImage; + await Sentry.startSpan( + { + op: "http.client", + name: "Fetch OpenGraph Image", + attributes: { + "http.url": link, + "og.domain": extractDomain(link), + }, + }, + async (span) => { + try { + const ogImage = await extractOgImage(link); + if (ogImage) { + imageUrl = ogImage; + span.setAttribute("og.found", true); + span.setStatus({ code: 1, message: "ok" }); + } else { + span.setAttribute("og.found", false); + span.setStatus({ code: 1, message: "no image found" }); + } + } catch (error) { + // Track OG fetch failures for pattern analysis + span.setAttribute( + "og.error", + error instanceof Error ? error.message : String(error) + ); + span.setStatus({ code: 2, message: "fetch failed" }); + + // Don't spam Sentry with every OG failure, but track metrics + emitCounter("og_image.fetch_error", 1, { + domain: extractDomain(link), + error_type: error instanceof Error ? error.name : "unknown", + }); + } } - } catch (error) { - // Silently ignore OG fetch errors - } + ); } // Audio URL from enclosures (for podcasts) From 75b246144d06ad76f62461fe7d99de1c610d1110 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 19:58:36 -0500 Subject: [PATCH 09/41] feat(subscriptions): add retry logic and comprehensive OPML monitoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement automatic retry for transient feed fetch failures and add full transaction monitoring for OPML import operations. Subscription Creation & Preview Enhancements: - Add automatic retry logic for HTTP 502, 503, 504, 429 errors - Implement exponential backoff (1s, 2s delays) with max 2 retries - Track retry attempts in Sentry breadcrumbs and error context - Enhanced error tagging with domain and HTTP status codes - Track attempt count and last status code for debugging OPML Import Transaction Monitoring: - Wrap entire import in Sentry transaction span - Create individual spans for each feed import - Track feed-level attributes (URL, title, domain, filters, categories) - Calculate and report success rate as span attribute - Proper error boundaries with per-feed exception capture - Handle limit reached and already subscribed cases gracefully - Fix loop control flow to work within Sentry span callbacks Benefits: - Improved resilience against transient failures (network, server) - Clear visibility into which feeds fail and why during bulk imports - Domain-based error pattern analysis for subscription issues - Success rate tracking for OPML import operations ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/routers/subscriptions.ts | 840 ++++++++++++++-------- 1 file changed, 559 insertions(+), 281 deletions(-) diff --git a/packages/api/src/routers/subscriptions.ts b/packages/api/src/routers/subscriptions.ts index eebff322..9fbc5e31 100644 --- a/packages/api/src/routers/subscriptions.ts +++ b/packages/api/src/routers/subscriptions.ts @@ -302,61 +302,151 @@ export const subscriptionsRouter = router({ let feedData; let feedContent: string | undefined; - try { - await Sentry.addBreadcrumb({ - category: "subscription", - message: `Fetching feed from ${feedUrl}`, - level: "info", - }); + // Retry logic for transient failures + const MAX_RETRIES = 2; + const RETRY_DELAY_MS = 1000; + const TRANSIENT_STATUS_CODES = [502, 503, 504, 429]; - const response = await fetch(feedUrl, { - headers: { - "User-Agent": "TuvixRSS/1.0", - Accept: - "application/rss+xml, application/atom+xml, application/xml, text/xml, */*", - }, - signal: AbortSignal.timeout(30000), - }); + let lastError: Error | undefined; + let lastStatusCode: number | undefined; - if (!response.ok) { - const errorMessage = `HTTP ${response.status}: ${response.statusText}`; - // Don't capture here - let the catch block handle it to avoid duplicates - throw new Error(errorMessage); - } + for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { + try { + if (attempt > 0) { + // Add delay between retries with exponential backoff + const delay = RETRY_DELAY_MS * Math.pow(2, attempt - 1); + await new Promise((resolve) => setTimeout(resolve, delay)); - feedContent = await response.text(); - const result = parseFeed(feedContent); - feedData = result.feed; + await Sentry.addBreadcrumb({ + category: "subscription", + message: `Retrying feed fetch (attempt ${attempt + 1}/${MAX_RETRIES + 1})`, + level: "info", + data: { + attempt: attempt + 1, + delay_ms: delay, + }, + }); + } else { + await Sentry.addBreadcrumb({ + category: "subscription", + message: `Fetching feed from ${feedUrl}`, + level: "info", + }); + } - await Sentry.addBreadcrumb({ - category: "subscription", - message: `Successfully parsed feed as ${result.format}`, - level: "info", - data: { feed_format: result.format }, - }); - } catch (error) { - // Extract HTTP status if this is an HTTP error - const errorMessage = error instanceof Error ? error.message : "Unknown"; - const httpStatusMatch = errorMessage.match(/HTTP (\d+):/); - const httpStatus = httpStatusMatch ? httpStatusMatch[1] : undefined; + const response = await fetch(feedUrl, { + headers: { + "User-Agent": "TuvixRSS/1.0", + Accept: + "application/rss+xml, application/atom+xml, application/xml, text/xml, */*", + }, + signal: AbortSignal.timeout(30000), + }); - await Sentry.captureException(error, { - level: "error", - tags: { - operation: "subscription_feed_parse", - domain: domain || "unknown", - ...(httpStatus && { http_status: httpStatus }), - }, - extra: { - url: input.url, - user_id: userId, - error_message: errorMessage, - }, - }); + lastStatusCode = response.status; + + if (!response.ok) { + const errorMessage = `HTTP ${response.status}: ${response.statusText}`; + + // Check if this is a transient error that should be retried + if ( + TRANSIENT_STATUS_CODES.includes(response.status) && + attempt < MAX_RETRIES + ) { + lastError = new Error(errorMessage); + continue; // Retry + } + + // Non-transient error or max retries reached + throw new Error(errorMessage); + } + + feedContent = await response.text(); + const result = parseFeed(feedContent); + feedData = result.feed; + + await Sentry.addBreadcrumb({ + category: "subscription", + message: `Successfully parsed feed as ${result.format}`, + level: "info", + data: { + feed_format: result.format, + attempts: attempt + 1, + }, + }); + + // Success - break out of retry loop + break; + } catch (error) { + lastError = error instanceof Error ? error : new Error(String(error)); + + // If this is the last attempt or not a transient error, throw + if (attempt === MAX_RETRIES) { + // Extract HTTP status if this is an HTTP error + const errorMessage = lastError.message; + const httpStatusMatch = errorMessage.match(/HTTP (\d+):/); + const httpStatus = httpStatusMatch + ? httpStatusMatch[1] + : lastStatusCode?.toString(); + + await Sentry.captureException(lastError, { + level: "error", + tags: { + operation: "subscription_feed_parse", + domain: domain || "unknown", + ...(httpStatus && { http_status: httpStatus }), + attempts: (attempt + 1).toString(), + }, + extra: { + url: input.url, + user_id: userId, + error_message: errorMessage, + final_attempt: attempt + 1, + last_status_code: lastStatusCode, + }, + }); + throw new TRPCError({ + code: "BAD_REQUEST", + message: `Failed to fetch or parse feed: ${errorMessage}`, + }); + } + + // Check if we should retry this error + const errorMessage = lastError.message; + const httpStatusMatch = errorMessage.match(/HTTP (\d+):/); + const httpStatus = httpStatusMatch ? parseInt(httpStatusMatch[1]) : 0; + + if (!TRANSIENT_STATUS_CODES.includes(httpStatus)) { + // Non-transient error - don't retry + await Sentry.captureException(lastError, { + level: "error", + tags: { + operation: "subscription_feed_parse", + domain: domain || "unknown", + http_status: httpStatus.toString(), + attempts: (attempt + 1).toString(), + }, + extra: { + url: input.url, + user_id: userId, + error_message: errorMessage, + }, + }); + + throw new TRPCError({ + code: "BAD_REQUEST", + message: `Failed to fetch or parse feed: ${errorMessage}`, + }); + } + } + } + + // If we get here, feedData should be defined from successful parse + if (!feedData) { throw new TRPCError({ - code: "BAD_REQUEST", - message: `Failed to fetch or parse feed: ${errorMessage}`, + code: "INTERNAL_SERVER_ERROR", + message: "Feed parsing failed unexpectedly", }); } @@ -870,54 +960,143 @@ export const subscriptionsRouter = router({ let feedUrl = input.url; let feedContent: string | undefined; - try { - const response = await fetch(feedUrl, { - headers: { - "User-Agent": "TuvixRSS/1.0", - Accept: - "application/rss+xml, application/atom+xml, application/xml, text/xml, */*", - }, - signal: AbortSignal.timeout(30000), - }); + // Retry logic for transient failures + const MAX_RETRIES = 2; + const RETRY_DELAY_MS = 1000; + const TRANSIENT_STATUS_CODES = [502, 503, 504, 429]; - if (!response.ok) { - const errorMessage = `HTTP ${response.status}: ${response.statusText}`; - // Don't capture here - let the catch block handle it to avoid duplicates - throw new Error(errorMessage); - } + let lastError: Error | undefined; + let lastStatusCode: number | undefined; - feedContent = await response.text(); - const result = parseFeed(feedContent); - feedData = result.feed; + for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { + try { + if (attempt > 0) { + // Add delay between retries with exponential backoff + const delay = RETRY_DELAY_MS * Math.pow(2, attempt - 1); + await new Promise((resolve) => setTimeout(resolve, delay)); - await Sentry.addBreadcrumb({ - category: "subscription", - message: `Preview parsed feed as ${result.format}`, - level: "info", - data: { feed_format: result.format }, - }); - } catch (error) { - // Extract HTTP status if this is an HTTP error - const errorMessage = error instanceof Error ? error.message : "Unknown"; - const httpStatusMatch = errorMessage.match(/HTTP (\d+):/); - const httpStatus = httpStatusMatch ? httpStatusMatch[1] : undefined; + await Sentry.addBreadcrumb({ + category: "subscription", + message: `Retrying feed preview (attempt ${attempt + 1}/${MAX_RETRIES + 1})`, + level: "info", + data: { + attempt: attempt + 1, + delay_ms: delay, + }, + }); + } - await Sentry.captureException(error, { - level: "error", - tags: { - operation: "subscription_preview_parse", - domain: domain || "unknown", - ...(httpStatus && { http_status: httpStatus }), - }, - extra: { - url: input.url, - error_message: errorMessage, - }, - }); + const response = await fetch(feedUrl, { + headers: { + "User-Agent": "TuvixRSS/1.0", + Accept: + "application/rss+xml, application/atom+xml, application/xml, text/xml, */*", + }, + signal: AbortSignal.timeout(30000), + }); + + lastStatusCode = response.status; + + if (!response.ok) { + const errorMessage = `HTTP ${response.status}: ${response.statusText}`; + + // Check if this is a transient error that should be retried + if ( + TRANSIENT_STATUS_CODES.includes(response.status) && + attempt < MAX_RETRIES + ) { + lastError = new Error(errorMessage); + continue; // Retry + } + + // Non-transient error or max retries reached + throw new Error(errorMessage); + } + + feedContent = await response.text(); + const result = parseFeed(feedContent); + feedData = result.feed; + + await Sentry.addBreadcrumb({ + category: "subscription", + message: `Preview parsed feed as ${result.format}`, + level: "info", + data: { + feed_format: result.format, + attempts: attempt + 1, + }, + }); + + // Success - break out of retry loop + break; + } catch (error) { + lastError = error instanceof Error ? error : new Error(String(error)); + + // If this is the last attempt or not a transient error, throw + if (attempt === MAX_RETRIES) { + // Extract HTTP status if this is an HTTP error + const errorMessage = lastError.message; + const httpStatusMatch = errorMessage.match(/HTTP (\d+):/); + const httpStatus = httpStatusMatch + ? httpStatusMatch[1] + : lastStatusCode?.toString(); + + await Sentry.captureException(lastError, { + level: "error", + tags: { + operation: "subscription_preview_parse", + domain: domain || "unknown", + ...(httpStatus && { http_status: httpStatus }), + attempts: (attempt + 1).toString(), + }, + extra: { + url: input.url, + error_message: errorMessage, + final_attempt: attempt + 1, + last_status_code: lastStatusCode, + }, + }); + throw new TRPCError({ + code: "BAD_REQUEST", + message: `Failed to fetch or parse feed: ${errorMessage}`, + }); + } + + // Check if we should retry this error + const errorMessage = lastError.message; + const httpStatusMatch = errorMessage.match(/HTTP (\d+):/); + const httpStatus = httpStatusMatch ? parseInt(httpStatusMatch[1]) : 0; + + if (!TRANSIENT_STATUS_CODES.includes(httpStatus)) { + // Non-transient error - don't retry + await Sentry.captureException(lastError, { + level: "error", + tags: { + operation: "subscription_preview_parse", + domain: domain || "unknown", + http_status: httpStatus.toString(), + attempts: (attempt + 1).toString(), + }, + extra: { + url: input.url, + error_message: errorMessage, + }, + }); + + throw new TRPCError({ + code: "BAD_REQUEST", + message: `Failed to fetch or parse feed: ${errorMessage}`, + }); + } + } + } + + // If we get here, feedData should be defined from successful parse + if (!feedData) { throw new TRPCError({ - code: "BAD_REQUEST", - message: `Failed to fetch or parse feed: ${errorMessage}`, + code: "INTERNAL_SERVER_ERROR", + message: "Feed parsing failed unexpectedly", }); } @@ -1580,210 +1759,309 @@ export const subscriptionsRouter = router({ let errorCount = 0; const errors: { url: string; error: string }[] = []; - // Process each feed - for (const feedInfo of feedsToProcess) { - try { - // Fetch and validate feed - const response = await fetch(feedInfo.url, { - headers: { - "User-Agent": "TuvixRSS/1.0", - Accept: - "application/rss+xml, application/atom+xml, application/xml, text/xml, */*", - }, - signal: AbortSignal.timeout(15000), - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}`); - } - - const feedContent = await response.text(); - const feedResult = parseFeed(feedContent); - const feedData = feedResult.feed; - - // Extract metadata - const feedTitle = - "title" in feedData && feedData.title - ? feedData.title - : feedInfo.title; - const feedDescription = - "description" in feedData && feedData.description - ? stripHtml(feedData.description) - : "subtitle" in feedData && feedData.subtitle - ? stripHtml(feedData.subtitle) - : undefined; - const siteUrl = - "link" in feedData && feedData.link - ? feedData.link - : "links" in feedData && - Array.isArray(feedData.links) && - feedData.links[0]?.href - ? feedData.links[0].href - : undefined; - - // Check if source exists - // Normalize Reddit URLs to prevent duplicates across different domains - const normalizedFeedUrl = normalizeRedditUrl(feedInfo.url); - - const existingSources = await ctx.db - .select() - .from(schema.sources) - .where(eq(schema.sources.url, normalizedFeedUrl)) - .limit(1); - - let sourceId: number; - - if (existingSources.length > 0) { - sourceId = existingSources[0].id; - await ctx.db - .update(schema.sources) - .set({ - title: feedTitle, - description: feedDescription, - siteUrl, - lastFetched: new Date(), - }) - .where(eq(schema.sources.id, sourceId)); - } else { - const newSource = await ctx.db - .insert(schema.sources) - .values({ - url: normalizedFeedUrl, - title: feedTitle, - description: feedDescription, - siteUrl, - iconType: "auto", - lastFetched: new Date(), - }) - .returning(); - sourceId = newSource[0].id; - } - - // Check if already subscribed - const existingSubscription = await ctx.db - .select() - .from(schema.subscriptions) - .where( - and( - eq(schema.subscriptions.userId, userId), - eq(schema.subscriptions.sourceId, sourceId) - ) - ) - .limit(1); + // Wrap entire OPML import in Sentry transaction for monitoring + await Sentry.startSpan( + { + op: "opml.import", + name: "OPML Import", + attributes: { + "opml.total_feeds": feedsToProcess.length, + "opml.user_id": userId, + }, + }, + async (importSpan) => { + // Process each feed + let shouldStopImporting = false; + for (const feedInfo of feedsToProcess) { + if (shouldStopImporting) { + break; // Stop processing remaining feeds if limit reached + } - if (existingSubscription.length > 0) { - // Already subscribed, skip - successCount++; - continue; - } + // Create a span for each feed import + await Sentry.startSpan( + { + op: "opml.import_feed", + name: `Import Feed: ${feedInfo.title}`, + attributes: { + "feed.url": feedInfo.url, + "feed.title": feedInfo.title, + "feed.domain": extractDomain(feedInfo.url) || "unknown", + "feed.has_filters": feedInfo.filters + ? feedInfo.filters.length > 0 + : false, + "feed.categories_count": feedInfo.categories.length, + }, + }, + async (feedSpan) => { + try { + // Fetch and validate feed + const response = await fetch(feedInfo.url, { + headers: { + "User-Agent": "TuvixRSS/1.0", + Accept: + "application/rss+xml, application/atom+xml, application/xml, text/xml, */*", + }, + signal: AbortSignal.timeout(15000), + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } - // Check source limit before creating subscription - const limitCheck = await checkSourceLimit(ctx.db, userId); - if (!limitCheck.allowed) { - // Limit reached, stop importing - errorCount++; - errors.push({ - url: feedInfo.url, - error: `Source limit reached (${limitCheck.limit}/${limitCheck.limit}). Remaining feeds not imported.`, - }); - break; // Stop processing remaining feeds - } + const feedContent = await response.text(); + const feedResult = parseFeed(feedContent); + const feedData = feedResult.feed; + + // Extract metadata + const feedTitle = + "title" in feedData && feedData.title + ? feedData.title + : feedInfo.title; + const feedDescription = + "description" in feedData && feedData.description + ? stripHtml(feedData.description) + : "subtitle" in feedData && feedData.subtitle + ? stripHtml(feedData.subtitle) + : undefined; + const siteUrl = + "link" in feedData && feedData.link + ? feedData.link + : "links" in feedData && + Array.isArray(feedData.links) && + feedData.links[0]?.href + ? feedData.links[0].href + : undefined; + + // Check if source exists + // Normalize Reddit URLs to prevent duplicates across different domains + const normalizedFeedUrl = normalizeRedditUrl(feedInfo.url); + + const existingSources = await ctx.db + .select() + .from(schema.sources) + .where(eq(schema.sources.url, normalizedFeedUrl)) + .limit(1); + + let sourceId: number; + + if (existingSources.length > 0) { + sourceId = existingSources[0].id; + await ctx.db + .update(schema.sources) + .set({ + title: feedTitle, + description: feedDescription, + siteUrl, + lastFetched: new Date(), + }) + .where(eq(schema.sources.id, sourceId)); + } else { + const newSource = await ctx.db + .insert(schema.sources) + .values({ + url: normalizedFeedUrl, + title: feedTitle, + description: feedDescription, + siteUrl, + iconType: "auto", + lastFetched: new Date(), + }) + .returning(); + sourceId = newSource[0].id; + } - // Determine filter settings from OPML data - const filterEnabled = - feedInfo.filterEnabled !== undefined - ? feedInfo.filterEnabled - : feedInfo.filters && feedInfo.filters.length > 0 - ? true - : false; - const filterMode = feedInfo.filterMode || "include"; - - // Create subscription - const newSubscription = await ctx.db - .insert(schema.subscriptions) - .values({ - userId, - sourceId, - customTitle: null, - filterEnabled, - filterMode, - }) - .returning(); - - const subscriptionId = newSubscription[0].id; - - // Create/link categories (using normalization helper to prevent duplicates) - if (feedInfo.categories.length > 0) { - // Track category IDs to prevent duplicate links - const linkedCategoryIds = new Set(); - - for (const categoryName of feedInfo.categories) { - // Use findOrCreateCategory for case-insensitive normalization - const categoryId = await findOrCreateCategory( - ctx.db, - schema.categories, - userId, - categoryName, - generateColorFromString - ); - - // Only link if we haven't already linked this category - if (!linkedCategoryIds.has(categoryId)) { - linkedCategoryIds.add(categoryId); - await ctx.db.insert(schema.subscriptionCategories).values({ - subscriptionId, - categoryId, - }); - } - } - } + // Check if already subscribed + const existingSubscription = await ctx.db + .select() + .from(schema.subscriptions) + .where( + and( + eq(schema.subscriptions.userId, userId), + eq(schema.subscriptions.sourceId, sourceId) + ) + ) + .limit(1); + + if (existingSubscription.length > 0) { + // Already subscribed, skip + successCount++; + + // Mark span as successful (already exists) + feedSpan.setAttribute("feed.status", "already_exists"); + feedSpan.setStatus({ + code: 1, + message: "already subscribed", + }); + return; // Return from span callback (equivalent to continue) + } - // Create filters if provided - if (feedInfo.filters && feedInfo.filters.length > 0) { - for (const filter of feedInfo.filters) { - try { - // Validate regex pattern if matchType is 'regex' - if (filter.matchType === "regex") { - try { - new RegExp(filter.pattern); - } catch (regexError) { - // Invalid regex - skip this filter but continue + // Check source limit before creating subscription + const limitCheck = await checkSourceLimit(ctx.db, userId); + if (!limitCheck.allowed) { + // Limit reached, stop importing + errorCount++; errors.push({ url: feedInfo.url, - error: `Invalid regex pattern in filter: ${regexError instanceof Error ? regexError.message : "Unknown error"}`, + error: `Source limit reached (${limitCheck.limit}/${limitCheck.limit}). Remaining feeds not imported.`, + }); + + // Mark span as failed due to limit + feedSpan.setAttribute("feed.status", "limit_reached"); + feedSpan.setStatus({ + code: 2, + message: "source limit reached", }); - continue; + + shouldStopImporting = true; // Signal to stop processing remaining feeds + return; // Return from span callback + } + + // Determine filter settings from OPML data + const filterEnabled = + feedInfo.filterEnabled !== undefined + ? feedInfo.filterEnabled + : feedInfo.filters && feedInfo.filters.length > 0 + ? true + : false; + const filterMode = feedInfo.filterMode || "include"; + + // Create subscription + const newSubscription = await ctx.db + .insert(schema.subscriptions) + .values({ + userId, + sourceId, + customTitle: null, + filterEnabled, + filterMode, + }) + .returning(); + + const subscriptionId = newSubscription[0].id; + + // Create/link categories (using normalization helper to prevent duplicates) + if (feedInfo.categories.length > 0) { + // Track category IDs to prevent duplicate links + const linkedCategoryIds = new Set(); + + for (const categoryName of feedInfo.categories) { + // Use findOrCreateCategory for case-insensitive normalization + const categoryId = await findOrCreateCategory( + ctx.db, + schema.categories, + userId, + categoryName, + generateColorFromString + ); + + // Only link if we haven't already linked this category + if (!linkedCategoryIds.has(categoryId)) { + linkedCategoryIds.add(categoryId); + await ctx.db + .insert(schema.subscriptionCategories) + .values({ + subscriptionId, + categoryId, + }); + } + } + } + + // Create filters if provided + if (feedInfo.filters && feedInfo.filters.length > 0) { + for (const filter of feedInfo.filters) { + try { + // Validate regex pattern if matchType is 'regex' + if (filter.matchType === "regex") { + try { + new RegExp(filter.pattern); + } catch (regexError) { + // Invalid regex - skip this filter but continue + errors.push({ + url: feedInfo.url, + error: `Invalid regex pattern in filter: ${regexError instanceof Error ? regexError.message : "Unknown error"}`, + }); + continue; + } + } + + // Insert filter directly (more efficient than API call) + await ctx.db.insert(schema.subscriptionFilters).values({ + subscriptionId, + field: filter.field, + matchType: filter.matchType, + pattern: filter.pattern, + caseSensitive: filter.caseSensitive, + }); + } catch (filterError) { + // Filter creation failed - log error but continue + errors.push({ + url: feedInfo.url, + error: `Failed to create filter: ${filterError instanceof Error ? filterError.message : "Unknown error"}`, + }); + // Continue with next filter + } + } } - } - // Insert filter directly (more efficient than API call) - await ctx.db.insert(schema.subscriptionFilters).values({ - subscriptionId, - field: filter.field, - matchType: filter.matchType, - pattern: filter.pattern, - caseSensitive: filter.caseSensitive, - }); - } catch (filterError) { - // Filter creation failed - log error but continue - errors.push({ - url: feedInfo.url, - error: `Failed to create filter: ${filterError instanceof Error ? filterError.message : "Unknown error"}`, - }); - // Continue with next filter + successCount++; + + // Mark span as successful + feedSpan.setAttribute("feed.status", "success"); + feedSpan.setStatus({ code: 1, message: "ok" }); + } catch (error) { + errorCount++; + const errorMessage = + error instanceof Error ? error.message : "Unknown error"; + + errors.push({ + url: feedInfo.url, + error: errorMessage, + }); + + // Mark span as failed + feedSpan.setAttribute("feed.status", "error"); + feedSpan.setAttribute("feed.error", errorMessage); + feedSpan.setStatus({ + code: 2, + message: "feed import failed", + }); + + // Capture exception for this specific feed + await Sentry.captureException(error, { + level: "warning", + tags: { + operation: "opml_import_feed", + domain: extractDomain(feedInfo.url) || "unknown", + }, + extra: { + feed_url: feedInfo.url, + feed_title: feedInfo.title, + user_id: userId, + }, + }); + } } - } + ); } - successCount++; - } catch (error) { - errorCount++; - errors.push({ - url: feedInfo.url, - error: error instanceof Error ? error.message : "Unknown error", - }); + // Set final status on import span + importSpan.setAttribute("opml.success_count", successCount); + importSpan.setAttribute("opml.error_count", errorCount); + importSpan.setAttribute( + "opml.success_rate", + feedsToProcess.length > 0 ? successCount / feedsToProcess.length : 0 + ); + + if (errorCount === 0) { + importSpan.setStatus({ code: 1, message: "ok" }); + } else if (successCount === 0) { + importSpan.setStatus({ code: 2, message: "all feeds failed" }); + } else { + importSpan.setStatus({ code: 1, message: "partial success" }); + } } - } + ); // Recalculate usage stats to ensure accuracy after bulk import await recalculateUsage(ctx.db, userId); From 851f6f2df1d89dbf718cf1a4c44a70149f59c8eb Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 21:39:02 -0500 Subject: [PATCH 10/41] fix: address Copilot review findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix critical bugs identified in code review: 1. Security Audit Logging (HIGH): - Fix incorrect action type for failed password resets - Changed "password_reset_success" to "password_reset_failed" in error handler - Added missing "password_reset_failed" to SecurityAction type 2. Retry Logic Separation (HIGH): - Separate fetch errors from parse errors in retry logic - Parse errors now throw immediately without retry - Only HTTP fetch errors (502, 503, 504, 429) are retried - Improves error classification: "fetch_error" vs "parse_error" - Applied to both create and preview endpoints 3. Header Extraction (MEDIUM): - Fix String(undefined) converting undefined to "undefined" string - Preserve undefined values correctly in header extraction - Prevents confusion where missing headers appear as "undefined" 4. Code Cleanup (LOW): - Remove unused lastError assignments in retry loops - Assignments were made but value never used before continue All changes verified with 819 passing tests. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/auth/security.ts | 10 +- packages/api/src/routers/auth.ts | 2 +- packages/api/src/routers/subscriptions.ts | 129 ++++++++++++++++------ 3 files changed, 104 insertions(+), 37 deletions(-) diff --git a/packages/api/src/auth/security.ts b/packages/api/src/auth/security.ts index 0e42ff3e..ad254a87 100644 --- a/packages/api/src/auth/security.ts +++ b/packages/api/src/auth/security.ts @@ -19,6 +19,7 @@ export type SecurityAction = | "password_reset_request" | "password_reset_email_sent" | "password_reset_success" + | "password_reset_failed" | "account_locked" | "account_unlocked" | "token_expired" @@ -111,9 +112,12 @@ export function extractHeaders( }); } else { Object.entries(reqHeaders).forEach(([key, value]) => { - headers[key.toLowerCase()] = Array.isArray(value) - ? value[0] - : String(value); + headers[key.toLowerCase()] = + value === undefined + ? undefined + : Array.isArray(value) + ? value[0] + : String(value); }); } diff --git a/packages/api/src/routers/auth.ts b/packages/api/src/routers/auth.ts index 2ec9947a..7d81a5a8 100644 --- a/packages/api/src/routers/auth.ts +++ b/packages/api/src/routers/auth.ts @@ -1141,7 +1141,7 @@ export const authRouter = router({ try { await logSecurityEvent(ctx.db, { userId, - action: "password_reset_success", + action: "password_reset_failed", ipAddress, userAgent, success: false, diff --git a/packages/api/src/routers/subscriptions.ts b/packages/api/src/routers/subscriptions.ts index 9fbc5e31..47b351fd 100644 --- a/packages/api/src/routers/subscriptions.ts +++ b/packages/api/src/routers/subscriptions.ts @@ -353,7 +353,6 @@ export const subscriptionsRouter = router({ TRANSIENT_STATUS_CODES.includes(response.status) && attempt < MAX_RETRIES ) { - lastError = new Error(errorMessage); continue; // Retry } @@ -362,22 +361,54 @@ export const subscriptionsRouter = router({ } feedContent = await response.text(); - const result = parseFeed(feedContent); - feedData = result.feed; - await Sentry.addBreadcrumb({ - category: "subscription", - message: `Successfully parsed feed as ${result.format}`, - level: "info", - data: { - feed_format: result.format, - attempts: attempt + 1, - }, - }); + // Parse feed - parsing errors should NOT be retried + try { + const result = parseFeed(feedContent); + feedData = result.feed; - // Success - break out of retry loop - break; + await Sentry.addBreadcrumb({ + category: "subscription", + message: `Successfully parsed feed as ${result.format}`, + level: "info", + data: { + feed_format: result.format, + attempts: attempt + 1, + }, + }); + + // Success - break out of retry loop + break; + } catch (parseError) { + // Parse error - capture and throw immediately (don't retry) + const errorMessage = + parseError instanceof Error + ? parseError.message + : "Failed to parse feed"; + + await Sentry.captureException(parseError, { + level: "error", + tags: { + operation: "subscription_feed_parse", + domain: domain || "unknown", + error_type: "parse_error", + attempts: (attempt + 1).toString(), + }, + extra: { + url: input.url, + user_id: userId, + error_message: errorMessage, + fetch_status: lastStatusCode, + }, + }); + + throw new TRPCError({ + code: "BAD_REQUEST", + message: `Failed to parse feed: ${errorMessage}`, + }); + } } catch (error) { + // This catch block is only for FETCH errors now lastError = error instanceof Error ? error : new Error(String(error)); // If this is the last attempt or not a transient error, throw @@ -1005,7 +1036,6 @@ export const subscriptionsRouter = router({ TRANSIENT_STATUS_CODES.includes(response.status) && attempt < MAX_RETRIES ) { - lastError = new Error(errorMessage); continue; // Retry } @@ -1014,25 +1044,56 @@ export const subscriptionsRouter = router({ } feedContent = await response.text(); - const result = parseFeed(feedContent); - feedData = result.feed; - await Sentry.addBreadcrumb({ - category: "subscription", - message: `Preview parsed feed as ${result.format}`, - level: "info", - data: { - feed_format: result.format, - attempts: attempt + 1, - }, - }); + // Parse feed - parsing errors should NOT be retried + try { + const result = parseFeed(feedContent); + feedData = result.feed; - // Success - break out of retry loop - break; + await Sentry.addBreadcrumb({ + category: "subscription", + message: `Preview parsed feed as ${result.format}`, + level: "info", + data: { + feed_format: result.format, + attempts: attempt + 1, + }, + }); + + // Success - break out of retry loop + break; + } catch (parseError) { + // Parse error - capture and throw immediately (don't retry) + const errorMessage = + parseError instanceof Error + ? parseError.message + : "Failed to parse feed"; + + await Sentry.captureException(parseError, { + level: "error", + tags: { + operation: "subscription_preview_parse", + domain: domain || "unknown", + error_type: "parse_error", + attempts: (attempt + 1).toString(), + }, + extra: { + url: input.url, + error_message: errorMessage, + fetch_status: lastStatusCode, + }, + }); + + throw new TRPCError({ + code: "BAD_REQUEST", + message: `Failed to parse feed: ${errorMessage}`, + }); + } } catch (error) { + // This catch block is only for FETCH errors now lastError = error instanceof Error ? error : new Error(String(error)); - // If this is the last attempt or not a transient error, throw + // If this is the last attempt, throw with full context if (attempt === MAX_RETRIES) { // Extract HTTP status if this is an HTTP error const errorMessage = lastError.message; @@ -1044,10 +1105,11 @@ export const subscriptionsRouter = router({ await Sentry.captureException(lastError, { level: "error", tags: { - operation: "subscription_preview_parse", + operation: "subscription_preview_fetch", domain: domain || "unknown", ...(httpStatus && { http_status: httpStatus }), attempts: (attempt + 1).toString(), + error_type: "fetch_error", }, extra: { url: input.url, @@ -1059,7 +1121,7 @@ export const subscriptionsRouter = router({ throw new TRPCError({ code: "BAD_REQUEST", - message: `Failed to fetch or parse feed: ${errorMessage}`, + message: `Failed to fetch feed: ${errorMessage}`, }); } @@ -1073,10 +1135,11 @@ export const subscriptionsRouter = router({ await Sentry.captureException(lastError, { level: "error", tags: { - operation: "subscription_preview_parse", + operation: "subscription_preview_fetch", domain: domain || "unknown", http_status: httpStatus.toString(), attempts: (attempt + 1).toString(), + error_type: "fetch_error", }, extra: { url: input.url, @@ -1086,7 +1149,7 @@ export const subscriptionsRouter = router({ throw new TRPCError({ code: "BAD_REQUEST", - message: `Failed to fetch or parse feed: ${errorMessage}`, + message: `Failed to fetch feed: ${errorMessage}`, }); } } From 1bac6e87dc2433af0dd96ca0621db1df8f58296f Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 23:39:41 -0500 Subject: [PATCH 11/41] fix: correct gitignore patterns for wrangler.toml files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add explicit paths for packages/api/wrangler.toml files since root-level patterns don't match subdirectories ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 20c64422..a492e21a 100644 --- a/.gitignore +++ b/.gitignore @@ -140,6 +140,8 @@ temp/ .dev.vars wrangler.toml.local wrangler.toml # Actual config (use wrangler.example.toml as template) +packages/api/wrangler.toml +packages/api/wrangler.toml.local packages/api/migrations/ # Generated folder for Wrangler (copied from drizzle/) # ============================================ From 1ba7caea723c6d21dfe786b4a23b2c04b0c89bcb Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 23:39:48 -0500 Subject: [PATCH 12/41] fix(admin): add emailVerified field to user list and filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Admin dashboard was missing emailVerified field, causing unverified users to appear as "active" when they weren't. This adds: - emailVerified field to AdminUserSchema - emailVerified filter option for queries - Metrics tracking for when filter is used Fixes user status discrepancy where unverified users showed as active ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/routers/admin.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/api/src/routers/admin.ts b/packages/api/src/routers/admin.ts index 101efb68..a06e5f65 100644 --- a/packages/api/src/routers/admin.ts +++ b/packages/api/src/routers/admin.ts @@ -46,6 +46,7 @@ const AdminUserSchema = z.object({ id: z.number(), username: z.string(), email: z.string(), + emailVerified: z.boolean(), role: z.enum(["user", "admin"]), plan: z.string(), // Plan ID - validated against plans table banned: z.boolean(), @@ -88,6 +89,7 @@ export const adminRouter = router({ role: z.enum(["user", "admin"]).optional(), plan: z.string().optional(), // Plan ID filter - validated at runtime banned: z.boolean().optional(), + emailVerified: z.boolean().optional(), // Filter by email verification status search: z.string().optional(), // Search by username or email }) ) @@ -104,6 +106,9 @@ export const adminRouter = router({ if (input.banned !== undefined) { conditions.push(eq(schema.user.banned, input.banned)); } + if (input.emailVerified !== undefined) { + conditions.push(eq(schema.user.emailVerified, input.emailVerified)); + } if (input.search) { conditions.push( sql`(LOWER(COALESCE(${schema.user.username}, ${schema.user.name})) LIKE LOWER(${`%${input.search}%`}) OR LOWER(${schema.user.email}) LIKE LOWER(${`%${input.search}%`}))` @@ -127,6 +132,7 @@ export const adminRouter = router({ "db.has_role_filter": input.role !== undefined, "db.has_plan_filter": input.plan !== undefined, "db.has_banned_filter": input.banned !== undefined, + "db.has_email_verified_filter": input.emailVerified !== undefined, "db.has_search": !!input.search, } ); From 8f6c5a60c91bc8eb7b15cc522b5d966fe129c16e Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 23:39:58 -0500 Subject: [PATCH 13/41] fix(audit): repair broken security audit logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security audit logging was completely non-functional due to missing SQL default on created_at column. This caused silent failures: Root cause: - security_audit_log.created_at was NOT NULL without SQL DEFAULT - Drizzle's $defaultFn() only works at app level, not in database - logSecurityEvent() didn't provide createdAt value - All insert attempts failed silently (caught and logged to console) - Result: Zero audit logs ever written to production Fix (3-part): 1. Quick fix: logSecurityEvent() now explicitly provides createdAt 2. Schema fix: Changed to SQL DEFAULT using unixepoch() 3. Migration: 0006_fix_security_audit_log_default.sql adds DEFAULT to DB Impact: - Enables tracking of login attempts, password resets, registrations - Critical for security monitoring and compliance - Consistent with other tables using same timestamp pattern ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../0006_fix_security_audit_log_default.sql | 38 + packages/api/drizzle/meta/0006_snapshot.json | 2325 +++++++++++++++++ packages/api/drizzle/meta/_journal.json | 7 + packages/api/src/auth/security.ts | 1 + packages/api/src/db/schema.ts | 2 +- 5 files changed, 2372 insertions(+), 1 deletion(-) create mode 100644 packages/api/drizzle/0006_fix_security_audit_log_default.sql create mode 100644 packages/api/drizzle/meta/0006_snapshot.json diff --git a/packages/api/drizzle/0006_fix_security_audit_log_default.sql b/packages/api/drizzle/0006_fix_security_audit_log_default.sql new file mode 100644 index 00000000..e3e6cf8a --- /dev/null +++ b/packages/api/drizzle/0006_fix_security_audit_log_default.sql @@ -0,0 +1,38 @@ +-- Fix security_audit_log.created_at missing SQL default +-- This fixes the bug where audit logs were silently failing to insert +-- because created_at had no default value and was NOT NULL + +-- SQLite doesn't support ALTER COLUMN, so we need to recreate the table +PRAGMA foreign_keys=OFF; + +-- Create new table with correct default +CREATE TABLE `__new_security_audit_log` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `user_id` integer, + `action` text NOT NULL, + `ip_address` text, + `user_agent` text, + `metadata` text, + `success` integer NOT NULL, + `created_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL, + FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE set null +); + +-- Copy existing data (table is currently empty in production, but safe to copy anyway) +INSERT INTO `__new_security_audit_log`("id", "user_id", "action", "ip_address", "user_agent", "metadata", "success", "created_at") +SELECT "id", "user_id", "action", "ip_address", "user_agent", "metadata", "success", "created_at" +FROM `security_audit_log`; + +-- Drop old table +DROP TABLE `security_audit_log`; + +-- Rename new table +ALTER TABLE `__new_security_audit_log` RENAME TO `security_audit_log`; + +-- Re-enable foreign keys +PRAGMA foreign_keys=ON; + +-- Recreate indexes +CREATE INDEX `idx_security_audit_log_user_id` ON `security_audit_log` (`user_id`); +CREATE INDEX `idx_security_audit_log_action` ON `security_audit_log` (`action`); +CREATE INDEX `idx_security_audit_log_created_at` ON `security_audit_log` (`created_at`); diff --git a/packages/api/drizzle/meta/0006_snapshot.json b/packages/api/drizzle/meta/0006_snapshot.json new file mode 100644 index 00000000..15a5cdfb --- /dev/null +++ b/packages/api/drizzle/meta/0006_snapshot.json @@ -0,0 +1,2325 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "e8126989-6d66-49c1-95ad-3392316739fc", + "prevId": "c31c4ddf-ba6f-41a5-9052-d13da161a011", + "tables": { + "account": { + "name": "account", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "api_usage_log": { + "name": "api_usage_log", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status_code": { + "name": "status_code", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "duration_ms": { + "name": "duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_api_usage_log_user_id": { + "name": "idx_api_usage_log_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_api_usage_log_created_at": { + "name": "idx_api_usage_log_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "idx_api_usage_log_endpoint": { + "name": "idx_api_usage_log_endpoint", + "columns": [ + "endpoint" + ], + "isUnique": false + } + }, + "foreignKeys": { + "api_usage_log_user_id_user_id_fk": { + "name": "api_usage_log_user_id_user_id_fk", + "tableFrom": "api_usage_log", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "articles": { + "name": "articles", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "source_id": { + "name": "source_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "guid": { + "name": "guid", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "link": { + "name": "link", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "author": { + "name": "author", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image_url": { + "name": "image_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "audio_url": { + "name": "audio_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "comment_link": { + "name": "comment_link", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_articles_source_id": { + "name": "idx_articles_source_id", + "columns": [ + "source_id" + ], + "isUnique": false + }, + "idx_articles_published_at": { + "name": "idx_articles_published_at", + "columns": [ + "published_at" + ], + "isUnique": false + }, + "idx_articles_guid": { + "name": "idx_articles_guid", + "columns": [ + "guid" + ], + "isUnique": false + }, + "idx_articles_audio_url": { + "name": "idx_articles_audio_url", + "columns": [ + "audio_url" + ], + "isUnique": false + }, + "articles_source_id_guid_unique": { + "name": "articles_source_id_guid_unique", + "columns": [ + "source_id", + "guid" + ], + "isUnique": true + } + }, + "foreignKeys": { + "articles_source_id_sources_id_fk": { + "name": "articles_source_id_sources_id_fk", + "tableFrom": "articles", + "tableTo": "sources", + "columnsFrom": [ + "source_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "blocked_domains": { + "name": "blocked_domains", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "blocked_domains_domain_unique": { + "name": "blocked_domains_domain_unique", + "columns": [ + "domain" + ], + "isUnique": true + }, + "idx_blocked_domains_domain": { + "name": "idx_blocked_domains_domain", + "columns": [ + "domain" + ], + "isUnique": false + }, + "idx_blocked_domains_reason": { + "name": "idx_blocked_domains_reason", + "columns": [ + "reason" + ], + "isUnique": false + }, + "idx_blocked_domains_created_at": { + "name": "idx_blocked_domains_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "blocked_domains_created_by_user_id_fk": { + "name": "blocked_domains_created_by_user_id_fk", + "tableFrom": "blocked_domains", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "categories": { + "name": "categories", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_categories_user_id": { + "name": "idx_categories_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_categories_user_id_name_normalized": { + "name": "idx_categories_user_id_name_normalized", + "columns": [ + "user_id", + "LOWER(\"name\")" + ], + "isUnique": true + } + }, + "foreignKeys": { + "categories_user_id_user_id_fk": { + "name": "categories_user_id_user_id_fk", + "tableFrom": "categories", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "feed_categories": { + "name": "feed_categories", + "columns": { + "feed_id": { + "name": "feed_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_feed_categories_feed_id": { + "name": "idx_feed_categories_feed_id", + "columns": [ + "feed_id" + ], + "isUnique": false + }, + "idx_feed_categories_category_id": { + "name": "idx_feed_categories_category_id", + "columns": [ + "category_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "feed_categories_feed_id_feeds_id_fk": { + "name": "feed_categories_feed_id_feeds_id_fk", + "tableFrom": "feed_categories", + "tableTo": "feeds", + "columnsFrom": [ + "feed_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "feed_categories_category_id_categories_id_fk": { + "name": "feed_categories_category_id_categories_id_fk", + "tableFrom": "feed_categories", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "feed_categories_feed_id_category_id_pk": { + "columns": [ + "feed_id", + "category_id" + ], + "name": "feed_categories_feed_id_category_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "feeds": { + "name": "feeds", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "public": { + "name": "public", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_feeds_user_id": { + "name": "idx_feeds_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_feeds_slug": { + "name": "idx_feeds_slug", + "columns": [ + "slug" + ], + "isUnique": false + }, + "feeds_user_id_slug_unique": { + "name": "feeds_user_id_slug_unique", + "columns": [ + "user_id", + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": { + "feeds_user_id_user_id_fk": { + "name": "feeds_user_id_user_id_fk", + "tableFrom": "feeds", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "global_settings": { + "name": "global_settings", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "max_login_attempts": { + "name": "max_login_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 5 + }, + "login_attempt_window_minutes": { + "name": "login_attempt_window_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 15 + }, + "lockout_duration_minutes": { + "name": "lockout_duration_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 30 + }, + "allow_registration": { + "name": "allow_registration", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "require_email_verification": { + "name": "require_email_verification", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "admin_bypass_email_verification": { + "name": "admin_bypass_email_verification", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "password_reset_token_expiry_hours": { + "name": "password_reset_token_expiry_hours", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "fetch_interval_minutes": { + "name": "fetch_interval_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 60 + }, + "prune_days": { + "name": "prune_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 90 + }, + "last_rss_fetch_at": { + "name": "last_rss_fetch_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_prune_at": { + "name": "last_prune_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_by": { + "name": "updated_by", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "global_settings_updated_by_user_id_fk": { + "name": "global_settings_updated_by_user_id_fk", + "tableFrom": "global_settings", + "tableTo": "user", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "password_reset_tokens": { + "name": "password_reset_tokens", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "used": { + "name": "used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "password_reset_tokens_token_unique": { + "name": "password_reset_tokens_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "idx_password_reset_tokens_token": { + "name": "idx_password_reset_tokens_token", + "columns": [ + "token" + ], + "isUnique": false + }, + "idx_password_reset_tokens_user_id": { + "name": "idx_password_reset_tokens_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "password_reset_tokens_user_id_user_id_fk": { + "name": "password_reset_tokens_user_id_user_id_fk", + "tableFrom": "password_reset_tokens", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "plans": { + "name": "plans", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "max_sources": { + "name": "max_sources", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "max_public_feeds": { + "name": "max_public_feeds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "max_categories": { + "name": "max_categories", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "api_rate_limit_per_minute": { + "name": "api_rate_limit_per_minute", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "public_feed_rate_limit_per_minute": { + "name": "public_feed_rate_limit_per_minute", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "price_cents": { + "name": "price_cents", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "features": { + "name": "features", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "public_feed_access_log": { + "name": "public_feed_access_log", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "feed_id": { + "name": "feed_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "accessed_at": { + "name": "accessed_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_public_feed_access_log_feed_id": { + "name": "idx_public_feed_access_log_feed_id", + "columns": [ + "feed_id" + ], + "isUnique": false + }, + "idx_public_feed_access_log_accessed_at": { + "name": "idx_public_feed_access_log_accessed_at", + "columns": [ + "accessed_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "public_feed_access_log_feed_id_feeds_id_fk": { + "name": "public_feed_access_log_feed_id_feeds_id_fk", + "tableFrom": "public_feed_access_log", + "tableTo": "feeds", + "columnsFrom": [ + "feed_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "security_audit_log": { + "name": "security_audit_log", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "success": { + "name": "success", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "idx_security_audit_log_user_id": { + "name": "idx_security_audit_log_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_security_audit_log_action": { + "name": "idx_security_audit_log_action", + "columns": [ + "action" + ], + "isUnique": false + }, + "idx_security_audit_log_created_at": { + "name": "idx_security_audit_log_created_at", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "security_audit_log_user_id_user_id_fk": { + "name": "security_audit_log_user_id_user_id_fk", + "tableFrom": "security_audit_log", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "session_token_unique": { + "name": "session_token_unique", + "columns": [ + "token" + ], + "isUnique": true + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "sources": { + "name": "sources", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "site_url": { + "name": "site_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "icon_type": { + "name": "icon_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'auto'" + }, + "icon_updated_at": { + "name": "icon_updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_fetched": { + "name": "last_fetched", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "sources_url_unique": { + "name": "sources_url_unique", + "columns": [ + "url" + ], + "isUnique": true + }, + "idx_sources_url": { + "name": "idx_sources_url", + "columns": [ + "url" + ], + "isUnique": false + }, + "idx_sources_icon_url": { + "name": "idx_sources_icon_url", + "columns": [ + "icon_url" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "subscription_categories": { + "name": "subscription_categories", + "columns": { + "subscription_id": { + "name": "subscription_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category_id": { + "name": "category_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_subscription_categories_subscription_id": { + "name": "idx_subscription_categories_subscription_id", + "columns": [ + "subscription_id" + ], + "isUnique": false + }, + "idx_subscription_categories_category_id": { + "name": "idx_subscription_categories_category_id", + "columns": [ + "category_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "subscription_categories_subscription_id_subscriptions_id_fk": { + "name": "subscription_categories_subscription_id_subscriptions_id_fk", + "tableFrom": "subscription_categories", + "tableTo": "subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "subscription_categories_category_id_categories_id_fk": { + "name": "subscription_categories_category_id_categories_id_fk", + "tableFrom": "subscription_categories", + "tableTo": "categories", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "subscription_categories_subscription_id_category_id_pk": { + "columns": [ + "subscription_id", + "category_id" + ], + "name": "subscription_categories_subscription_id_category_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "subscription_filters": { + "name": "subscription_filters", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "subscription_id": { + "name": "subscription_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "field": { + "name": "field", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "match_type": { + "name": "match_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "pattern": { + "name": "pattern", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "case_sensitive": { + "name": "case_sensitive", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_subscription_filters_subscription_id": { + "name": "idx_subscription_filters_subscription_id", + "columns": [ + "subscription_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "subscription_filters_subscription_id_subscriptions_id_fk": { + "name": "subscription_filters_subscription_id_subscriptions_id_fk", + "tableFrom": "subscription_filters", + "tableTo": "subscriptions", + "columnsFrom": [ + "subscription_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "subscriptions": { + "name": "subscriptions", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_id": { + "name": "source_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "custom_title": { + "name": "custom_title", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "filter_enabled": { + "name": "filter_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "filter_mode": { + "name": "filter_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'include'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_subscriptions_user_id": { + "name": "idx_subscriptions_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_subscriptions_source_id": { + "name": "idx_subscriptions_source_id", + "columns": [ + "source_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "subscriptions_user_id_user_id_fk": { + "name": "subscriptions_user_id_user_id_fk", + "tableFrom": "subscriptions", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "subscriptions_source_id_sources_id_fk": { + "name": "subscriptions_source_id_sources_id_fk", + "tableFrom": "subscriptions", + "tableTo": "sources", + "columnsFrom": [ + "source_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "usage_stats": { + "name": "usage_stats", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "source_count": { + "name": "source_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "public_feed_count": { + "name": "public_feed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "category_count": { + "name": "category_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "article_count": { + "name": "article_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_updated": { + "name": "last_updated", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "usage_stats_user_id_user_id_fk": { + "name": "usage_stats_user_id_user_id_fk", + "tableFrom": "usage_stats", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "display_username": { + "name": "display_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'free'" + }, + "banned": { + "name": "banned", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + }, + "user_username_unique": { + "name": "user_username_unique", + "columns": [ + "username" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user_article_states": { + "name": "user_article_states", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "article_id": { + "name": "article_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "read": { + "name": "read", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "saved": { + "name": "saved", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "audio_position": { + "name": "audio_position", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "audio_duration": { + "name": "audio_duration", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "audio_completed_at": { + "name": "audio_completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "audio_last_played_at": { + "name": "audio_last_played_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_user_article_states_user_id": { + "name": "idx_user_article_states_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "idx_user_article_states_read": { + "name": "idx_user_article_states_read", + "columns": [ + "read" + ], + "isUnique": false + }, + "idx_user_article_states_saved": { + "name": "idx_user_article_states_saved", + "columns": [ + "saved" + ], + "isUnique": false + }, + "idx_user_article_states_audio_position": { + "name": "idx_user_article_states_audio_position", + "columns": [ + "audio_position" + ], + "isUnique": false + } + }, + "foreignKeys": { + "user_article_states_user_id_user_id_fk": { + "name": "user_article_states_user_id_user_id_fk", + "tableFrom": "user_article_states", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_article_states_article_id_articles_id_fk": { + "name": "user_article_states_article_id_articles_id_fk", + "tableFrom": "user_article_states", + "tableTo": "articles", + "columnsFrom": [ + "article_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_article_states_user_id_article_id_pk": { + "columns": [ + "user_id", + "article_id" + ], + "name": "user_article_states_user_id_article_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user_limits": { + "name": "user_limits", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "max_sources": { + "name": "max_sources", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "max_public_feeds": { + "name": "max_public_feeds", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "max_categories": { + "name": "max_categories", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "api_rate_limit_per_minute": { + "name": "api_rate_limit_per_minute", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "public_feed_rate_limit_per_minute": { + "name": "public_feed_rate_limit_per_minute", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_limits_user_id_user_id_fk": { + "name": "user_limits_user_id_user_id_fk", + "tableFrom": "user_limits", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user_settings": { + "name": "user_settings", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "theme": { + "name": "theme", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'system'" + }, + "auto_age_days": { + "name": "auto_age_days", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 7 + }, + "default_filter": { + "name": "default_filter", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'all'" + }, + "share_email": { + "name": "share_email", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "share_hackernews": { + "name": "share_hackernews", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "share_reddit": { + "name": "share_reddit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "share_twitter": { + "name": "share_twitter", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "share_bluesky": { + "name": "share_bluesky", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "share_mastodon": { + "name": "share_mastodon", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "idx_user_settings_user_id": { + "name": "idx_user_settings_user_id", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "user_settings_user_id_user_id_fk": { + "name": "user_settings_user_id_user_id_fk", + "tableFrom": "user_settings", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification": { + "name": "verification", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": { + "idx_categories_user_id_name_normalized": { + "columns": { + "LOWER(\"name\")": { + "isExpression": true + } + } + } + } + } +} \ No newline at end of file diff --git a/packages/api/drizzle/meta/_journal.json b/packages/api/drizzle/meta/_journal.json index 6c19954a..e7730760 100644 --- a/packages/api/drizzle/meta/_journal.json +++ b/packages/api/drizzle/meta/_journal.json @@ -43,6 +43,13 @@ "when": 1764633557616, "tag": "0005_panoramic_magdalene", "breakpoints": true + }, + { + "idx": 6, + "version": "6", + "when": 1764822482306, + "tag": "0006_fix_security_audit_log_default", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/api/src/auth/security.ts b/packages/api/src/auth/security.ts index ad254a87..70833770 100644 --- a/packages/api/src/auth/security.ts +++ b/packages/api/src/auth/security.ts @@ -50,6 +50,7 @@ export async function logSecurityEvent( userAgent: params.userAgent, metadata: params.metadata ? JSON.stringify(params.metadata) : undefined, success: params.success, + createdAt: new Date(), }); } catch (error) { // Log but don't throw - audit logging shouldn't break the app diff --git a/packages/api/src/db/schema.ts b/packages/api/src/db/schema.ts index 24d3dbf1..3c28273a 100644 --- a/packages/api/src/db/schema.ts +++ b/packages/api/src/db/schema.ts @@ -458,7 +458,7 @@ export const securityAuditLog = sqliteTable( success: integer("success", { mode: "boolean" }).notNull(), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() - .$defaultFn(() => new Date()), + .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`), }, (table) => [ index("idx_security_audit_log_user_id").on(table.userId), From 43b509ca7281206ac236f38d15539132c8e178ec Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 23:40:23 -0500 Subject: [PATCH 14/41] chore: allow .claude/settings.json in git for team sharing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While .claude/ directory is gitignored, settings.json should be committed to share team-wide Claude Code configurations like security policies and approved tool permissions ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .gitignore | 1 + CLAUDE.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index a492e21a..999bd33d 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,7 @@ data/ # Claude AI .claude/ +!.claude/settings.json # Share team settings, keep local settings private # Cursor AI .cursor/ .cursorrules diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..1e3a9b14 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,80 @@ +# TuvixRSS - Claude Code Guidelines + +TuvixRSS is a modern RSS reader with AI features, built on Cloudflare Workers. + +## Tech Stack + +- **API**: Hono (Cloudflare Workers), tRPC, Drizzle ORM, Cloudflare D1 +- **Frontend**: React, TanStack Router, TanStack Query, Tailwind CSS +- **Auth**: Better Auth (email/password) +- **Observability**: Sentry (errors, performance, metrics) +- **Email**: Resend +- **Monorepo**: pnpm workspaces (`packages/api`, `packages/app`, `packages/tricorder`) + +## Project Structure + +``` +packages/ + api/ # Cloudflare Workers API (Hono + tRPC) + src/ + routers/ # tRPC route handlers + services/ # Business logic (RSS fetching, email, etc.) + auth/ # Better Auth configuration + db/ # Drizzle schema and migrations + app/ # React frontend (Vite + TanStack) + tricorder/ # RSS/Atom feed discovery library +``` + +## Critical Rules + +### Production Database Operations + +**โ›” NEVER run production database migrations or modifications without explicit user permission.** + +This includes but is not limited to: +- `wrangler d1 execute --remote` +- Any SQL migrations against production databases +- Schema alterations on live systems +- Data modifications in production + +**Required Process:** +1. Generate migrations locally +2. Show the user what will change +3. Explain impact and safety +4. **ASK FOR PERMISSION** +5. Only after explicit approval, proceed + +**Rationale:** Production database operations are irreversible and can cause data loss, service disruption, or schema conflicts. Always give the user control over these decisions. + +**Exception:** Local/dev database operations (`--local`, `db:migrate:local`) are safe to run without asking. + +### Production Deployments + +**โ›” NEVER deploy to production. Only local development is allowed.** + +Deployment is explicitly forbidden and handled by CI/CD pipelines. + +## Common Workflows + +### Database Changes +1. Modify schema in `packages/api/src/db/schema.ts` +2. Generate migration: `pnpm db:generate` +3. Review generated SQL in `packages/api/drizzle/` +4. Apply locally: `pnpm db:migrate:local` + +### Running Tests +- API: `pnpm --filter @tuvixrss/api test` +- App: `pnpm --filter @tuvixrss/app test` +- All: `pnpm test` + +### Type Checking & Linting +- `pnpm type-check` - Check all packages +- `pnpm lint` - Lint all packages +- `pnpm format` - Format with Prettier + +## Key Architecture Decisions + +- **Fire-and-forget emails**: Email sending doesn't block API responses; uses Sentry spans for tracking +- **Admin dashboard**: User management at `packages/api/src/routers/admin.ts` +- **Security audit logging**: All auth events logged to `security_audit_log` table +- **Rate limiting**: Cloudflare Workers rate limit API per plan tier From 06df3b60e556a9f4d467bfcd3cb221daa4d0fb4a Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Wed, 3 Dec 2025 23:40:40 -0500 Subject: [PATCH 15/41] chore: add Claude Code team settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add shared Claude Code configuration with: - Security deny rules for .env, secrets, production operations - Broader allow patterns for common workflows - Git co-authoring enabled - MCP server integrations (Sentry, Better Auth, Shadcn) Establishes team-wide safety guardrails and tool permissions for AI-assisted development ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .claude/settings.json | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .claude/settings.json diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..bc28fb5f --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,47 @@ +{ + "permissions": { + "allow": [ + "Bash(pnpm run *)", + "Bash(git add *)", + "Bash(git commit *)", + "Bash(git branch *)", + "Bash(git log *)", + "Bash(git checkout *)", + "Bash(git status *)", + "Bash(ls *)", + "Bash(cat *)", + "Bash(grep *)", + "Bash(find *)", + "Bash(tree *)", + "WebSearch", + "WebFetch(domain:*.anthropic.com)", + "WebFetch(domain:docs.sentry.io)", + "WebFetch(domain:www.better-auth.com)", + "WebFetch(domain:hono.dev)", + "WebFetch(domain:tanstack.com)", + "WebFetch(domain:trpc.io)", + "WebFetch(domain:orm.drizzle.team)", + "mcp__sentry__*", + "mcp__better-auth__*", + "mcp__shadcn__*", + "Skill(*)" + ], + "deny": [ + "Read(.env*)", + "Read(**/.env*)", + "Read(**/secrets.*)", + "Read(**/*secret*)", + "Read(**/*credential*)", + "Read(**/wrangler.toml)", + "Write(.env*)", + "Write(**/.env*)", + "Bash(wrangler d1 execute * --remote *)", + "Bash(rm -rf *)", + "Bash(sudo *)" + ] + }, + "enableAllProjectMcpServers": true, + "git": { + "includeCoAuthoredBy": true + } +} From 27fc099aed4eeff26aa1185a8a07081fa60f86f1 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 00:20:52 -0500 Subject: [PATCH 16/41] refactor: address Copilot review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improvements from code review: - Extract getRequestMetadata() helper to reduce header extraction duplication - Consolidate security module imports in auth router - Extract retry constants (MAX_RETRIES, RETRY_DELAY_MS, TRANSIENT_STATUS_CODES) to module top - Add clarifying comment for createdAt defense-in-depth pattern No functional changes, just improved maintainability ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/auth/security.ts | 23 +++++++++++++ packages/api/src/routers/auth.ts | 40 ++++++++--------------- packages/api/src/routers/subscriptions.ts | 17 +++------- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/packages/api/src/auth/security.ts b/packages/api/src/auth/security.ts index 70833770..a5f20a76 100644 --- a/packages/api/src/auth/security.ts +++ b/packages/api/src/auth/security.ts @@ -50,6 +50,8 @@ export async function logSecurityEvent( userAgent: params.userAgent, metadata: params.metadata ? JSON.stringify(params.metadata) : undefined, success: params.success, + // Explicitly set createdAt for consistency with app time + // DB has SQL DEFAULT but we set it here for defense-in-depth createdAt: new Date(), }); } catch (error) { @@ -91,6 +93,27 @@ export function getUserAgent( return headers["user-agent"]; } +/** + * Extract request metadata for security audit logging + * Convenience function that combines header extraction, IP, and user agent + */ +export function getRequestMetadata( + reqHeaders: + | Headers + | Record + | undefined +): { + headers: Record; + ipAddress: string | undefined; + userAgent: string | undefined; +} { + const headers = extractHeaders(reqHeaders); + const ipAddress = getClientIp(headers); + const userAgent = getUserAgent(headers); + + return { headers, ipAddress, userAgent }; +} + /** * Extract headers from request into a normalized Record * Handles both Headers object and plain object formats diff --git a/packages/api/src/routers/auth.ts b/packages/api/src/routers/auth.ts index 7d81a5a8..7f06dbcf 100644 --- a/packages/api/src/routers/auth.ts +++ b/packages/api/src/routers/auth.ts @@ -497,16 +497,12 @@ export const authRouter = router({ } // Log successful login to security audit - const { - logSecurityEvent, - getClientIp, - getUserAgent, - extractHeaders, - } = await import("@/auth/security"); + const { logSecurityEvent, getRequestMetadata } = + await import("@/auth/security"); - const headers = extractHeaders(ctx.req.headers); - const ipAddress = getClientIp(headers); - const userAgent = getUserAgent(headers); + const { ipAddress, userAgent } = getRequestMetadata( + ctx.req.headers + ); try { await logSecurityEvent(ctx.db, { @@ -598,16 +594,12 @@ export const authRouter = router({ }); // Log failed login attempt to security audit (if not a generic auth error) - const { - logSecurityEvent, - getClientIp, - getUserAgent, - extractHeaders, - } = await import("@/auth/security"); + const { logSecurityEvent, getRequestMetadata } = + await import("@/auth/security"); - const headers = extractHeaders(ctx.req.headers); - const ipAddress = getClientIp(headers); - const userAgent = getUserAgent(headers); + const { ipAddress, userAgent } = getRequestMetadata( + ctx.req.headers + ); try { await logSecurityEvent(ctx.db, { @@ -864,7 +856,7 @@ export const authRouter = router({ .output(z.object({ success: z.boolean() })) .mutation(async ({ ctx, input }) => { const auth = createAuth(ctx.env, ctx.db); - const { logSecurityEvent, getClientIp, getUserAgent, extractHeaders } = + const { logSecurityEvent, getRequestMetadata } = await import("@/auth/security"); // Convert headers for Better Auth @@ -881,9 +873,7 @@ export const authRouter = router({ ); // Extract IP and user agent for audit logging - const headers = extractHeaders(ctx.req.headers); - const ipAddress = getClientIp(headers); - const userAgent = getUserAgent(headers); + const { ipAddress, userAgent } = getRequestMetadata(ctx.req.headers); try { await auth.api.changePassword({ @@ -1058,7 +1048,7 @@ export const authRouter = router({ .output(z.object({ success: z.boolean() })) .mutation(async ({ ctx, input }) => { const auth = createAuth(ctx.env, ctx.db); - const { logSecurityEvent, getClientIp, getUserAgent, extractHeaders } = + const { logSecurityEvent, getRequestMetadata } = await import("@/auth/security"); // Convert headers for Better Auth @@ -1075,9 +1065,7 @@ export const authRouter = router({ ); // Extract IP and user agent for audit logging - const headers = extractHeaders(ctx.req.headers); - const ipAddress = getClientIp(headers); - const userAgent = getUserAgent(headers); + const { ipAddress, userAgent } = getRequestMetadata(ctx.req.headers); // Find user by verification token BEFORE resetting (token is deleted after use) let userId: number | undefined; diff --git a/packages/api/src/routers/subscriptions.ts b/packages/api/src/routers/subscriptions.ts index 47b351fd..1ca8062f 100644 --- a/packages/api/src/routers/subscriptions.ts +++ b/packages/api/src/routers/subscriptions.ts @@ -5,6 +5,11 @@ */ import * as Sentry from "@/utils/sentry"; + +// Retry configuration for transient HTTP failures +const MAX_RETRIES = 2; +const RETRY_DELAY_MS = 1000; +const TRANSIENT_STATUS_CODES = [502, 503, 504, 429]; import { z } from "zod"; import { TRPCError } from "@trpc/server"; import { eq, and } from "drizzle-orm"; @@ -301,12 +306,6 @@ export const subscriptionsRouter = router({ let feedUrl = input.url; let feedData; let feedContent: string | undefined; - - // Retry logic for transient failures - const MAX_RETRIES = 2; - const RETRY_DELAY_MS = 1000; - const TRANSIENT_STATUS_CODES = [502, 503, 504, 429]; - let lastError: Error | undefined; let lastStatusCode: number | undefined; @@ -990,12 +989,6 @@ export const subscriptionsRouter = router({ let feedData; let feedUrl = input.url; let feedContent: string | undefined; - - // Retry logic for transient failures - const MAX_RETRIES = 2; - const RETRY_DELAY_MS = 1000; - const TRANSIENT_STATUS_CODES = [502, 503, 504, 429]; - let lastError: Error | undefined; let lastStatusCode: number | undefined; From 955cde937a0e647d54841163914af3f1ff5e89ab Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 00:51:54 -0500 Subject: [PATCH 17/41] chore: format --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 1e3a9b14..d719c478 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -32,12 +32,14 @@ packages/ **โ›” NEVER run production database migrations or modifications without explicit user permission.** This includes but is not limited to: + - `wrangler d1 execute --remote` - Any SQL migrations against production databases - Schema alterations on live systems - Data modifications in production **Required Process:** + 1. Generate migrations locally 2. Show the user what will change 3. Explain impact and safety @@ -57,17 +59,20 @@ Deployment is explicitly forbidden and handled by CI/CD pipelines. ## Common Workflows ### Database Changes + 1. Modify schema in `packages/api/src/db/schema.ts` 2. Generate migration: `pnpm db:generate` 3. Review generated SQL in `packages/api/drizzle/` 4. Apply locally: `pnpm db:migrate:local` ### Running Tests + - API: `pnpm --filter @tuvixrss/api test` - App: `pnpm --filter @tuvixrss/app test` - All: `pnpm test` ### Type Checking & Linting + - `pnpm type-check` - Check all packages - `pnpm lint` - Lint all packages - `pnpm format` - Format with Prettier From 6dbf9498e3170f39f476cfc669ad6794387ddad7 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 01:03:47 -0500 Subject: [PATCH 18/41] fix: add missing security module imports and emailVerified field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Import logSecurityEvent and getRequestMetadata in login handler - Add emailVerified field to admin listUsers and getUser responses - Fixes type errors from refactoring ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/routers/admin.ts | 2 ++ packages/api/src/routers/auth.ts | 24 ++++++++---------------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/api/src/routers/admin.ts b/packages/api/src/routers/admin.ts index a06e5f65..4aede417 100644 --- a/packages/api/src/routers/admin.ts +++ b/packages/api/src/routers/admin.ts @@ -175,6 +175,7 @@ export const adminRouter = router({ id: user.id, username: user.username || user.name, email: user.email, + emailVerified: user.emailVerified || false, role: (user.role as "user" | "admin") || "user", plan: user.plan || "free", banned: user.banned || false, @@ -269,6 +270,7 @@ export const adminRouter = router({ id: user.id, username: user.username || user.name, email: user.email, + emailVerified: user.emailVerified || false, role: (user.role as "user" | "admin") || "user", plan: user.plan || "free", banned: user.banned || false, diff --git a/packages/api/src/routers/auth.ts b/packages/api/src/routers/auth.ts index 7f06dbcf..666052bb 100644 --- a/packages/api/src/routers/auth.ts +++ b/packages/api/src/routers/auth.ts @@ -32,6 +32,12 @@ import { import { getBaseUrl } from "@/utils/base-url"; import * as Sentry from "@/utils/sentry"; import { emitCounter, emitMetrics } from "@/utils/metrics"; +import { + logSecurityEvent, + getRequestMetadata, + getClientIp, + getUserAgent, +} from "@/auth/security"; import type { BetterAuthUser, SignUpEmailResult, @@ -267,9 +273,6 @@ export const authRouter = router({ op: "db.insert", }, async (span) => { - const { logSecurityEvent, getClientIp, getUserAgent } = - await import("@/auth/security"); - // Extract IP and user agent from request headers const headers: Record = {}; if (ctx.req.headers) { @@ -447,6 +450,8 @@ export const authRouter = router({ }); const auth = createAuth(ctx.env, ctx.db); + const { logSecurityEvent, getRequestMetadata } = + await import("@/auth/security"); // Convert headers for Better Auth const authHeaders = @@ -497,9 +502,6 @@ export const authRouter = router({ } // Log successful login to security audit - const { logSecurityEvent, getRequestMetadata } = - await import("@/auth/security"); - const { ipAddress, userAgent } = getRequestMetadata( ctx.req.headers ); @@ -594,9 +596,6 @@ export const authRouter = router({ }); // Log failed login attempt to security audit (if not a generic auth error) - const { logSecurityEvent, getRequestMetadata } = - await import("@/auth/security"); - const { ipAddress, userAgent } = getRequestMetadata( ctx.req.headers ); @@ -856,8 +855,6 @@ export const authRouter = router({ .output(z.object({ success: z.boolean() })) .mutation(async ({ ctx, input }) => { const auth = createAuth(ctx.env, ctx.db); - const { logSecurityEvent, getRequestMetadata } = - await import("@/auth/security"); // Convert headers for Better Auth const authHeaders = @@ -954,9 +951,6 @@ export const authRouter = router({ ) .mutation(async ({ ctx, input }) => { const auth = createAuth(ctx.env, ctx.db); - const { logSecurityEvent, getClientIp, getUserAgent } = await import( - "@/auth/security" - ); // Convert headers for Better Auth const authHeaders = @@ -1048,8 +1042,6 @@ export const authRouter = router({ .output(z.object({ success: z.boolean() })) .mutation(async ({ ctx, input }) => { const auth = createAuth(ctx.env, ctx.db); - const { logSecurityEvent, getRequestMetadata } = - await import("@/auth/security"); // Convert headers for Better Auth const authHeaders = From 821824a57164d06e18c7688eac1d7a5c134f7942 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 01:05:06 -0500 Subject: [PATCH 19/41] fix: add explicit type annotations for getRequestMetadata destructuring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dynamic imports lose type information, causing ESLint to treat destructured values as 'error' types. Added explicit type annotations to resolve "unsafe assignment" and "unsafe call" linting errors. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/routers/auth.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/api/src/routers/auth.ts b/packages/api/src/routers/auth.ts index 666052bb..5375bdf9 100644 --- a/packages/api/src/routers/auth.ts +++ b/packages/api/src/routers/auth.ts @@ -502,9 +502,10 @@ export const authRouter = router({ } // Log successful login to security audit - const { ipAddress, userAgent } = getRequestMetadata( - ctx.req.headers - ); + const { ipAddress, userAgent }: { + ipAddress: string | undefined; + userAgent: string | undefined; + } = getRequestMetadata(ctx.req.headers); try { await logSecurityEvent(ctx.db, { @@ -596,9 +597,10 @@ export const authRouter = router({ }); // Log failed login attempt to security audit (if not a generic auth error) - const { ipAddress, userAgent } = getRequestMetadata( - ctx.req.headers - ); + const { ipAddress, userAgent }: { + ipAddress: string | undefined; + userAgent: string | undefined; + } = getRequestMetadata(ctx.req.headers); try { await logSecurityEvent(ctx.db, { @@ -870,7 +872,10 @@ export const authRouter = router({ ); // Extract IP and user agent for audit logging - const { ipAddress, userAgent } = getRequestMetadata(ctx.req.headers); + const { ipAddress, userAgent }: { + ipAddress: string | undefined; + userAgent: string | undefined; + } = getRequestMetadata(ctx.req.headers); try { await auth.api.changePassword({ @@ -1057,7 +1062,10 @@ export const authRouter = router({ ); // Extract IP and user agent for audit logging - const { ipAddress, userAgent } = getRequestMetadata(ctx.req.headers); + const { ipAddress, userAgent }: { + ipAddress: string | undefined; + userAgent: string | undefined; + } = getRequestMetadata(ctx.req.headers); // Find user by verification token BEFORE resetting (token is deleted after use) let userId: number | undefined; From 3441aad8398449d9cde781fd6a1c0bdcc2017eb1 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 01:05:56 -0500 Subject: [PATCH 20/41] fix: use type assertions instead of annotations for getRequestMetadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Type assertions (as) are more effective than type annotations (:) for dynamic imports, as they assert the return type of the function call rather than just the destructured variables. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/routers/auth.ts | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/api/src/routers/auth.ts b/packages/api/src/routers/auth.ts index 5375bdf9..907185de 100644 --- a/packages/api/src/routers/auth.ts +++ b/packages/api/src/routers/auth.ts @@ -502,10 +502,9 @@ export const authRouter = router({ } // Log successful login to security audit - const { ipAddress, userAgent }: { - ipAddress: string | undefined; - userAgent: string | undefined; - } = getRequestMetadata(ctx.req.headers); + const { ipAddress, userAgent } = getRequestMetadata( + ctx.req.headers + ) as { ipAddress: string | undefined; userAgent: string | undefined }; try { await logSecurityEvent(ctx.db, { @@ -597,10 +596,9 @@ export const authRouter = router({ }); // Log failed login attempt to security audit (if not a generic auth error) - const { ipAddress, userAgent }: { - ipAddress: string | undefined; - userAgent: string | undefined; - } = getRequestMetadata(ctx.req.headers); + const { ipAddress, userAgent } = getRequestMetadata( + ctx.req.headers + ) as { ipAddress: string | undefined; userAgent: string | undefined }; try { await logSecurityEvent(ctx.db, { @@ -872,10 +870,9 @@ export const authRouter = router({ ); // Extract IP and user agent for audit logging - const { ipAddress, userAgent }: { - ipAddress: string | undefined; - userAgent: string | undefined; - } = getRequestMetadata(ctx.req.headers); + const { ipAddress, userAgent } = getRequestMetadata( + ctx.req.headers + ) as { ipAddress: string | undefined; userAgent: string | undefined }; try { await auth.api.changePassword({ @@ -1062,10 +1059,9 @@ export const authRouter = router({ ); // Extract IP and user agent for audit logging - const { ipAddress, userAgent }: { - ipAddress: string | undefined; - userAgent: string | undefined; - } = getRequestMetadata(ctx.req.headers); + const { ipAddress, userAgent } = getRequestMetadata( + ctx.req.headers + ) as { ipAddress: string | undefined; userAgent: string | undefined }; // Find user by verification token BEFORE resetting (token is deleted after use) let userId: number | undefined; From 349b822f6494e8ba222b1873cfc5b8756e2b8739 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 02:16:44 -0500 Subject: [PATCH 21/41] fix(auth): resolve TypeScript unsafe call errors with getRequestMetadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove dynamic imports of security module to fix type inference - Add explicit type annotations to destructured variables - Use top-level import instead of await import() pattern This resolves ESLint errors where dynamic imports lose type information, causing getRequestMetadata to be treated as 'error' type. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/routers/auth.ts | 44 +++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/api/src/routers/auth.ts b/packages/api/src/routers/auth.ts index 907185de..2729cb4c 100644 --- a/packages/api/src/routers/auth.ts +++ b/packages/api/src/routers/auth.ts @@ -34,9 +34,9 @@ import * as Sentry from "@/utils/sentry"; import { emitCounter, emitMetrics } from "@/utils/metrics"; import { logSecurityEvent, - getRequestMetadata, getClientIp, getUserAgent, + getRequestMetadata, } from "@/auth/security"; import type { BetterAuthUser, @@ -450,8 +450,6 @@ export const authRouter = router({ }); const auth = createAuth(ctx.env, ctx.db); - const { logSecurityEvent, getRequestMetadata } = - await import("@/auth/security"); // Convert headers for Better Auth const authHeaders = @@ -502,9 +500,13 @@ export const authRouter = router({ } // Log successful login to security audit - const { ipAddress, userAgent } = getRequestMetadata( - ctx.req.headers - ) as { ipAddress: string | undefined; userAgent: string | undefined }; + const { + ipAddress, + userAgent, + }: { + ipAddress: string | undefined; + userAgent: string | undefined; + } = getRequestMetadata(ctx.req.headers); try { await logSecurityEvent(ctx.db, { @@ -596,9 +598,13 @@ export const authRouter = router({ }); // Log failed login attempt to security audit (if not a generic auth error) - const { ipAddress, userAgent } = getRequestMetadata( - ctx.req.headers - ) as { ipAddress: string | undefined; userAgent: string | undefined }; + const { + ipAddress, + userAgent, + }: { + ipAddress: string | undefined; + userAgent: string | undefined; + } = getRequestMetadata(ctx.req.headers); try { await logSecurityEvent(ctx.db, { @@ -870,9 +876,13 @@ export const authRouter = router({ ); // Extract IP and user agent for audit logging - const { ipAddress, userAgent } = getRequestMetadata( - ctx.req.headers - ) as { ipAddress: string | undefined; userAgent: string | undefined }; + const { + ipAddress, + userAgent, + }: { + ipAddress: string | undefined; + userAgent: string | undefined; + } = getRequestMetadata(ctx.req.headers); try { await auth.api.changePassword({ @@ -1059,9 +1069,13 @@ export const authRouter = router({ ); // Extract IP and user agent for audit logging - const { ipAddress, userAgent } = getRequestMetadata( - ctx.req.headers - ) as { ipAddress: string | undefined; userAgent: string | undefined }; + const { + ipAddress, + userAgent, + }: { + ipAddress: string | undefined; + userAgent: string | undefined; + } = getRequestMetadata(ctx.req.headers); // Find user by verification token BEFORE resetting (token is deleted after use) let userId: number | undefined; From ed646131dae24b06d6f2225027facda311629923 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 02:16:49 -0500 Subject: [PATCH 22/41] fix(articles): add missing Sentry import and fix undefined attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add top-level import for Sentry module - Add nullish coalescing for optional olderThanDays attribute Resolves ESLint errors from missing Sentry import and TypeScript errors from passing undefined to Sentry span attributes. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/routers/articles.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/api/src/routers/articles.ts b/packages/api/src/routers/articles.ts index 2311f744..ea93a627 100644 --- a/packages/api/src/routers/articles.ts +++ b/packages/api/src/routers/articles.ts @@ -23,6 +23,7 @@ import * as schema from "@/db/schema"; import { executeBatch } from "@/db/utils"; import { withQueryMetrics } from "@/utils/db-metrics"; import { upsertArticleState } from "@/db/helpers"; +import * as Sentry from "@/utils/sentry"; /** * Helper function to transform database row to article output @@ -954,7 +955,7 @@ export const articlesRouter = router({ "db.batch_size": statements.length, "db.operation": "mark_all_read", "db.user_id": userId, - "filter.older_than_days": input.olderThanDays, + "filter.older_than_days": input.olderThanDays ?? "none", }, }, async (span) => { From 7f3ad05d186e69623e3dc6ac5dc8473a2523437e Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 02:16:56 -0500 Subject: [PATCH 23/41] fix(rss-fetcher): handle null domains in Sentry attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add nullish coalescing for extractDomain() calls - Prevents TypeScript errors when domain extraction returns null Sentry span attributes require string | number | boolean types, not null or undefined. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/services/rss-fetcher.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api/src/services/rss-fetcher.ts b/packages/api/src/services/rss-fetcher.ts index 40362608..ecf5145a 100644 --- a/packages/api/src/services/rss-fetcher.ts +++ b/packages/api/src/services/rss-fetcher.ts @@ -876,7 +876,7 @@ async function extractArticleData( name: "Fetch OpenGraph Image", attributes: { "http.url": link, - "og.domain": extractDomain(link), + "og.domain": extractDomain(link) ?? "unknown", }, }, async (span) => { @@ -900,7 +900,7 @@ async function extractArticleData( // Don't spam Sentry with every OG failure, but track metrics emitCounter("og_image.fetch_error", 1, { - domain: extractDomain(link), + domain: extractDomain(link) ?? "unknown", error_type: error instanceof Error ? error.name : "unknown", }); } From d9408722e653475418bb12cc2229a3fd06d32702 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 02:17:01 -0500 Subject: [PATCH 24/41] fix(migration): split security audit log migration into separate statements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add --> statement-breakpoint markers for Drizzle compatibility - Prevents "more than one statement" error in test migrations - Maintains same logic, just properly formatted for migration runner This fixes test failures caused by better-sqlite3 rejecting multi-statement SQL strings. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../0006_fix_security_audit_log_default.sql | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/api/drizzle/0006_fix_security_audit_log_default.sql b/packages/api/drizzle/0006_fix_security_audit_log_default.sql index e3e6cf8a..32481a51 100644 --- a/packages/api/drizzle/0006_fix_security_audit_log_default.sql +++ b/packages/api/drizzle/0006_fix_security_audit_log_default.sql @@ -1,11 +1,5 @@ --- Fix security_audit_log.created_at missing SQL default --- This fixes the bug where audit logs were silently failing to insert --- because created_at had no default value and was NOT NULL - --- SQLite doesn't support ALTER COLUMN, so we need to recreate the table PRAGMA foreign_keys=OFF; - --- Create new table with correct default +--> statement-breakpoint CREATE TABLE `__new_security_audit_log` ( `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `user_id` integer, @@ -17,22 +11,19 @@ CREATE TABLE `__new_security_audit_log` ( `created_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL, FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE set null ); - --- Copy existing data (table is currently empty in production, but safe to copy anyway) +--> statement-breakpoint INSERT INTO `__new_security_audit_log`("id", "user_id", "action", "ip_address", "user_agent", "metadata", "success", "created_at") SELECT "id", "user_id", "action", "ip_address", "user_agent", "metadata", "success", "created_at" FROM `security_audit_log`; - --- Drop old table +--> statement-breakpoint DROP TABLE `security_audit_log`; - --- Rename new table +--> statement-breakpoint ALTER TABLE `__new_security_audit_log` RENAME TO `security_audit_log`; - --- Re-enable foreign keys -PRAGMA foreign_keys=ON; - --- Recreate indexes +--> statement-breakpoint CREATE INDEX `idx_security_audit_log_user_id` ON `security_audit_log` (`user_id`); +--> statement-breakpoint CREATE INDEX `idx_security_audit_log_action` ON `security_audit_log` (`action`); +--> statement-breakpoint CREATE INDEX `idx_security_audit_log_created_at` ON `security_audit_log` (`created_at`); +--> statement-breakpoint +PRAGMA foreign_keys=ON; From 63715e6f395c5e92ecb948115584f90d28adf1ab Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 11:02:50 -0500 Subject: [PATCH 25/41] fix: address Copilot review feedback on error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security audit logging: - Remove redundant createdAt field setting in application code - Rely solely on SQL DEFAULT from database schema Subscription error handling: - Add TRPCError rethrow guards in catch blocks - Prevents parse errors from being incorrectly retried as fetch errors - Ensures parse errors exit immediately without retry logic All three issues identified by Copilot AI have been resolved. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/auth/security.ts | 4 +--- packages/api/src/routers/subscriptions.ts | 10 ++++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/api/src/auth/security.ts b/packages/api/src/auth/security.ts index a5f20a76..9f17b175 100644 --- a/packages/api/src/auth/security.ts +++ b/packages/api/src/auth/security.ts @@ -50,9 +50,7 @@ export async function logSecurityEvent( userAgent: params.userAgent, metadata: params.metadata ? JSON.stringify(params.metadata) : undefined, success: params.success, - // Explicitly set createdAt for consistency with app time - // DB has SQL DEFAULT but we set it here for defense-in-depth - createdAt: new Date(), + // createdAt uses SQL DEFAULT from schema }); } catch (error) { // Log but don't throw - audit logging shouldn't break the app diff --git a/packages/api/src/routers/subscriptions.ts b/packages/api/src/routers/subscriptions.ts index 1ca8062f..1f43c574 100644 --- a/packages/api/src/routers/subscriptions.ts +++ b/packages/api/src/routers/subscriptions.ts @@ -408,6 +408,11 @@ export const subscriptionsRouter = router({ } } catch (error) { // This catch block is only for FETCH errors now + // If it's a TRPCError (from parse error), rethrow immediately + if (error instanceof TRPCError) { + throw error; + } + lastError = error instanceof Error ? error : new Error(String(error)); // If this is the last attempt or not a transient error, throw @@ -1084,6 +1089,11 @@ export const subscriptionsRouter = router({ } } catch (error) { // This catch block is only for FETCH errors now + // If it's a TRPCError (from parse error), rethrow immediately + if (error instanceof TRPCError) { + throw error; + } + lastError = error instanceof Error ? error : new Error(String(error)); // If this is the last attempt, throw with full context From 2ce5a6a027fb879699ab2d7f6ab8b4c34071851f Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 11:06:35 -0500 Subject: [PATCH 26/41] refactor(articles): consolidate duplicate @/db/utils import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Merge executeBatch import with existing @/db/utils imports - Group @/db/* imports together for better organization - Remove duplicate import statement ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/routers/articles.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/api/src/routers/articles.ts b/packages/api/src/routers/articles.ts index ea93a627..6cf9278c 100644 --- a/packages/api/src/routers/articles.ts +++ b/packages/api/src/routers/articles.ts @@ -18,11 +18,10 @@ import { applyCategoryFilter, buildArticlesWhereConditions, } from "./articles-helpers"; -import { D1_MAX_PARAMETERS, chunkArray } from "@/db/utils"; import * as schema from "@/db/schema"; -import { executeBatch } from "@/db/utils"; -import { withQueryMetrics } from "@/utils/db-metrics"; +import { D1_MAX_PARAMETERS, chunkArray, executeBatch } from "@/db/utils"; import { upsertArticleState } from "@/db/helpers"; +import { withQueryMetrics } from "@/utils/db-metrics"; import * as Sentry from "@/utils/sentry"; /** From 23e386bc851e21ea8a3edeffa04d90fdba46ab11 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 11:22:34 -0500 Subject: [PATCH 27/41] fix(docker): resolve pnpm lockfile mismatch in Docker builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #75 ## Problem Docker Compose builds were failing with "ERR_PNPM_OUTDATED_LOCKFILE" because the build context was set to individual package directories (./packages/api, ./packages/app), but pnpm workspaces require the monorepo root context to access the shared pnpm-lock.yaml. The individual package lockfiles were outdated and shouldn't exist in a pnpm workspace setup. ## Solution 1. Changed Docker build context from individual packages to monorepo root (.) 2. Updated Dockerfiles to copy workspace files (pnpm-workspace.yaml, root pnpm-lock.yaml) 3. Updated Dockerfiles to copy all needed package.json files 4. Removed outdated individual package lockfiles 5. Added packages/*/pnpm-lock.yaml to .gitignore 6. Updated deployment documentation ## Changes - docker-compose.yml: Changed context from ./packages/* to . (monorepo root) - packages/api/Dockerfile: Copy workspace files and all needed packages - packages/app/Dockerfile: Copy workspace files and all needed packages - Removed packages/api/pnpm-lock.yaml (outdated) - Removed packages/app/pnpm-lock.yaml (outdated) - .gitignore: Prevent individual package lockfiles - docs/deployment.md: Updated to reflect new build context ## Testing โœ… Tested with `docker compose build --no-cache` โœ… Both API and app containers build successfully ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .gitignore | 3 + docker-compose.yml | 8 +- docs/deployment.md | 14 +- packages/api/Dockerfile | 31 +- packages/api/pnpm-lock.yaml | 3616 ------------------- packages/app/Dockerfile | 15 +- packages/app/pnpm-lock.yaml | 6804 ----------------------------------- 7 files changed, 52 insertions(+), 10439 deletions(-) delete mode 100644 packages/api/pnpm-lock.yaml delete mode 100644 packages/app/pnpm-lock.yaml diff --git a/.gitignore b/.gitignore index 999bd33d..a5235722 100644 --- a/.gitignore +++ b/.gitignore @@ -184,6 +184,9 @@ pnpm-lock.yaml.bak package-lock.json yarn.lock +# Individual package lockfiles (use workspace root lockfile) +packages/*/pnpm-lock.yaml + # ============================================ # Misc # ============================================ diff --git a/docker-compose.yml b/docker-compose.yml index a305fcae..58582c99 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,8 @@ services: api: build: - context: ./packages/api - dockerfile: Dockerfile + context: . + dockerfile: ./packages/api/Dockerfile pull_policy: build container_name: tuvix-api restart: unless-stopped @@ -30,8 +30,8 @@ services: app: build: - context: ./packages/app - dockerfile: Dockerfile + context: . + dockerfile: ./packages/app/Dockerfile args: - VITE_API_URL=${VITE_API_URL:-http://localhost:3001/trpc} pull_policy: build diff --git a/docs/deployment.md b/docs/deployment.md index c4012114..0e5508b2 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -293,15 +293,22 @@ docker compose up -d #### Dockerfile Structure +**Important:** Both Dockerfiles use the monorepo root as the build context and copy workspace files. This ensures the correct `pnpm-lock.yaml` from the workspace root is used. + **API Dockerfile** (`packages/api/Dockerfile`): - Multi-stage build (builder + production) +- Build context: monorepo root (not `packages/api`) +- Copies workspace files (`pnpm-workspace.yaml`, root `pnpm-lock.yaml`) - Installs pnpm 10.19.0 +- Installs dependencies for all needed packages (api + tricorder) - Runs migrations on startup - Exposes port 3001 - Health check on /health endpoint **App Dockerfile** (`packages/app/Dockerfile`): - Multi-stage build with nginx +- Build context: monorepo root (not `packages/app`) +- Copies workspace files (`pnpm-workspace.yaml`, root `pnpm-lock.yaml`) - Accepts VITE_API_URL build arg - SPA routing support - Static asset caching @@ -312,7 +319,9 @@ docker compose up -d ```yaml services: api: - build: ./packages/api + build: + context: . + dockerfile: ./packages/api/Dockerfile ports: - "3001:3001" volumes: @@ -329,7 +338,8 @@ services: app: build: - context: ./packages/app + context: . + dockerfile: ./packages/app/Dockerfile args: - VITE_API_URL=${VITE_API_URL:-http://localhost:3001/trpc} ports: diff --git a/packages/api/Dockerfile b/packages/api/Dockerfile index 9095095d..e6a414e3 100644 --- a/packages/api/Dockerfile +++ b/packages/api/Dockerfile @@ -5,15 +5,22 @@ WORKDIR /app # Install pnpm RUN npm install -g pnpm@10.19.0 +# Copy workspace files +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ + # Copy package files -COPY package.json pnpm-lock.yaml* ./ +COPY packages/api/package.json ./packages/api/ +COPY packages/tricorder/package.json ./packages/tricorder/ + +# Install all dependencies (includes workspace deps) RUN pnpm install --frozen-lockfile --prod=false -# Copy source code -COPY . . +# Copy source code for all needed packages +COPY packages/api ./packages/api +COPY packages/tricorder ./packages/tricorder # Build the application -RUN pnpm build +RUN pnpm --filter @tuvixrss/api build # Production stage FROM node:20-alpine @@ -23,13 +30,19 @@ WORKDIR /app # Install pnpm RUN npm install -g pnpm@10.19.0 -# Copy package files and install production dependencies only -COPY package.json pnpm-lock.yaml* ./ +# Copy workspace files +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ + +# Copy package files +COPY packages/api/package.json ./packages/api/ +COPY packages/tricorder/package.json ./packages/tricorder/ + +# Install production dependencies only RUN pnpm install --frozen-lockfile --prod # Copy built files from builder -COPY --from=builder /app/dist ./dist -COPY --from=builder /app/drizzle ./drizzle +COPY --from=builder /app/packages/api/dist ./packages/api/dist +COPY --from=builder /app/packages/api/drizzle ./packages/api/drizzle # Create data directory for SQLite database RUN mkdir -p /app/data @@ -42,4 +55,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1 # Run migrations then start the server -CMD ["sh", "-c", "node dist/db/migrate-local.js && node dist/entries/node.js"] +CMD ["sh", "-c", "node packages/api/dist/db/migrate-local.js && node packages/api/dist/entries/node.js"] diff --git a/packages/api/pnpm-lock.yaml b/packages/api/pnpm-lock.yaml deleted file mode 100644 index fdfad47c..00000000 --- a/packages/api/pnpm-lock.yaml +++ /dev/null @@ -1,3616 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@trpc/server': - specifier: ^11.7.1 - version: 11.7.1(typescript@5.9.3) - bcrypt: - specifier: ^6.0.0 - version: 6.0.0 - better-sqlite3: - specifier: ^12.4.1 - version: 12.4.1 - cors: - specifier: ^2.8.5 - version: 2.8.5 - drizzle-orm: - specifier: ^0.44.7 - version: 0.44.7(@cloudflare/workers-types@4.20251111.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1) - drizzle-zod: - specifier: ^0.8.3 - version: 0.8.3(drizzle-orm@0.44.7(@cloudflare/workers-types@4.20251111.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1))(zod@4.1.12) - eslint-plugin-drizzle: - specifier: ^0.2.3 - version: 0.2.3(eslint@9.39.1) - express: - specifier: ^5.1.0 - version: 5.1.0 - feedsmith: - specifier: ^2.4.0 - version: 2.4.0 - jose: - specifier: ^6.1.1 - version: 6.1.1 - node-cron: - specifier: ^4.2.1 - version: 4.2.1 - zod: - specifier: ^4.1.12 - version: 4.1.12 - devDependencies: - '@cloudflare/workers-types': - specifier: ^4.20251111.0 - version: 4.20251111.0 - '@types/bcrypt': - specifier: ^6.0.0 - version: 6.0.0 - '@types/better-sqlite3': - specifier: ^7.6.13 - version: 7.6.13 - '@types/cors': - specifier: ^2.8.19 - version: 2.8.19 - '@types/express': - specifier: ^5.0.5 - version: 5.0.5 - '@types/node': - specifier: ^24.10.0 - version: 24.10.0 - '@types/node-cron': - specifier: ^3.0.11 - version: 3.0.11 - '@typescript-eslint/eslint-plugin': - specifier: ^8.46.4 - version: 8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/parser': - specifier: ^8.46.4 - version: 8.46.4(eslint@9.39.1)(typescript@5.9.3) - drizzle-kit: - specifier: ^0.31.6 - version: 0.31.6 - eslint: - specifier: ^9.39.1 - version: 9.39.1 - prettier: - specifier: ^3.6.2 - version: 3.6.2 - tsx: - specifier: ^4.20.6 - version: 4.20.6 - typescript: - specifier: ^5.9.3 - version: 5.9.3 - wrangler: - specifier: ^4.47.0 - version: 4.47.0(@cloudflare/workers-types@4.20251111.0) - -packages: - - '@cloudflare/kv-asset-handler@0.4.0': - resolution: {integrity: sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==} - engines: {node: '>=18.0.0'} - - '@cloudflare/unenv-preset@2.7.10': - resolution: {integrity: sha512-mvsNAiJSduC/9yxv1ZpCxwgAXgcuoDvkl8yaHjxoLpFxXy2ugc6TZK20EKgv4yO0vZhAEKwqJm+eGOzf8Oc45w==} - peerDependencies: - unenv: 2.0.0-rc.24 - workerd: ^1.20251106.1 - peerDependenciesMeta: - workerd: - optional: true - - '@cloudflare/workerd-darwin-64@1.20251109.0': - resolution: {integrity: sha512-GAYXHOgPTJm6F+mOt0/Zf+rL+xPfMp8zAxGN4pqkzJ6QVQA/mNVMMuj22dI5x8+Ey+lCulKC3rNs4K3VE12hlA==} - engines: {node: '>=16'} - cpu: [x64] - os: [darwin] - - '@cloudflare/workerd-darwin-arm64@1.20251109.0': - resolution: {integrity: sha512-fpLJvZi3i+btgrXJcOtKYrbmdnHVTKpaZigoKIcpBX4mbwxUh/GVbrCmOqLebr57asQC+PmBfghUEYniqRgnhA==} - engines: {node: '>=16'} - cpu: [arm64] - os: [darwin] - - '@cloudflare/workerd-linux-64@1.20251109.0': - resolution: {integrity: sha512-5NjCnXQoaySFAGGn10w0rPfmEhTSKTP/k7f3aduvt1syt462+66X7luOME/k2x5EB/Z5L8xvwf3/LejSSZ4EVA==} - engines: {node: '>=16'} - cpu: [x64] - os: [linux] - - '@cloudflare/workerd-linux-arm64@1.20251109.0': - resolution: {integrity: sha512-f2AeJlpSwrEvEV57+JU+vRPL8c/Dv8nwY4XW+YwnzPo2TpbI/zzqloPXQ6PY79ftDfEsJJPzQuaDDPq3UOGJQA==} - engines: {node: '>=16'} - cpu: [arm64] - os: [linux] - - '@cloudflare/workerd-windows-64@1.20251109.0': - resolution: {integrity: sha512-IGo/lzbYoeJdfLkpaKLoeG6C7Rwcf5kXjzV0wO8fLUSmlfOLQvXTIehWc7EkbHFHjPapDqYqR0KsmbizBi68Lg==} - engines: {node: '>=16'} - cpu: [x64] - os: [win32] - - '@cloudflare/workers-types@4.20251111.0': - resolution: {integrity: sha512-C8BgQRJlnxcUGycNr8pSKs7WBDQwc43p3pnuGv+Lc0KR2y6raR/9Rs7/lPqQ086ECYSiNqU6IPcbeszKbg4LXA==} - - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - - '@drizzle-team/brocli@0.10.2': - resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} - - '@emnapi/runtime@1.7.0': - resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==} - - '@esbuild-kit/core-utils@3.3.2': - resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} - deprecated: 'Merged into tsx: https://tsx.is' - - '@esbuild-kit/esm-loader@2.6.5': - resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} - deprecated: 'Merged into tsx: https://tsx.is' - - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.25.4': - resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.18.20': - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.25.4': - resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.18.20': - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.25.4': - resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.18.20': - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.25.4': - resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.18.20': - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.25.4': - resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.18.20': - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.4': - resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.18.20': - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.25.4': - resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.18.20': - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.4': - resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.18.20': - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.25.4': - resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.18.20': - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.25.4': - resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.18.20': - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.25.4': - resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.18.20': - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.25.4': - resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.18.20': - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.25.4': - resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.18.20': - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.25.4': - resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.18.20': - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.4': - resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.18.20': - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.25.4': - resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.18.20': - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.25.4': - resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-arm64@0.25.4': - resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.18.20': - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.4': - resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-arm64@0.25.4': - resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.18.20': - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.4': - resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.18.20': - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.25.4': - resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.18.20': - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.25.4': - resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.18.20': - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.25.4': - resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.18.20': - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.25.4': - resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@eslint-community/eslint-utils@4.9.0': - resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.2': - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/config-array@0.21.1': - resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.4.2': - resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.17.0': - resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.39.1': - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.7': - resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.4.1': - resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} - engines: {node: '>=18.18.0'} - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/retry@0.4.3': - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} - engines: {node: '>=18.18'} - - '@img/sharp-darwin-arm64@0.33.5': - resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.33.5': - resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.0.4': - resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.0.4': - resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.0.4': - resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linux-arm@1.0.5': - resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} - cpu: [arm] - os: [linux] - - '@img/sharp-libvips-linux-s390x@1.0.4': - resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} - cpu: [s390x] - os: [linux] - - '@img/sharp-libvips-linux-x64@1.0.4': - resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} - cpu: [x64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': - resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-x64@1.0.4': - resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} - cpu: [x64] - os: [linux] - - '@img/sharp-linux-arm64@0.33.5': - resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linux-arm@0.33.5': - resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - - '@img/sharp-linux-s390x@0.33.5': - resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - - '@img/sharp-linux-x64@0.33.5': - resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-linuxmusl-arm64@0.33.5': - resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linuxmusl-x64@0.33.5': - resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-wasm32@0.33.5': - resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-ia32@0.33.5': - resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.33.5': - resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@poppinss/colors@4.1.5': - resolution: {integrity: sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw==} - - '@poppinss/dumper@0.6.5': - resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} - - '@poppinss/exception@1.2.2': - resolution: {integrity: sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==} - - '@sindresorhus/is@7.1.1': - resolution: {integrity: sha512-rO92VvpgMc3kfiTjGT52LEtJ8Yc5kCWhZjLQ3LwlA4pSgPpQO7bVpYXParOD8Jwf+cVQECJo3yP/4I8aZtUQTQ==} - engines: {node: '>=18'} - - '@speed-highlight/core@1.2.12': - resolution: {integrity: sha512-uilwrK0Ygyri5dToHYdZSjcvpS2ZwX0w5aSt3GCEN9hrjxWCoeV4Z2DTXuxjwbntaLQIEEAlCeNQss5SoHvAEA==} - - '@trpc/server@11.7.1': - resolution: {integrity: sha512-N3U8LNLIP4g9C7LJ/sLkjuPHwqlvE3bnspzC4DEFVdvx2+usbn70P80E3wj5cjOTLhmhRiwJCSXhlB+MHfGeCw==} - peerDependencies: - typescript: '>=5.7.2' - - '@types/bcrypt@6.0.0': - resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==} - - '@types/better-sqlite3@7.6.13': - resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} - - '@types/body-parser@1.19.6': - resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} - - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - - '@types/cors@2.8.19': - resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/express-serve-static-core@5.1.0': - resolution: {integrity: sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==} - - '@types/express@5.0.5': - resolution: {integrity: sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==} - - '@types/http-errors@2.0.5': - resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/mime@1.3.5': - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - - '@types/node-cron@3.0.11': - resolution: {integrity: sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==} - - '@types/node@24.10.0': - resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==} - - '@types/qs@6.14.0': - resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - - '@types/send@0.17.6': - resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} - - '@types/send@1.2.1': - resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} - - '@types/serve-static@1.15.10': - resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} - - '@typescript-eslint/eslint-plugin@8.46.4': - resolution: {integrity: sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.46.4 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/parser@8.46.4': - resolution: {integrity: sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/project-service@8.46.4': - resolution: {integrity: sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/scope-manager@8.46.4': - resolution: {integrity: sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/tsconfig-utils@8.46.4': - resolution: {integrity: sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/type-utils@8.46.4': - resolution: {integrity: sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/types@8.46.4': - resolution: {integrity: sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.46.4': - resolution: {integrity: sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/utils@8.46.4': - resolution: {integrity: sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/visitor-keys@8.46.4': - resolution: {integrity: sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn-walk@8.3.2: - resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} - engines: {node: '>=0.4.0'} - - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} - engines: {node: '>=0.4.0'} - hasBin: true - - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - bcrypt@6.0.0: - resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==} - engines: {node: '>= 18'} - - better-sqlite3@12.4.1: - resolution: {integrity: sha512-3yVdyZhklTiNrtg+4WqHpJpFDd+WHTg2oM7UcR80GqL05AOV0xEJzc6qNvFYoEtE+hRp1n9MpN6/+4yhlGkDXQ==} - engines: {node: 20.x || 22.x || 23.x || 24.x} - - bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - blake3-wasm@2.1.5: - resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} - - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} - engines: {node: '>=18'} - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - - color@4.2.3: - resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} - engines: {node: '>=12.5.0'} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - content-disposition@1.0.0: - resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} - engines: {node: '>= 0.6'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - - cookie@1.0.2: - resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} - engines: {node: '>=18'} - - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - drizzle-kit@0.31.6: - resolution: {integrity: sha512-/B4e/4pwnx25QwD5xXgdpo1S+077a2VZdosXbItE/oNmUgQwZydGDz9qJYmnQl/b+5IX0rLfwRhrPnroGtrg8Q==} - hasBin: true - - drizzle-orm@0.44.7: - resolution: {integrity: sha512-quIpnYznjU9lHshEOAYLoZ9s3jweleHlZIAWR/jX9gAWNg/JhQ1wj0KGRf7/Zm+obRrYd9GjPVJg790QY9N5AQ==} - peerDependencies: - '@aws-sdk/client-rds-data': '>=3' - '@cloudflare/workers-types': '>=4' - '@electric-sql/pglite': '>=0.2.0' - '@libsql/client': '>=0.10.0' - '@libsql/client-wasm': '>=0.10.0' - '@neondatabase/serverless': '>=0.10.0' - '@op-engineering/op-sqlite': '>=2' - '@opentelemetry/api': ^1.4.1 - '@planetscale/database': '>=1.13' - '@prisma/client': '*' - '@tidbcloud/serverless': '*' - '@types/better-sqlite3': '*' - '@types/pg': '*' - '@types/sql.js': '*' - '@upstash/redis': '>=1.34.7' - '@vercel/postgres': '>=0.8.0' - '@xata.io/client': '*' - better-sqlite3: '>=7' - bun-types: '*' - expo-sqlite: '>=14.0.0' - gel: '>=2' - knex: '*' - kysely: '*' - mysql2: '>=2' - pg: '>=8' - postgres: '>=3' - prisma: '*' - sql.js: '>=1' - sqlite3: '>=5' - peerDependenciesMeta: - '@aws-sdk/client-rds-data': - optional: true - '@cloudflare/workers-types': - optional: true - '@electric-sql/pglite': - optional: true - '@libsql/client': - optional: true - '@libsql/client-wasm': - optional: true - '@neondatabase/serverless': - optional: true - '@op-engineering/op-sqlite': - optional: true - '@opentelemetry/api': - optional: true - '@planetscale/database': - optional: true - '@prisma/client': - optional: true - '@tidbcloud/serverless': - optional: true - '@types/better-sqlite3': - optional: true - '@types/pg': - optional: true - '@types/sql.js': - optional: true - '@upstash/redis': - optional: true - '@vercel/postgres': - optional: true - '@xata.io/client': - optional: true - better-sqlite3: - optional: true - bun-types: - optional: true - expo-sqlite: - optional: true - gel: - optional: true - knex: - optional: true - kysely: - optional: true - mysql2: - optional: true - pg: - optional: true - postgres: - optional: true - prisma: - optional: true - sql.js: - optional: true - sqlite3: - optional: true - - drizzle-zod@0.8.3: - resolution: {integrity: sha512-66yVOuvGhKJnTdiqj1/Xaaz9/qzOdRJADpDa68enqS6g3t0kpNkwNYjUuaeXgZfO/UWuIM9HIhSlJ6C5ZraMww==} - peerDependencies: - drizzle-orm: '>=0.36.0' - zod: ^3.25.0 || ^4.0.0 - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - entities@7.0.0: - resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==} - engines: {node: '>=0.12'} - - error-stack-parser-es@1.0.5: - resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - esbuild-register@3.6.0: - resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} - peerDependencies: - esbuild: '>=0.12 <1' - - esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} - engines: {node: '>=18'} - hasBin: true - - esbuild@0.25.4: - resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} - engines: {node: '>=18'} - hasBin: true - - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - eslint-plugin-drizzle@0.2.3: - resolution: {integrity: sha512-BO+ymHo33IUNoJlC0rbd7HP9EwwpW4VIp49R/tWQF/d2E1K2kgTf0tCXT0v9MSiBr6gGR1LtPwMLapTKEWSg9A==} - peerDependencies: - eslint: '>=8.0.0' - - eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint@9.39.1: - resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - - espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - exit-hook@2.2.1: - resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} - engines: {node: '>=6'} - - expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - - express@5.1.0: - resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} - engines: {node: '>= 18'} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fast-xml-parser@5.3.1: - resolution: {integrity: sha512-jbNkWiv2Ec1A7wuuxk0br0d0aTMUtQ4IkL+l/i1r9PRf6pLXjDgsBsWwO+UyczmQlnehi4Tbc8/KIvxGQe+I/A==} - hasBin: true - - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - - feedsmith@2.4.0: - resolution: {integrity: sha512-2yZSUERJ+ctCpb/ADrMlP/vfBXDKtN6hpVAvHLZaE17WAyXdI2UGq/NsKxBE+Wb3hRF6wTzBoJ4/yEginfIPZQ==} - - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - - file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - finalhandler@2.1.0: - resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} - engines: {node: '>= 0.8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-tsconfig@4.13.0: - resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} - - github-from-package@0.0.0: - resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob-to-regexp@0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - iconv-lite@0.7.0: - resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} - engines: {node: '>=0.10.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} - engines: {node: '>= 4'} - - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - is-arrayish@0.3.4: - resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jose@6.1.1: - resolution: {integrity: sha512-GWSqjfOPf4cWOkBzw5THBjtGPhXKqYnfRBzh4Ni+ArTrQQ9unvmsA3oFLqaYKoKe5sjWmGu5wVKg9Ft1i+LQfg==} - - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@3.0.1: - resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} - engines: {node: '>= 0.6'} - - mime@3.0.0: - resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} - engines: {node: '>=10.0.0'} - hasBin: true - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - miniflare@4.20251109.0: - resolution: {integrity: sha512-fm0J/IFrrx7RT1w3SIoDM5m7zPCa2wBtxBApy6G0QVjd2tx8w0WGlMFop6R49XyTfF1q3LRHCjFMfzJ8YS0RzQ==} - engines: {node: '>=18.0.0'} - hasBin: true - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - napi-build-utils@2.0.0: - resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} - - node-abi@3.80.0: - resolution: {integrity: sha512-LyPuZJcI9HVwzXK1GPxWNzrr+vr8Hp/3UqlmWxxh8p54U1ZbclOqbSog9lWHaCX+dBaiGi6n/hIX+mKu74GmPA==} - engines: {node: '>=10'} - - node-addon-api@8.5.0: - resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} - engines: {node: ^18 || ^20 || >= 21} - - node-cron@4.2.1: - resolution: {integrity: sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==} - engines: {node: '>=6.0.0'} - - node-gyp-build@4.8.4: - resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} - hasBin: true - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-to-regexp@6.3.0: - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - prebuild-install@7.1.3: - resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} - engines: {node: '>=10'} - hasBin: true - - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} - engines: {node: '>=14'} - hasBin: true - - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} - engines: {node: '>=0.6'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - - raw-body@3.0.1: - resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} - engines: {node: '>= 0.10'} - - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true - - send@1.2.0: - resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} - engines: {node: '>= 18'} - - serve-static@2.2.0: - resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} - engines: {node: '>= 18'} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - sharp@0.33.5: - resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - simple-get@4.0.1: - resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} - - simple-swizzle@0.2.4: - resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - - statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} - - stoppable@1.1.0: - resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} - engines: {node: '>=4', npm: '>=6'} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - strnum@2.1.1: - resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} - - supports-color@10.2.2: - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} - engines: {node: '>=18'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - tar-fs@2.1.4: - resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tsx@4.20.6: - resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} - engines: {node: '>=18.0.0'} - hasBin: true - - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - - undici@7.14.0: - resolution: {integrity: sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==} - engines: {node: '>=20.18.1'} - - unenv@2.0.0-rc.24: - resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} - - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - workerd@1.20251109.0: - resolution: {integrity: sha512-VfazMiymlzos0c1t9AhNi0w8gN9+ZbCVLdEE0VDOsI22WYa6yj+pYOhpZzI/mOzCGmk/o1eNjLMkfjWli6aRVg==} - engines: {node: '>=16'} - hasBin: true - - wrangler@4.47.0: - resolution: {integrity: sha512-JP0U8oqUETK9D+ZbrSjFFOxGdufYsS6HsT0vLU1IAQrban9a6woMHdBZlGNn/lt8QA70xv1uFiJK8DUMPzC73A==} - engines: {node: '>=18.0.0'} - hasBin: true - peerDependencies: - '@cloudflare/workers-types': ^4.20251109.0 - peerDependenciesMeta: - '@cloudflare/workers-types': - optional: true - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - youch-core@0.3.3: - resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} - - youch@4.1.0-beta.10: - resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} - - zod@3.22.3: - resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} - - zod@4.1.12: - resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} - -snapshots: - - '@cloudflare/kv-asset-handler@0.4.0': - dependencies: - mime: 3.0.0 - - '@cloudflare/unenv-preset@2.7.10(unenv@2.0.0-rc.24)(workerd@1.20251109.0)': - dependencies: - unenv: 2.0.0-rc.24 - optionalDependencies: - workerd: 1.20251109.0 - - '@cloudflare/workerd-darwin-64@1.20251109.0': - optional: true - - '@cloudflare/workerd-darwin-arm64@1.20251109.0': - optional: true - - '@cloudflare/workerd-linux-64@1.20251109.0': - optional: true - - '@cloudflare/workerd-linux-arm64@1.20251109.0': - optional: true - - '@cloudflare/workerd-windows-64@1.20251109.0': - optional: true - - '@cloudflare/workers-types@4.20251111.0': {} - - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@drizzle-team/brocli@0.10.2': {} - - '@emnapi/runtime@1.7.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild-kit/core-utils@3.3.2': - dependencies: - esbuild: 0.18.20 - source-map-support: 0.5.21 - - '@esbuild-kit/esm-loader@2.6.5': - dependencies: - '@esbuild-kit/core-utils': 3.3.2 - get-tsconfig: 4.13.0 - - '@esbuild/aix-ppc64@0.25.12': - optional: true - - '@esbuild/aix-ppc64@0.25.4': - optional: true - - '@esbuild/android-arm64@0.18.20': - optional: true - - '@esbuild/android-arm64@0.25.12': - optional: true - - '@esbuild/android-arm64@0.25.4': - optional: true - - '@esbuild/android-arm@0.18.20': - optional: true - - '@esbuild/android-arm@0.25.12': - optional: true - - '@esbuild/android-arm@0.25.4': - optional: true - - '@esbuild/android-x64@0.18.20': - optional: true - - '@esbuild/android-x64@0.25.12': - optional: true - - '@esbuild/android-x64@0.25.4': - optional: true - - '@esbuild/darwin-arm64@0.18.20': - optional: true - - '@esbuild/darwin-arm64@0.25.12': - optional: true - - '@esbuild/darwin-arm64@0.25.4': - optional: true - - '@esbuild/darwin-x64@0.18.20': - optional: true - - '@esbuild/darwin-x64@0.25.12': - optional: true - - '@esbuild/darwin-x64@0.25.4': - optional: true - - '@esbuild/freebsd-arm64@0.18.20': - optional: true - - '@esbuild/freebsd-arm64@0.25.12': - optional: true - - '@esbuild/freebsd-arm64@0.25.4': - optional: true - - '@esbuild/freebsd-x64@0.18.20': - optional: true - - '@esbuild/freebsd-x64@0.25.12': - optional: true - - '@esbuild/freebsd-x64@0.25.4': - optional: true - - '@esbuild/linux-arm64@0.18.20': - optional: true - - '@esbuild/linux-arm64@0.25.12': - optional: true - - '@esbuild/linux-arm64@0.25.4': - optional: true - - '@esbuild/linux-arm@0.18.20': - optional: true - - '@esbuild/linux-arm@0.25.12': - optional: true - - '@esbuild/linux-arm@0.25.4': - optional: true - - '@esbuild/linux-ia32@0.18.20': - optional: true - - '@esbuild/linux-ia32@0.25.12': - optional: true - - '@esbuild/linux-ia32@0.25.4': - optional: true - - '@esbuild/linux-loong64@0.18.20': - optional: true - - '@esbuild/linux-loong64@0.25.12': - optional: true - - '@esbuild/linux-loong64@0.25.4': - optional: true - - '@esbuild/linux-mips64el@0.18.20': - optional: true - - '@esbuild/linux-mips64el@0.25.12': - optional: true - - '@esbuild/linux-mips64el@0.25.4': - optional: true - - '@esbuild/linux-ppc64@0.18.20': - optional: true - - '@esbuild/linux-ppc64@0.25.12': - optional: true - - '@esbuild/linux-ppc64@0.25.4': - optional: true - - '@esbuild/linux-riscv64@0.18.20': - optional: true - - '@esbuild/linux-riscv64@0.25.12': - optional: true - - '@esbuild/linux-riscv64@0.25.4': - optional: true - - '@esbuild/linux-s390x@0.18.20': - optional: true - - '@esbuild/linux-s390x@0.25.12': - optional: true - - '@esbuild/linux-s390x@0.25.4': - optional: true - - '@esbuild/linux-x64@0.18.20': - optional: true - - '@esbuild/linux-x64@0.25.12': - optional: true - - '@esbuild/linux-x64@0.25.4': - optional: true - - '@esbuild/netbsd-arm64@0.25.12': - optional: true - - '@esbuild/netbsd-arm64@0.25.4': - optional: true - - '@esbuild/netbsd-x64@0.18.20': - optional: true - - '@esbuild/netbsd-x64@0.25.12': - optional: true - - '@esbuild/netbsd-x64@0.25.4': - optional: true - - '@esbuild/openbsd-arm64@0.25.12': - optional: true - - '@esbuild/openbsd-arm64@0.25.4': - optional: true - - '@esbuild/openbsd-x64@0.18.20': - optional: true - - '@esbuild/openbsd-x64@0.25.12': - optional: true - - '@esbuild/openbsd-x64@0.25.4': - optional: true - - '@esbuild/openharmony-arm64@0.25.12': - optional: true - - '@esbuild/sunos-x64@0.18.20': - optional: true - - '@esbuild/sunos-x64@0.25.12': - optional: true - - '@esbuild/sunos-x64@0.25.4': - optional: true - - '@esbuild/win32-arm64@0.18.20': - optional: true - - '@esbuild/win32-arm64@0.25.12': - optional: true - - '@esbuild/win32-arm64@0.25.4': - optional: true - - '@esbuild/win32-ia32@0.18.20': - optional: true - - '@esbuild/win32-ia32@0.25.12': - optional: true - - '@esbuild/win32-ia32@0.25.4': - optional: true - - '@esbuild/win32-x64@0.18.20': - optional: true - - '@esbuild/win32-x64@0.25.12': - optional: true - - '@esbuild/win32-x64@0.25.4': - optional: true - - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': - dependencies: - eslint: 9.39.1 - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.12.2': {} - - '@eslint/config-array@0.21.1': - dependencies: - '@eslint/object-schema': 2.1.7 - debug: 4.4.3 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@eslint/config-helpers@0.4.2': - dependencies: - '@eslint/core': 0.17.0 - - '@eslint/core@0.17.0': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/eslintrc@3.3.1': - dependencies: - ajv: 6.12.6 - debug: 4.4.3 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@9.39.1': {} - - '@eslint/object-schema@2.1.7': {} - - '@eslint/plugin-kit@0.4.1': - dependencies: - '@eslint/core': 0.17.0 - levn: 0.4.1 - - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.7': - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.4.3 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.4.3': {} - - '@img/sharp-darwin-arm64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.0.4 - optional: true - - '@img/sharp-darwin-x64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.0.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.0.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.0.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.0.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.0.5': - optional: true - - '@img/sharp-libvips-linux-s390x@1.0.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.0.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.0.4': - optional: true - - '@img/sharp-linux-arm64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.0.4 - optional: true - - '@img/sharp-linux-arm@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.0.5 - optional: true - - '@img/sharp-linux-s390x@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.0.4 - optional: true - - '@img/sharp-linux-x64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.0.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - optional: true - - '@img/sharp-wasm32@0.33.5': - dependencies: - '@emnapi/runtime': 1.7.0 - optional: true - - '@img/sharp-win32-ia32@0.33.5': - optional: true - - '@img/sharp-win32-x64@0.33.5': - optional: true - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - - '@poppinss/colors@4.1.5': - dependencies: - kleur: 4.1.5 - - '@poppinss/dumper@0.6.5': - dependencies: - '@poppinss/colors': 4.1.5 - '@sindresorhus/is': 7.1.1 - supports-color: 10.2.2 - - '@poppinss/exception@1.2.2': {} - - '@sindresorhus/is@7.1.1': {} - - '@speed-highlight/core@1.2.12': {} - - '@trpc/server@11.7.1(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - - '@types/bcrypt@6.0.0': - dependencies: - '@types/node': 24.10.0 - - '@types/better-sqlite3@7.6.13': - dependencies: - '@types/node': 24.10.0 - - '@types/body-parser@1.19.6': - dependencies: - '@types/connect': 3.4.38 - '@types/node': 24.10.0 - - '@types/connect@3.4.38': - dependencies: - '@types/node': 24.10.0 - - '@types/cors@2.8.19': - dependencies: - '@types/node': 24.10.0 - - '@types/estree@1.0.8': {} - - '@types/express-serve-static-core@5.1.0': - dependencies: - '@types/node': 24.10.0 - '@types/qs': 6.14.0 - '@types/range-parser': 1.2.7 - '@types/send': 1.2.1 - - '@types/express@5.0.5': - dependencies: - '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 5.1.0 - '@types/serve-static': 1.15.10 - - '@types/http-errors@2.0.5': {} - - '@types/json-schema@7.0.15': {} - - '@types/mime@1.3.5': {} - - '@types/node-cron@3.0.11': {} - - '@types/node@24.10.0': - dependencies: - undici-types: 7.16.0 - - '@types/qs@6.14.0': {} - - '@types/range-parser@1.2.7': {} - - '@types/send@0.17.6': - dependencies: - '@types/mime': 1.3.5 - '@types/node': 24.10.0 - - '@types/send@1.2.1': - dependencies: - '@types/node': 24.10.0 - - '@types/serve-static@1.15.10': - dependencies: - '@types/http-errors': 2.0.5 - '@types/node': 24.10.0 - '@types/send': 0.17.6 - - '@typescript-eslint/eslint-plugin@8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.46.4(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/type-utils': 8.46.4(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.4(eslint@9.39.1)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.4 - eslint: 9.39.1 - graphemer: 1.4.0 - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.46.4(eslint@9.39.1)(typescript@5.9.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.4 - debug: 4.4.3 - eslint: 9.39.1 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.46.4(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) - '@typescript-eslint/types': 8.46.4 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.46.4': - dependencies: - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/visitor-keys': 8.46.4 - - '@typescript-eslint/tsconfig-utils@8.46.4(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - - '@typescript-eslint/type-utils@8.46.4(eslint@9.39.1)(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.4(eslint@9.39.1)(typescript@5.9.3) - debug: 4.4.3 - eslint: 9.39.1 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@8.46.4': {} - - '@typescript-eslint/typescript-estree@8.46.4(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.46.4(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.9.3) - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/visitor-keys': 8.46.4 - debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.46.4(eslint@9.39.1)(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) - '@typescript-eslint/scope-manager': 8.46.4 - '@typescript-eslint/types': 8.46.4 - '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.9.3) - eslint: 9.39.1 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.46.4': - dependencies: - '@typescript-eslint/types': 8.46.4 - eslint-visitor-keys: 4.2.1 - - accepts@2.0.0: - dependencies: - mime-types: 3.0.1 - negotiator: 1.0.0 - - acorn-jsx@5.3.2(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - - acorn-walk@8.3.2: {} - - acorn@8.14.0: {} - - acorn@8.15.0: {} - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - argparse@2.0.1: {} - - balanced-match@1.0.2: {} - - base64-js@1.5.1: {} - - bcrypt@6.0.0: - dependencies: - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - - better-sqlite3@12.4.1: - dependencies: - bindings: 1.5.0 - prebuild-install: 7.1.3 - - bindings@1.5.0: - dependencies: - file-uri-to-path: 1.0.0 - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - blake3-wasm@2.1.5: {} - - body-parser@2.2.0: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.3 - http-errors: 2.0.0 - iconv-lite: 0.6.3 - on-finished: 2.4.1 - qs: 6.14.0 - raw-body: 3.0.1 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - buffer-from@1.1.2: {} - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - bytes@3.1.2: {} - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - callsites@3.1.0: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chownr@1.1.4: {} - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - color-string@1.9.1: - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.4 - - color@4.2.3: - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - - concat-map@0.0.1: {} - - content-disposition@1.0.0: - dependencies: - safe-buffer: 5.2.1 - - content-type@1.0.5: {} - - cookie-signature@1.2.2: {} - - cookie@0.7.2: {} - - cookie@1.0.2: {} - - cors@2.8.5: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - deep-extend@0.6.0: {} - - deep-is@0.1.4: {} - - depd@2.0.0: {} - - detect-libc@2.1.2: {} - - drizzle-kit@0.31.6: - dependencies: - '@drizzle-team/brocli': 0.10.2 - '@esbuild-kit/esm-loader': 2.6.5 - esbuild: 0.25.12 - esbuild-register: 3.6.0(esbuild@0.25.12) - transitivePeerDependencies: - - supports-color - - drizzle-orm@0.44.7(@cloudflare/workers-types@4.20251111.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1): - optionalDependencies: - '@cloudflare/workers-types': 4.20251111.0 - '@types/better-sqlite3': 7.6.13 - better-sqlite3: 12.4.1 - - drizzle-zod@0.8.3(drizzle-orm@0.44.7(@cloudflare/workers-types@4.20251111.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1))(zod@4.1.12): - dependencies: - drizzle-orm: 0.44.7(@cloudflare/workers-types@4.20251111.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.4.1) - zod: 4.1.12 - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - ee-first@1.1.1: {} - - encodeurl@2.0.0: {} - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - entities@7.0.0: {} - - error-stack-parser-es@1.0.5: {} - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - esbuild-register@3.6.0(esbuild@0.25.12): - dependencies: - debug: 4.4.3 - esbuild: 0.25.12 - transitivePeerDependencies: - - supports-color - - esbuild@0.18.20: - optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 - - esbuild@0.25.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 - - esbuild@0.25.4: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.4 - '@esbuild/android-arm': 0.25.4 - '@esbuild/android-arm64': 0.25.4 - '@esbuild/android-x64': 0.25.4 - '@esbuild/darwin-arm64': 0.25.4 - '@esbuild/darwin-x64': 0.25.4 - '@esbuild/freebsd-arm64': 0.25.4 - '@esbuild/freebsd-x64': 0.25.4 - '@esbuild/linux-arm': 0.25.4 - '@esbuild/linux-arm64': 0.25.4 - '@esbuild/linux-ia32': 0.25.4 - '@esbuild/linux-loong64': 0.25.4 - '@esbuild/linux-mips64el': 0.25.4 - '@esbuild/linux-ppc64': 0.25.4 - '@esbuild/linux-riscv64': 0.25.4 - '@esbuild/linux-s390x': 0.25.4 - '@esbuild/linux-x64': 0.25.4 - '@esbuild/netbsd-arm64': 0.25.4 - '@esbuild/netbsd-x64': 0.25.4 - '@esbuild/openbsd-arm64': 0.25.4 - '@esbuild/openbsd-x64': 0.25.4 - '@esbuild/sunos-x64': 0.25.4 - '@esbuild/win32-arm64': 0.25.4 - '@esbuild/win32-ia32': 0.25.4 - '@esbuild/win32-x64': 0.25.4 - - escape-html@1.0.3: {} - - escape-string-regexp@4.0.0: {} - - eslint-plugin-drizzle@0.2.3(eslint@9.39.1): - dependencies: - eslint: 9.39.1 - - eslint-scope@8.4.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.1: {} - - eslint@9.39.1: - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.1 - '@eslint/config-helpers': 0.4.2 - '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.1 - '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - transitivePeerDependencies: - - supports-color - - espree@10.4.0: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 4.2.1 - - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - - esutils@2.0.3: {} - - etag@1.8.1: {} - - exit-hook@2.2.1: {} - - expand-template@2.0.3: {} - - express@5.1.0: - dependencies: - accepts: 2.0.0 - body-parser: 2.2.0 - content-disposition: 1.0.0 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.0 - fresh: 2.0.0 - http-errors: 2.0.0 - merge-descriptors: 2.0.0 - mime-types: 3.0.1 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.14.0 - range-parser: 1.2.1 - router: 2.2.0 - send: 1.2.0 - serve-static: 2.2.0 - statuses: 2.0.2 - type-is: 2.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - - fast-deep-equal@3.1.3: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fast-xml-parser@5.3.1: - dependencies: - strnum: 2.1.1 - - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - - feedsmith@2.4.0: - dependencies: - entities: 7.0.0 - fast-xml-parser: 5.3.1 - - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - - file-uri-to-path@1.0.0: {} - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - finalhandler@2.1.0: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@4.0.1: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - - flatted@3.3.3: {} - - forwarded@0.2.0: {} - - fresh@2.0.0: {} - - fs-constants@1.0.0: {} - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-tsconfig@4.13.0: - dependencies: - resolve-pkg-maps: 1.0.0 - - github-from-package@0.0.0: {} - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - glob-to-regexp@0.4.1: {} - - globals@14.0.0: {} - - gopd@1.2.0: {} - - graphemer@1.4.0: {} - - has-flag@4.0.0: {} - - has-symbols@1.1.0: {} - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - - iconv-lite@0.7.0: - dependencies: - safer-buffer: 2.1.2 - - ieee754@1.2.1: {} - - ignore@5.3.2: {} - - ignore@7.0.5: {} - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} - - inherits@2.0.4: {} - - ini@1.3.8: {} - - ipaddr.js@1.9.1: {} - - is-arrayish@0.3.4: {} - - is-extglob@2.1.1: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-number@7.0.0: {} - - is-promise@4.0.0: {} - - isexe@2.0.0: {} - - jose@6.1.1: {} - - js-yaml@4.1.0: - dependencies: - argparse: 2.0.1 - - json-buffer@3.0.1: {} - - json-schema-traverse@0.4.1: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - kleur@4.1.5: {} - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - lodash.merge@4.6.2: {} - - math-intrinsics@1.1.0: {} - - media-typer@1.1.0: {} - - merge-descriptors@2.0.0: {} - - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mime-db@1.54.0: {} - - mime-types@3.0.1: - dependencies: - mime-db: 1.54.0 - - mime@3.0.0: {} - - mimic-response@3.1.0: {} - - miniflare@4.20251109.0: - dependencies: - '@cspotcode/source-map-support': 0.8.1 - acorn: 8.14.0 - acorn-walk: 8.3.2 - exit-hook: 2.2.1 - glob-to-regexp: 0.4.1 - sharp: 0.33.5 - stoppable: 1.1.0 - undici: 7.14.0 - workerd: 1.20251109.0 - ws: 8.18.0 - youch: 4.1.0-beta.10 - zod: 3.22.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minimist@1.2.8: {} - - mkdirp-classic@0.5.3: {} - - ms@2.1.3: {} - - napi-build-utils@2.0.0: {} - - natural-compare@1.4.0: {} - - negotiator@1.0.0: {} - - node-abi@3.80.0: - dependencies: - semver: 7.7.3 - - node-addon-api@8.5.0: {} - - node-cron@4.2.1: {} - - node-gyp-build@4.8.4: {} - - object-assign@4.1.1: {} - - object-inspect@1.13.4: {} - - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parseurl@1.3.3: {} - - path-exists@4.0.0: {} - - path-key@3.1.1: {} - - path-to-regexp@6.3.0: {} - - path-to-regexp@8.3.0: {} - - pathe@2.0.3: {} - - picomatch@2.3.1: {} - - prebuild-install@7.1.3: - dependencies: - detect-libc: 2.1.2 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 2.0.0 - node-abi: 3.80.0 - pump: 3.0.3 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.4 - tunnel-agent: 0.6.0 - - prelude-ls@1.2.1: {} - - prettier@3.6.2: {} - - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - punycode@2.3.1: {} - - qs@6.14.0: - dependencies: - side-channel: 1.1.0 - - queue-microtask@1.2.3: {} - - range-parser@1.2.1: {} - - raw-body@3.0.1: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.7.0 - unpipe: 1.0.0 - - rc@1.2.8: - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - resolve-from@4.0.0: {} - - resolve-pkg-maps@1.0.0: {} - - reusify@1.1.0: {} - - router@2.2.0: - dependencies: - debug: 4.4.3 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.3.0 - transitivePeerDependencies: - - supports-color - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-buffer@5.2.1: {} - - safer-buffer@2.1.2: {} - - semver@7.7.3: {} - - send@1.2.0: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.0 - mime-types: 3.0.1 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - serve-static@2.2.0: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.0 - transitivePeerDependencies: - - supports-color - - setprototypeof@1.2.0: {} - - sharp@0.33.5: - dependencies: - color: 4.2.3 - detect-libc: 2.1.2 - semver: 7.7.3 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.5 - '@img/sharp-darwin-x64': 0.33.5 - '@img/sharp-libvips-darwin-arm64': 1.0.4 - '@img/sharp-libvips-darwin-x64': 1.0.4 - '@img/sharp-libvips-linux-arm': 1.0.5 - '@img/sharp-libvips-linux-arm64': 1.0.4 - '@img/sharp-libvips-linux-s390x': 1.0.4 - '@img/sharp-libvips-linux-x64': 1.0.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - '@img/sharp-linux-arm': 0.33.5 - '@img/sharp-linux-arm64': 0.33.5 - '@img/sharp-linux-s390x': 0.33.5 - '@img/sharp-linux-x64': 0.33.5 - '@img/sharp-linuxmusl-arm64': 0.33.5 - '@img/sharp-linuxmusl-x64': 0.33.5 - '@img/sharp-wasm32': 0.33.5 - '@img/sharp-win32-ia32': 0.33.5 - '@img/sharp-win32-x64': 0.33.5 - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - simple-concat@1.0.1: {} - - simple-get@4.0.1: - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - - simple-swizzle@0.2.4: - dependencies: - is-arrayish: 0.3.4 - - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map@0.6.1: {} - - statuses@2.0.1: {} - - statuses@2.0.2: {} - - stoppable@1.1.0: {} - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - strip-json-comments@2.0.1: {} - - strip-json-comments@3.1.1: {} - - strnum@2.1.1: {} - - supports-color@10.2.2: {} - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - tar-fs@2.1.4: - dependencies: - chownr: 1.1.4 - mkdirp-classic: 0.5.3 - pump: 3.0.3 - tar-stream: 2.2.0 - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.5 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - toidentifier@1.0.1: {} - - ts-api-utils@2.1.0(typescript@5.9.3): - dependencies: - typescript: 5.9.3 - - tslib@2.8.1: - optional: true - - tsx@4.20.6: - dependencies: - esbuild: 0.25.12 - get-tsconfig: 4.13.0 - optionalDependencies: - fsevents: 2.3.3 - - tunnel-agent@0.6.0: - dependencies: - safe-buffer: 5.2.1 - - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-is@2.0.1: - dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 - mime-types: 3.0.1 - - typescript@5.9.3: {} - - undici-types@7.16.0: {} - - undici@7.14.0: {} - - unenv@2.0.0-rc.24: - dependencies: - pathe: 2.0.3 - - unpipe@1.0.0: {} - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - util-deprecate@1.0.2: {} - - vary@1.1.2: {} - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - word-wrap@1.2.5: {} - - workerd@1.20251109.0: - optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20251109.0 - '@cloudflare/workerd-darwin-arm64': 1.20251109.0 - '@cloudflare/workerd-linux-64': 1.20251109.0 - '@cloudflare/workerd-linux-arm64': 1.20251109.0 - '@cloudflare/workerd-windows-64': 1.20251109.0 - - wrangler@4.47.0(@cloudflare/workers-types@4.20251111.0): - dependencies: - '@cloudflare/kv-asset-handler': 0.4.0 - '@cloudflare/unenv-preset': 2.7.10(unenv@2.0.0-rc.24)(workerd@1.20251109.0) - blake3-wasm: 2.1.5 - esbuild: 0.25.4 - miniflare: 4.20251109.0 - path-to-regexp: 6.3.0 - unenv: 2.0.0-rc.24 - workerd: 1.20251109.0 - optionalDependencies: - '@cloudflare/workers-types': 4.20251111.0 - fsevents: 2.3.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - wrappy@1.0.2: {} - - ws@8.18.0: {} - - yocto-queue@0.1.0: {} - - youch-core@0.3.3: - dependencies: - '@poppinss/exception': 1.2.2 - error-stack-parser-es: 1.0.5 - - youch@4.1.0-beta.10: - dependencies: - '@poppinss/colors': 4.1.5 - '@poppinss/dumper': 0.6.5 - '@speed-highlight/core': 1.2.12 - cookie: 1.0.2 - youch-core: 0.3.3 - - zod@3.22.3: {} - - zod@4.1.12: {} diff --git a/packages/app/Dockerfile b/packages/app/Dockerfile index fe845c61..7d266f16 100644 --- a/packages/app/Dockerfile +++ b/packages/app/Dockerfile @@ -5,25 +5,32 @@ WORKDIR /app # Install pnpm RUN npm install -g pnpm@10.19.0 +# Copy workspace files +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ + # Copy package files -COPY package.json pnpm-lock.yaml* ./ +COPY packages/app/package.json ./packages/app/ +COPY packages/api/package.json ./packages/api/ +COPY packages/tricorder/package.json ./packages/tricorder/ + +# Install dependencies RUN pnpm install --frozen-lockfile # Copy source code -COPY . . +COPY packages/app ./packages/app # Build argument for API URL ARG VITE_API_URL=http://localhost:3001/trpc ENV VITE_API_URL=${VITE_API_URL} # Build the application -RUN pnpm build +RUN pnpm --filter @tuvixrss/app build # Production stage with nginx FROM nginx:alpine # Copy built files from builder -COPY --from=builder /app/dist /usr/share/nginx/html +COPY --from=builder /app/packages/app/dist /usr/share/nginx/html # Create nginx configuration for SPA routing and health check RUN echo 'server { \ diff --git a/packages/app/pnpm-lock.yaml b/packages/app/pnpm-lock.yaml deleted file mode 100644 index 094ac454..00000000 --- a/packages/app/pnpm-lock.yaml +++ /dev/null @@ -1,6804 +0,0 @@ -lockfileVersion: "9.0" - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - .: - dependencies: - "@hookform/resolvers": - specifier: ^5.2.2 - version: 5.2.2(react-hook-form@7.66.0(react@19.2.0)) - "@radix-ui/react-alert-dialog": - specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-avatar": - specifier: ^1.1.10 - version: 1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-checkbox": - specifier: ^1.3.3 - version: 1.3.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-collapsible": - specifier: ^1.1.12 - version: 1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-dialog": - specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-dropdown-menu": - specifier: ^2.1.16 - version: 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-label": - specifier: ^2.1.7 - version: 2.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-scroll-area": - specifier: ^1.2.10 - version: 1.2.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-select": - specifier: ^2.2.6 - version: 2.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-separator": - specifier: ^1.1.7 - version: 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-slot": - specifier: ^1.2.3 - version: 1.2.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-switch": - specifier: ^1.2.6 - version: 1.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-tabs": - specifier: ^1.1.13 - version: 1.1.13(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-tooltip": - specifier: ^1.2.8 - version: 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@tailwindcss/vite": - specifier: ^4.1.16 - version: 4.1.16(rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6)) - "@tanstack/react-query": - specifier: ^5.90.6 - version: 5.90.6(react@19.2.0) - "@tanstack/react-router": - specifier: ^1.134.9 - version: 1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@trpc/client": - specifier: ^11.7.1 - version: 11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3) - "@trpc/react-query": - specifier: ^11.7.1 - version: 11.7.1(@tanstack/react-query@5.90.6(react@19.2.0))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3) - "@trpc/server": - specifier: ^11.7.1 - version: 11.7.1(typescript@5.9.3) - class-variance-authority: - specifier: ^0.7.1 - version: 0.7.1 - clsx: - specifier: ^2.1.1 - version: 2.1.1 - lucide-react: - specifier: ^0.552.0 - version: 0.552.0(react@19.2.0) - motion: - specifier: ^12.23.24 - version: 12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - radix-ui: - specifier: ^1.4.3 - version: 1.4.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: - specifier: ^19.1.1 - version: 19.2.0 - react-dom: - specifier: ^19.1.1 - version: 19.2.0(react@19.2.0) - react-hook-form: - specifier: ^7.66.0 - version: 7.66.0(react@19.2.0) - react-intersection-observer: - specifier: ^10.0.0 - version: 10.0.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - sonner: - specifier: ^2.0.7 - version: 2.0.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - tailwind-merge: - specifier: ^3.3.1 - version: 3.3.1 - tailwindcss: - specifier: ^4.1.16 - version: 4.1.16 - zod: - specifier: ^4.1.12 - version: 4.1.12 - devDependencies: - "@eslint/js": - specifier: ^9.36.0 - version: 9.39.0 - "@tanstack/router-cli": - specifier: ^1.134.9 - version: 1.134.9 - "@tanstack/router-plugin": - specifier: ^1.134.9 - version: 1.134.9(@tanstack/react-router@1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6)) - "@types/node": - specifier: ^24.6.0 - version: 24.9.2 - "@types/react": - specifier: ^19.1.16 - version: 19.2.2 - "@types/react-dom": - specifier: ^19.1.9 - version: 19.2.2(@types/react@19.2.2) - "@vitejs/plugin-react": - specifier: ^5.1.0 - version: 5.1.0(rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6)) - "@vitejs/plugin-react-swc": - specifier: ^4.1.0 - version: 4.2.0(rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6)) - eslint: - specifier: ^9.36.0 - version: 9.39.0(jiti@2.6.1) - eslint-plugin-react-hooks: - specifier: ^5.2.0 - version: 5.2.0(eslint@9.39.0(jiti@2.6.1)) - eslint-plugin-react-refresh: - specifier: ^0.4.22 - version: 0.4.24(eslint@9.39.0(jiti@2.6.1)) - globals: - specifier: ^16.4.0 - version: 16.5.0 - knip: - specifier: ^5.67.1 - version: 5.67.1(@types/node@24.9.2)(typescript@5.9.3) - prettier: - specifier: ^3.6.2 - version: 3.6.2 - tw-animate-css: - specifier: ^1.4.0 - version: 1.4.0 - typescript: - specifier: ~5.9.3 - version: 5.9.3 - typescript-eslint: - specifier: ^8.45.0 - version: 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3) - vite: - specifier: npm:rolldown-vite@7.1.14 - version: rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6) - -packages: - "@babel/code-frame@7.27.1": - resolution: - { - integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==, - } - engines: { node: ">=6.9.0" } - - "@babel/compat-data@7.28.5": - resolution: - { - integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==, - } - engines: { node: ">=6.9.0" } - - "@babel/core@7.28.5": - resolution: - { - integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==, - } - engines: { node: ">=6.9.0" } - - "@babel/generator@7.28.5": - resolution: - { - integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-annotate-as-pure@7.27.3": - resolution: - { - integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-compilation-targets@7.27.2": - resolution: - { - integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-create-class-features-plugin@7.28.5": - resolution: - { - integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0 - - "@babel/helper-globals@7.28.0": - resolution: - { - integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-member-expression-to-functions@7.28.5": - resolution: - { - integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-module-imports@7.27.1": - resolution: - { - integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-module-transforms@7.28.3": - resolution: - { - integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0 - - "@babel/helper-optimise-call-expression@7.27.1": - resolution: - { - integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-plugin-utils@7.27.1": - resolution: - { - integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-replace-supers@7.27.1": - resolution: - { - integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0 - - "@babel/helper-skip-transparent-expression-wrappers@7.27.1": - resolution: - { - integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-string-parser@7.27.1": - resolution: - { - integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-validator-identifier@7.28.5": - resolution: - { - integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-validator-option@7.27.1": - resolution: - { - integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==, - } - engines: { node: ">=6.9.0" } - - "@babel/helpers@7.28.4": - resolution: - { - integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==, - } - engines: { node: ">=6.9.0" } - - "@babel/parser@7.28.5": - resolution: - { - integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==, - } - engines: { node: ">=6.0.0" } - hasBin: true - - "@babel/plugin-syntax-jsx@7.27.1": - resolution: - { - integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0-0 - - "@babel/plugin-syntax-typescript@7.27.1": - resolution: - { - integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0-0 - - "@babel/plugin-transform-modules-commonjs@7.27.1": - resolution: - { - integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0-0 - - "@babel/plugin-transform-react-jsx-self@7.27.1": - resolution: - { - integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0-0 - - "@babel/plugin-transform-react-jsx-source@7.27.1": - resolution: - { - integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0-0 - - "@babel/plugin-transform-typescript@7.28.5": - resolution: - { - integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0-0 - - "@babel/preset-typescript@7.28.5": - resolution: - { - integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0-0 - - "@babel/template@7.27.2": - resolution: - { - integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==, - } - engines: { node: ">=6.9.0" } - - "@babel/traverse@7.28.5": - resolution: - { - integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==, - } - engines: { node: ">=6.9.0" } - - "@babel/types@7.28.5": - resolution: - { - integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==, - } - engines: { node: ">=6.9.0" } - - "@emnapi/core@1.6.0": - resolution: - { - integrity: sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==, - } - - "@emnapi/runtime@1.6.0": - resolution: - { - integrity: sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==, - } - - "@emnapi/wasi-threads@1.1.0": - resolution: - { - integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==, - } - - "@esbuild/aix-ppc64@0.25.12": - resolution: - { - integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==, - } - engines: { node: ">=18" } - cpu: [ppc64] - os: [aix] - - "@esbuild/android-arm64@0.25.12": - resolution: - { - integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [android] - - "@esbuild/android-arm@0.25.12": - resolution: - { - integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==, - } - engines: { node: ">=18" } - cpu: [arm] - os: [android] - - "@esbuild/android-x64@0.25.12": - resolution: - { - integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [android] - - "@esbuild/darwin-arm64@0.25.12": - resolution: - { - integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [darwin] - - "@esbuild/darwin-x64@0.25.12": - resolution: - { - integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [darwin] - - "@esbuild/freebsd-arm64@0.25.12": - resolution: - { - integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [freebsd] - - "@esbuild/freebsd-x64@0.25.12": - resolution: - { - integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [freebsd] - - "@esbuild/linux-arm64@0.25.12": - resolution: - { - integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [linux] - - "@esbuild/linux-arm@0.25.12": - resolution: - { - integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==, - } - engines: { node: ">=18" } - cpu: [arm] - os: [linux] - - "@esbuild/linux-ia32@0.25.12": - resolution: - { - integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==, - } - engines: { node: ">=18" } - cpu: [ia32] - os: [linux] - - "@esbuild/linux-loong64@0.25.12": - resolution: - { - integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==, - } - engines: { node: ">=18" } - cpu: [loong64] - os: [linux] - - "@esbuild/linux-mips64el@0.25.12": - resolution: - { - integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==, - } - engines: { node: ">=18" } - cpu: [mips64el] - os: [linux] - - "@esbuild/linux-ppc64@0.25.12": - resolution: - { - integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==, - } - engines: { node: ">=18" } - cpu: [ppc64] - os: [linux] - - "@esbuild/linux-riscv64@0.25.12": - resolution: - { - integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==, - } - engines: { node: ">=18" } - cpu: [riscv64] - os: [linux] - - "@esbuild/linux-s390x@0.25.12": - resolution: - { - integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==, - } - engines: { node: ">=18" } - cpu: [s390x] - os: [linux] - - "@esbuild/linux-x64@0.25.12": - resolution: - { - integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [linux] - - "@esbuild/netbsd-arm64@0.25.12": - resolution: - { - integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [netbsd] - - "@esbuild/netbsd-x64@0.25.12": - resolution: - { - integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [netbsd] - - "@esbuild/openbsd-arm64@0.25.12": - resolution: - { - integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [openbsd] - - "@esbuild/openbsd-x64@0.25.12": - resolution: - { - integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [openbsd] - - "@esbuild/openharmony-arm64@0.25.12": - resolution: - { - integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [openharmony] - - "@esbuild/sunos-x64@0.25.12": - resolution: - { - integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [sunos] - - "@esbuild/win32-arm64@0.25.12": - resolution: - { - integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==, - } - engines: { node: ">=18" } - cpu: [arm64] - os: [win32] - - "@esbuild/win32-ia32@0.25.12": - resolution: - { - integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==, - } - engines: { node: ">=18" } - cpu: [ia32] - os: [win32] - - "@esbuild/win32-x64@0.25.12": - resolution: - { - integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==, - } - engines: { node: ">=18" } - cpu: [x64] - os: [win32] - - "@eslint-community/eslint-utils@4.9.0": - resolution: - { - integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - "@eslint-community/regexpp@4.12.2": - resolution: - { - integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, - } - engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } - - "@eslint/config-array@0.21.1": - resolution: - { - integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/config-helpers@0.4.2": - resolution: - { - integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/core@0.17.0": - resolution: - { - integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/eslintrc@3.3.1": - resolution: - { - integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/js@9.39.0": - resolution: - { - integrity: sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/object-schema@2.1.7": - resolution: - { - integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@eslint/plugin-kit@0.4.1": - resolution: - { - integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@floating-ui/core@1.7.3": - resolution: - { - integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==, - } - - "@floating-ui/dom@1.7.4": - resolution: - { - integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==, - } - - "@floating-ui/react-dom@2.1.6": - resolution: - { - integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==, - } - peerDependencies: - react: ">=16.8.0" - react-dom: ">=16.8.0" - - "@floating-ui/utils@0.2.10": - resolution: - { - integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==, - } - - "@hookform/resolvers@5.2.2": - resolution: - { - integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==, - } - peerDependencies: - react-hook-form: ^7.55.0 - - "@humanfs/core@0.19.1": - resolution: - { - integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, - } - engines: { node: ">=18.18.0" } - - "@humanfs/node@0.16.7": - resolution: - { - integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==, - } - engines: { node: ">=18.18.0" } - - "@humanwhocodes/module-importer@1.0.1": - resolution: - { - integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, - } - engines: { node: ">=12.22" } - - "@humanwhocodes/retry@0.4.3": - resolution: - { - integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, - } - engines: { node: ">=18.18" } - - "@jridgewell/gen-mapping@0.3.13": - resolution: - { - integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, - } - - "@jridgewell/remapping@2.3.5": - resolution: - { - integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, - } - - "@jridgewell/resolve-uri@3.1.2": - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, - } - engines: { node: ">=6.0.0" } - - "@jridgewell/sourcemap-codec@1.5.5": - resolution: - { - integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, - } - - "@jridgewell/trace-mapping@0.3.31": - resolution: - { - integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, - } - - "@napi-rs/wasm-runtime@1.0.7": - resolution: - { - integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==, - } - - "@nodelib/fs.scandir@2.1.5": - resolution: - { - integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, - } - engines: { node: ">= 8" } - - "@nodelib/fs.stat@2.0.5": - resolution: - { - integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, - } - engines: { node: ">= 8" } - - "@nodelib/fs.walk@1.2.8": - resolution: - { - integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, - } - engines: { node: ">= 8" } - - "@oxc-project/runtime@0.92.0": - resolution: - { - integrity: sha512-Z7x2dZOmznihvdvCvLKMl+nswtOSVxS2H2ocar+U9xx6iMfTp0VGIrX6a4xB1v80IwOPC7dT1LXIJrY70Xu3Jw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - - "@oxc-project/types@0.93.0": - resolution: - { - integrity: sha512-yNtwmWZIBtJsMr5TEfoZFDxIWV6OdScOpza/f5YxbqUMJk+j6QX3Cf3jgZShGEFYWQJ5j9mJ6jM0tZHu2J9Yrg==, - } - - "@oxc-resolver/binding-android-arm-eabi@11.13.1": - resolution: - { - integrity: sha512-YijiebZnGbKtwhLJXmUkOTS2iFF5Mh7TZb3SpVGrbgH6t2flJn7K+k78FJN7tc2lfixdlI1amkcCbTCgV+2WwQ==, - } - cpu: [arm] - os: [android] - - "@oxc-resolver/binding-android-arm64@11.13.1": - resolution: - { - integrity: sha512-cURsasEvObw/KCi8eRuZhHiT4agR4cui6uWX8ss2z/Ok23f8W+P8fvEZD0iUMIAmHwyAxA93RxNTIKh48zK39A==, - } - cpu: [arm64] - os: [android] - - "@oxc-resolver/binding-darwin-arm64@11.13.1": - resolution: - { - integrity: sha512-IKsn9oeVrbWpbE+PanGr5C4tRPVhVuBh/ZY8I7bbqaxBjemlgKKNGNSq73VDzQjRApJgjjzsVDgkTwTrKivLGg==, - } - cpu: [arm64] - os: [darwin] - - "@oxc-resolver/binding-darwin-x64@11.13.1": - resolution: - { - integrity: sha512-FW9toaDOXSLmP3lYXsXPalQKLs8eXwZCNUOPeng84MExl+ALe0Ik+sif/U6P/nqJgVdVm4MEiZcnnNtQ+Bn29Q==, - } - cpu: [x64] - os: [darwin] - - "@oxc-resolver/binding-freebsd-x64@11.13.1": - resolution: - { - integrity: sha512-9EODydJ8P/DhEmVIdcjLnlDXAw9hot2NLuwY1/6gp3fKNXsqz3s9ch/vlDpq0CMtvjQ3Z4a2P+4IsH5A73Eh/A==, - } - cpu: [x64] - os: [freebsd] - - "@oxc-resolver/binding-linux-arm-gnueabihf@11.13.1": - resolution: - { - integrity: sha512-Ud/q31NNEFXVy9mwO1jbXXsuqYd8ftoweL4z9MZ5wahlncnzPYKcEGSdBfSi7TKct4KU8EdvAxi+F9wdO1dCGw==, - } - cpu: [arm] - os: [linux] - - "@oxc-resolver/binding-linux-arm-musleabihf@11.13.1": - resolution: - { - integrity: sha512-4x/eNAoQ7Ec2n81S2akaBeDbM4ceuy8R4sd41p1ETnM5PBhvBzWSuf75vQp4K1dLyKKPe+fw+uG4eIpgzqvj8A==, - } - cpu: [arm] - os: [linux] - - "@oxc-resolver/binding-linux-arm64-gnu@11.13.1": - resolution: - { - integrity: sha512-435Sf0a1KKjU7jgB5gcisTq6WMxQQVfsmKWAcQ3VhbXU/NpaUUZaezKmZJXNiAO1sUY6/zRJnTaPtsBq9msYlQ==, - } - cpu: [arm64] - os: [linux] - - "@oxc-resolver/binding-linux-arm64-musl@11.13.1": - resolution: - { - integrity: sha512-Okb7KgPJvA/Db0QwdVziuYs5MZQEq9PC5MEDrBK7jmcqQL2RO+mk7oztqSegcNJ7kMyNM7Zi2cN9G69g4Cs3zg==, - } - cpu: [arm64] - os: [linux] - - "@oxc-resolver/binding-linux-ppc64-gnu@11.13.1": - resolution: - { - integrity: sha512-HyM9+MlH7bWQtjtGzhxVMVhIuy2C1+MqavBfSMyY2d9SSdxcKvboMhl/0vTTMH/R94z8n/gP5XSJ1M6/BC30Pw==, - } - cpu: [ppc64] - os: [linux] - - "@oxc-resolver/binding-linux-riscv64-gnu@11.13.1": - resolution: - { - integrity: sha512-ukJFu+798IzODSIupFAbouehJOLqQwhz56VlzRXi+42xtsmtZ+NLla2CXlaw1V9nMB7HLEQU1+XklkeFsIxz4g==, - } - cpu: [riscv64] - os: [linux] - - "@oxc-resolver/binding-linux-riscv64-musl@11.13.1": - resolution: - { - integrity: sha512-gCr05/1CbuKQ/E39pzVjBLE/amtdvFpHeEd6lUOshnoInZ48g33b+1/CNyeO+B1CoiIydYGrkbyIoIeSMWzSsw==, - } - cpu: [riscv64] - os: [linux] - - "@oxc-resolver/binding-linux-s390x-gnu@11.13.1": - resolution: - { - integrity: sha512-ojQVasxjsZGCxt+ygyipCSp74P22WdUToBLM8D9qVm/yehOtxIT8nv0FyQrc4DOpqzGPxQS2OcgvLag+9AhsFg==, - } - cpu: [s390x] - os: [linux] - - "@oxc-resolver/binding-linux-x64-gnu@11.13.1": - resolution: - { - integrity: sha512-Vr28gTydAegrq+qmQu4IvR+LEq3A8amuHdOPSOwMM44cwpIvEDd4MmhimfEqoWjcfVZy9vpd5mPZZY6C/lHq9g==, - } - cpu: [x64] - os: [linux] - - "@oxc-resolver/binding-linux-x64-musl@11.13.1": - resolution: - { - integrity: sha512-a2g2nv3IulLb9lHd8ZDGEnWIpNXcZviLiEKt+PHP3k3d86U1adlL5rNmImjF+eNGReTyttlX/hYNT4UIPo7IjA==, - } - cpu: [x64] - os: [linux] - - "@oxc-resolver/binding-wasm32-wasi@11.13.1": - resolution: - { - integrity: sha512-PhvfJQG6IyI9uN1c5NAZqfl1N9lLF1XdenX+H3aHYHlADPiOgwtpQgBETSD2L3ySeR7jLzJRVFUrWEu4uDz7Lg==, - } - engines: { node: ">=14.0.0" } - cpu: [wasm32] - - "@oxc-resolver/binding-win32-arm64-msvc@11.13.1": - resolution: - { - integrity: sha512-hyKUC0JQbTKoaPw3r9XHWHtj+B/win36VjTyKDd0OjG71UeyAhZiJBjoNJwfmnTIPcQS4YNesjNkqqDe4qN44w==, - } - cpu: [arm64] - os: [win32] - - "@oxc-resolver/binding-win32-ia32-msvc@11.13.1": - resolution: - { - integrity: sha512-0/y+YMQJEd8kltqPTAUi1PHsYTUi/7UL8Jkhh6BODn3VBQIMMfHhyS8MH4geYJLEJUxuRxGKtya57GOTAN2WSw==, - } - cpu: [ia32] - os: [win32] - - "@oxc-resolver/binding-win32-x64-msvc@11.13.1": - resolution: - { - integrity: sha512-0r1P/PDUD936rZShGdfnqNFdozRVgFYrcdajm1ZZ8wMoN594YkjKmlM3z3DB6arS+Bz7RhA9uLXcP74GqZ/lAw==, - } - cpu: [x64] - os: [win32] - - "@radix-ui/number@1.1.1": - resolution: - { - integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==, - } - - "@radix-ui/primitive@1.1.3": - resolution: - { - integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==, - } - - "@radix-ui/react-accessible-icon@1.1.7": - resolution: - { - integrity: sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==, - } - 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 - - "@radix-ui/react-accordion@1.2.12": - resolution: - { - integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==, - } - 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 - - "@radix-ui/react-alert-dialog@1.1.15": - resolution: - { - integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==, - } - 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 - - "@radix-ui/react-arrow@1.1.7": - resolution: - { - integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==, - } - 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 - - "@radix-ui/react-aspect-ratio@1.1.7": - resolution: - { - integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==, - } - 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 - - "@radix-ui/react-avatar@1.1.10": - resolution: - { - integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==, - } - 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 - - "@radix-ui/react-checkbox@1.3.3": - resolution: - { - integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==, - } - 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 - - "@radix-ui/react-collapsible@1.1.12": - resolution: - { - integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==, - } - 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 - - "@radix-ui/react-collection@1.1.7": - resolution: - { - integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==, - } - 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 - - "@radix-ui/react-compose-refs@1.1.2": - resolution: - { - integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-context-menu@2.2.16": - resolution: - { - integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==, - } - 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 - - "@radix-ui/react-context@1.1.2": - resolution: - { - integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-dialog@1.1.15": - resolution: - { - integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==, - } - 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 - - "@radix-ui/react-direction@1.1.1": - resolution: - { - integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-dismissable-layer@1.1.11": - resolution: - { - integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==, - } - 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 - - "@radix-ui/react-dropdown-menu@2.1.16": - resolution: - { - integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==, - } - 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 - - "@radix-ui/react-focus-guards@1.1.3": - resolution: - { - integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-focus-scope@1.1.7": - resolution: - { - integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==, - } - 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 - - "@radix-ui/react-form@0.1.8": - resolution: - { - integrity: sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==, - } - 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 - - "@radix-ui/react-hover-card@1.1.15": - resolution: - { - integrity: sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==, - } - 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 - - "@radix-ui/react-id@1.1.1": - resolution: - { - integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-label@2.1.7": - resolution: - { - integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==, - } - 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 - - "@radix-ui/react-menu@2.1.16": - resolution: - { - integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==, - } - 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 - - "@radix-ui/react-menubar@1.1.16": - resolution: - { - integrity: sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==, - } - 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 - - "@radix-ui/react-navigation-menu@1.2.14": - resolution: - { - integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==, - } - 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 - - "@radix-ui/react-one-time-password-field@0.1.8": - resolution: - { - integrity: sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg==, - } - 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 - - "@radix-ui/react-password-toggle-field@0.1.3": - resolution: - { - integrity: sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw==, - } - 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 - - "@radix-ui/react-popover@1.1.15": - resolution: - { - integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==, - } - 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 - - "@radix-ui/react-popper@1.2.8": - resolution: - { - integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==, - } - 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 - - "@radix-ui/react-portal@1.1.9": - resolution: - { - integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==, - } - 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 - - "@radix-ui/react-presence@1.1.5": - resolution: - { - integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==, - } - 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 - - "@radix-ui/react-primitive@2.1.3": - resolution: - { - integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==, - } - 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 - - "@radix-ui/react-progress@1.1.7": - resolution: - { - integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==, - } - 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 - - "@radix-ui/react-radio-group@1.3.8": - resolution: - { - integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==, - } - 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 - - "@radix-ui/react-roving-focus@1.1.11": - resolution: - { - integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==, - } - 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 - - "@radix-ui/react-scroll-area@1.2.10": - resolution: - { - integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==, - } - 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 - - "@radix-ui/react-select@2.2.6": - resolution: - { - integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==, - } - 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 - - "@radix-ui/react-separator@1.1.7": - resolution: - { - integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==, - } - 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 - - "@radix-ui/react-slider@1.3.6": - resolution: - { - integrity: sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==, - } - 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 - - "@radix-ui/react-slot@1.2.3": - resolution: - { - integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-switch@1.2.6": - resolution: - { - integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==, - } - 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 - - "@radix-ui/react-tabs@1.1.13": - resolution: - { - integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==, - } - 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 - - "@radix-ui/react-toast@1.2.15": - resolution: - { - integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==, - } - 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 - - "@radix-ui/react-toggle-group@1.1.11": - resolution: - { - integrity: sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==, - } - 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 - - "@radix-ui/react-toggle@1.1.10": - resolution: - { - integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==, - } - 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 - - "@radix-ui/react-toolbar@1.1.11": - resolution: - { - integrity: sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==, - } - 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 - - "@radix-ui/react-tooltip@1.2.8": - resolution: - { - integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==, - } - 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 - - "@radix-ui/react-use-callback-ref@1.1.1": - resolution: - { - integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-use-controllable-state@1.2.2": - resolution: - { - integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-use-effect-event@0.0.2": - resolution: - { - integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-use-escape-keydown@1.1.1": - resolution: - { - integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-use-is-hydrated@0.1.0": - resolution: - { - integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-use-layout-effect@1.1.1": - resolution: - { - integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-use-previous@1.1.1": - resolution: - { - integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-use-rect@1.1.1": - resolution: - { - integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-use-size@1.1.1": - resolution: - { - integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==, - } - peerDependencies: - "@types/react": "*" - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - "@radix-ui/react-visually-hidden@1.2.3": - resolution: - { - integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==, - } - 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 - - "@radix-ui/rect@1.1.1": - resolution: - { - integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==, - } - - "@rolldown/binding-android-arm64@1.0.0-beta.41": - resolution: - { - integrity: sha512-Edflndd9lU7JVhVIvJlZhdCj5DkhYDJPIRn4Dx0RUdfc8asP9xHOI5gMd8MesDDx+BJpdIT/uAmVTearteU/mQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [arm64] - os: [android] - - "@rolldown/binding-darwin-arm64@1.0.0-beta.41": - resolution: - { - integrity: sha512-XGCzqfjdk7550PlyZRTBKbypXrB7ATtXhw/+bjtxnklLQs0mKP/XkQVOKyn9qGKSlvH8I56JLYryVxl0PCvSNw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [arm64] - os: [darwin] - - "@rolldown/binding-darwin-x64@1.0.0-beta.41": - resolution: - { - integrity: sha512-Ho6lIwGJed98zub7n0xcRKuEtnZgbxevAmO4x3zn3C3N4GVXZD5xvCvTVxSMoeBJwTcIYzkVDRTIhylQNsTgLQ==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [x64] - os: [darwin] - - "@rolldown/binding-freebsd-x64@1.0.0-beta.41": - resolution: - { - integrity: sha512-ijAZETywvL+gACjbT4zBnCp5ez1JhTRs6OxRN4J+D6AzDRbU2zb01Esl51RP5/8ZOlvB37xxsRQ3X4YRVyYb3g==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [x64] - os: [freebsd] - - "@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.41": - resolution: - { - integrity: sha512-EgIOZt7UildXKFEFvaiLNBXm+4ggQyGe3E5Z1QP9uRcJJs9omihOnm897FwOBQdCuMvI49iBgjFrkhH+wMJ2MA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [arm] - os: [linux] - - "@rolldown/binding-linux-arm64-gnu@1.0.0-beta.41": - resolution: - { - integrity: sha512-F8bUwJq8v/JAU8HSwgF4dztoqJ+FjdyjuvX4//3+Fbe2we9UktFeZ27U4lRMXF1vxWtdV4ey6oCSqI7yUrSEeg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [arm64] - os: [linux] - - "@rolldown/binding-linux-arm64-musl@1.0.0-beta.41": - resolution: - { - integrity: sha512-MioXcCIX/wB1pBnBoJx8q4OGucUAfC1+/X1ilKFsjDK05VwbLZGRgOVD5OJJpUQPK86DhQciNBrfOKDiatxNmg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [arm64] - os: [linux] - - "@rolldown/binding-linux-x64-gnu@1.0.0-beta.41": - resolution: - { - integrity: sha512-m66M61fizvRCwt5pOEiZQMiwBL9/y0bwU/+Kc4Ce/Pef6YfoEkR28y+DzN9rMdjo8Z28NXjsDPq9nH4mXnAP0g==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [x64] - os: [linux] - - "@rolldown/binding-linux-x64-musl@1.0.0-beta.41": - resolution: - { - integrity: sha512-yRxlSfBvWnnfrdtJfvi9lg8xfG5mPuyoSHm0X01oiE8ArmLRvoJGHUTJydCYz+wbK2esbq5J4B4Tq9WAsOlP1Q==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [x64] - os: [linux] - - "@rolldown/binding-openharmony-arm64@1.0.0-beta.41": - resolution: - { - integrity: sha512-PHVxYhBpi8UViS3/hcvQQb9RFqCtvFmFU1PvUoTRiUdBtgHA6fONNHU4x796lgzNlVSD3DO/MZNk1s5/ozSMQg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [arm64] - os: [openharmony] - - "@rolldown/binding-wasm32-wasi@1.0.0-beta.41": - resolution: - { - integrity: sha512-OAfcO37ME6GGWmj9qTaDT7jY4rM0T2z0/8ujdQIJQ2x2nl+ztO32EIwURfmXOK0U1tzkyuaKYvE34Pug/ucXlQ==, - } - engines: { node: ">=14.0.0" } - cpu: [wasm32] - - "@rolldown/binding-win32-arm64-msvc@1.0.0-beta.41": - resolution: - { - integrity: sha512-NIYGuCcuXaq5BC4Q3upbiMBvmZsTsEPG9k/8QKQdmrch+ocSy5Jv9tdpdmXJyighKqm182nh/zBt+tSJkYoNlg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [arm64] - os: [win32] - - "@rolldown/binding-win32-ia32-msvc@1.0.0-beta.41": - resolution: - { - integrity: sha512-kANdsDbE5FkEOb5NrCGBJBCaZ2Sabp3D7d4PRqMYJqyLljwh9mDyYyYSv5+QNvdAmifj+f3lviNEUUuUZPEFPw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [ia32] - os: [win32] - - "@rolldown/binding-win32-x64-msvc@1.0.0-beta.41": - resolution: - { - integrity: sha512-UlpxKmFdik0Y2VjZrgUCgoYArZJiZllXgIipdBRV1hw6uK45UbQabSTW6Kp6enuOu7vouYWftwhuxfpE8J2JAg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - cpu: [x64] - os: [win32] - - "@rolldown/pluginutils@1.0.0-beta.41": - resolution: - { - integrity: sha512-ycMEPrS3StOIeb87BT3/+bu+blEtyvwQ4zmo2IcJQy0Rd1DAAhKksA0iUZ3MYSpJtjlPhg0Eo6mvVS6ggPhRbw==, - } - - "@rolldown/pluginutils@1.0.0-beta.43": - resolution: - { - integrity: sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==, - } - - "@standard-schema/utils@0.3.0": - resolution: - { - integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==, - } - - "@swc/core-darwin-arm64@1.14.0": - resolution: - { - integrity: sha512-uHPC8rlCt04nvYNczWzKVdgnRhxCa3ndKTBBbBpResOZsRmiwRAvByIGh599j+Oo6Z5eyTPrgY+XfJzVmXnN7Q==, - } - engines: { node: ">=10" } - cpu: [arm64] - os: [darwin] - - "@swc/core-darwin-x64@1.14.0": - resolution: - { - integrity: sha512-2SHrlpl68vtePRknv9shvM9YKKg7B9T13tcTg9aFCwR318QTYo+FzsKGmQSv9ox/Ua0Q2/5y2BNjieffJoo4nA==, - } - engines: { node: ">=10" } - cpu: [x64] - os: [darwin] - - "@swc/core-linux-arm-gnueabihf@1.14.0": - resolution: - { - integrity: sha512-SMH8zn01dxt809svetnxpeg/jWdpi6dqHKO3Eb11u4OzU2PK7I5uKS6gf2hx5LlTbcJMFKULZiVwjlQLe8eqtg==, - } - engines: { node: ">=10" } - cpu: [arm] - os: [linux] - - "@swc/core-linux-arm64-gnu@1.14.0": - resolution: - { - integrity: sha512-q2JRu2D8LVqGeHkmpVCljVNltG0tB4o4eYg+dElFwCS8l2Mnt9qurMCxIeo9mgoqz0ax+k7jWtIRHktnVCbjvQ==, - } - engines: { node: ">=10" } - cpu: [arm64] - os: [linux] - - "@swc/core-linux-arm64-musl@1.14.0": - resolution: - { - integrity: sha512-uofpVoPCEUjYIv454ZEZ3sLgMD17nIwlz2z7bsn7rl301Kt/01umFA7MscUovFfAK2IRGck6XB+uulMu6aFhKQ==, - } - engines: { node: ">=10" } - cpu: [arm64] - os: [linux] - - "@swc/core-linux-x64-gnu@1.14.0": - resolution: - { - integrity: sha512-quTTx1Olm05fBfv66DEBuOsOgqdypnZ/1Bh3yGXWY7ANLFeeRpCDZpljD9BSjdsNdPOlwJmEUZXMHtGm3v1TZQ==, - } - engines: { node: ">=10" } - cpu: [x64] - os: [linux] - - "@swc/core-linux-x64-musl@1.14.0": - resolution: - { - integrity: sha512-caaNAu+aIqT8seLtCf08i8C3/UC5ttQujUjejhMcuS1/LoCKtNiUs4VekJd2UGt+pyuuSrQ6dKl8CbCfWvWeXw==, - } - engines: { node: ">=10" } - cpu: [x64] - os: [linux] - - "@swc/core-win32-arm64-msvc@1.14.0": - resolution: - { - integrity: sha512-EeW3jFlT3YNckJ6V/JnTfGcX7UHGyh6/AiCPopZ1HNaGiXVCKHPpVQZicmtyr/UpqxCXLrTgjHOvyMke7YN26A==, - } - engines: { node: ">=10" } - cpu: [arm64] - os: [win32] - - "@swc/core-win32-ia32-msvc@1.14.0": - resolution: - { - integrity: sha512-dPai3KUIcihV5hfoO4QNQF5HAaw8+2bT7dvi8E5zLtecW2SfL3mUZipzampXq5FHll0RSCLzlrXnSx+dBRZIIQ==, - } - engines: { node: ">=10" } - cpu: [ia32] - os: [win32] - - "@swc/core-win32-x64-msvc@1.14.0": - resolution: - { - integrity: sha512-nm+JajGrTqUA6sEHdghDlHMNfH1WKSiuvljhdmBACW4ta4LC3gKurX2qZuiBARvPkephW9V/i5S8QPY1PzFEqg==, - } - engines: { node: ">=10" } - cpu: [x64] - os: [win32] - - "@swc/core@1.14.0": - resolution: - { - integrity: sha512-oExhY90bes5pDTVrei0xlMVosTxwd/NMafIpqsC4dMbRYZ5KB981l/CX8tMnGsagTplj/RcG9BeRYmV6/J5m3w==, - } - engines: { node: ">=10" } - peerDependencies: - "@swc/helpers": ">=0.5.17" - peerDependenciesMeta: - "@swc/helpers": - optional: true - - "@swc/counter@0.1.3": - resolution: - { - integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==, - } - - "@swc/types@0.1.25": - resolution: - { - integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==, - } - - "@tailwindcss/node@4.1.16": - resolution: - { - integrity: sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==, - } - - "@tailwindcss/oxide-android-arm64@4.1.16": - resolution: - { - integrity: sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==, - } - engines: { node: ">= 10" } - cpu: [arm64] - os: [android] - - "@tailwindcss/oxide-darwin-arm64@4.1.16": - resolution: - { - integrity: sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==, - } - engines: { node: ">= 10" } - cpu: [arm64] - os: [darwin] - - "@tailwindcss/oxide-darwin-x64@4.1.16": - resolution: - { - integrity: sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==, - } - engines: { node: ">= 10" } - cpu: [x64] - os: [darwin] - - "@tailwindcss/oxide-freebsd-x64@4.1.16": - resolution: - { - integrity: sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==, - } - engines: { node: ">= 10" } - cpu: [x64] - os: [freebsd] - - "@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16": - resolution: - { - integrity: sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==, - } - engines: { node: ">= 10" } - cpu: [arm] - os: [linux] - - "@tailwindcss/oxide-linux-arm64-gnu@4.1.16": - resolution: - { - integrity: sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==, - } - engines: { node: ">= 10" } - cpu: [arm64] - os: [linux] - - "@tailwindcss/oxide-linux-arm64-musl@4.1.16": - resolution: - { - integrity: sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==, - } - engines: { node: ">= 10" } - cpu: [arm64] - os: [linux] - - "@tailwindcss/oxide-linux-x64-gnu@4.1.16": - resolution: - { - integrity: sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==, - } - engines: { node: ">= 10" } - cpu: [x64] - os: [linux] - - "@tailwindcss/oxide-linux-x64-musl@4.1.16": - resolution: - { - integrity: sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==, - } - engines: { node: ">= 10" } - cpu: [x64] - os: [linux] - - "@tailwindcss/oxide-wasm32-wasi@4.1.16": - resolution: - { - integrity: sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==, - } - engines: { node: ">=14.0.0" } - cpu: [wasm32] - bundledDependencies: - - "@napi-rs/wasm-runtime" - - "@emnapi/core" - - "@emnapi/runtime" - - "@tybys/wasm-util" - - "@emnapi/wasi-threads" - - tslib - - "@tailwindcss/oxide-win32-arm64-msvc@4.1.16": - resolution: - { - integrity: sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==, - } - engines: { node: ">= 10" } - cpu: [arm64] - os: [win32] - - "@tailwindcss/oxide-win32-x64-msvc@4.1.16": - resolution: - { - integrity: sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==, - } - engines: { node: ">= 10" } - cpu: [x64] - os: [win32] - - "@tailwindcss/oxide@4.1.16": - resolution: - { - integrity: sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==, - } - engines: { node: ">= 10" } - - "@tailwindcss/vite@4.1.16": - resolution: - { - integrity: sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg==, - } - peerDependencies: - vite: ^5.2.0 || ^6 || ^7 - - "@tanstack/history@1.133.28": - resolution: - { - integrity: sha512-B7+x7eP2FFvi3fgd3rNH9o/Eixt+pp0zCIdGhnQbAJjFrlwIKGjGnwyJjhWJ5fMQlGks/E2LdDTqEV4W9Plx7g==, - } - engines: { node: ">=12" } - - "@tanstack/query-core@5.90.6": - resolution: - { - integrity: sha512-AnZSLF26R8uX+tqb/ivdrwbVdGemdEDm1Q19qM6pry6eOZ6bEYiY7mWhzXT1YDIPTNEVcZ5kYP9nWjoxDLiIVw==, - } - - "@tanstack/react-query@5.90.6": - resolution: - { - integrity: sha512-gB1sljYjcobZKxjPbKSa31FUTyr+ROaBdoH+wSSs9Dk+yDCmMs+TkTV3PybRRVLC7ax7q0erJ9LvRWnMktnRAw==, - } - peerDependencies: - react: ^18 || ^19 - - "@tanstack/react-router@1.134.9": - resolution: - { - integrity: sha512-JIxFamShs3gRIkOxpgz/3bglbSKZHMrzKASwNFg+sQPVXVPOLtN35D5PuEDAFTPPht9Wv48WWUNYE03ZytnNug==, - } - engines: { node: ">=12" } - peerDependencies: - react: ">=18.0.0 || >=19.0.0" - react-dom: ">=18.0.0 || >=19.0.0" - - "@tanstack/react-store@0.8.0": - resolution: - { - integrity: sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow==, - } - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - "@tanstack/router-cli@1.134.9": - resolution: - { - integrity: sha512-5aE55UaaJcP9Pi1UArlpOQzyGK4JKAfES/toNrc+HDVpNqyhFegZWCumxgdauHFU13YjQXZJAi2dn0ITfD7lFw==, - } - engines: { node: ">=12" } - hasBin: true - - "@tanstack/router-core@1.134.9": - resolution: - { - integrity: sha512-9Vr8tYC59I70DYGVRknRf4vjQMjSfHvmc+iTM8vcpwERBh3Vgkv90f8ol85KHKqjorSsCqMeYFhFt8AM4A4CSw==, - } - engines: { node: ">=12" } - - "@tanstack/router-generator@1.134.9": - resolution: - { - integrity: sha512-yBPX/xCWE/sdEEtCKOtPBl4cQo+G5Tt7UTB0li49CW8qhmD2eFKTQY1enRb68SwFNH5uwToBXFmJkSG1zPaA5Q==, - } - engines: { node: ">=12" } - - "@tanstack/router-plugin@1.134.9": - resolution: - { - integrity: sha512-iD85GvRADpVhRXkVGRwJqprhIXPLNH+O210UjFDQ8RC2Vn92IwKY6sx8fCgwjHtcYgnTdu3p8eIYJ8CfrLazxA==, - } - engines: { node: ">=12" } - peerDependencies: - "@rsbuild/core": ">=1.0.2" - "@tanstack/react-router": ^1.134.9 - vite: ">=5.0.0 || >=6.0.0 || >=7.0.0" - vite-plugin-solid: ^2.11.10 - webpack: ">=5.92.0" - peerDependenciesMeta: - "@rsbuild/core": - optional: true - "@tanstack/react-router": - optional: true - vite: - optional: true - vite-plugin-solid: - optional: true - webpack: - optional: true - - "@tanstack/router-utils@1.133.19": - resolution: - { - integrity: sha512-WEp5D2gPxvlLDRXwD/fV7RXjYtqaqJNXKB/L6OyZEbT+9BG/Ib2d7oG9GSUZNNMGPGYAlhBUOi3xutySsk6rxA==, - } - engines: { node: ">=12" } - - "@tanstack/store@0.8.0": - resolution: - { - integrity: sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ==, - } - - "@tanstack/virtual-file-routes@1.133.19": - resolution: - { - integrity: sha512-IKwZENsK7owmW1Lm5FhuHegY/SyQ8KqtL/7mTSnzoKJgfzhrrf9qwKB1rmkKkt+svUuy/Zw3uVEpZtUzQruWtA==, - } - engines: { node: ">=12" } - - "@trpc/client@11.7.1": - resolution: - { - integrity: sha512-uOnAjElKI892/U6aQMcBHYs3x7mme3Cvv1F87ytBL56rBvs7+DyK7r43zgaXKf13+GtPEI6ex5xjVUfyDW8XcQ==, - } - peerDependencies: - "@trpc/server": 11.7.1 - typescript: ">=5.7.2" - - "@trpc/react-query@11.7.1": - resolution: - { - integrity: sha512-dEHDjIqSTzO8nLlCbtiFBMBwhbSkK1QP7aYVo3nP3sYBna0b+iCtrPXdxVPCSopr9/aIqDTEh+dMRZa7yBgjfQ==, - } - peerDependencies: - "@tanstack/react-query": ^5.80.3 - "@trpc/client": 11.7.1 - "@trpc/server": 11.7.1 - react: ">=18.2.0" - react-dom: ">=18.2.0" - typescript: ">=5.7.2" - - "@trpc/server@11.7.1": - resolution: - { - integrity: sha512-N3U8LNLIP4g9C7LJ/sLkjuPHwqlvE3bnspzC4DEFVdvx2+usbn70P80E3wj5cjOTLhmhRiwJCSXhlB+MHfGeCw==, - } - peerDependencies: - typescript: ">=5.7.2" - - "@tybys/wasm-util@0.10.1": - resolution: - { - integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==, - } - - "@types/babel__core@7.20.5": - resolution: - { - integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==, - } - - "@types/babel__generator@7.27.0": - resolution: - { - integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==, - } - - "@types/babel__template@7.4.4": - resolution: - { - integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==, - } - - "@types/babel__traverse@7.28.0": - resolution: - { - integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==, - } - - "@types/estree@1.0.8": - resolution: - { - integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, - } - - "@types/json-schema@7.0.15": - resolution: - { - integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, - } - - "@types/node@24.9.2": - resolution: - { - integrity: sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==, - } - - "@types/react-dom@19.2.2": - resolution: - { - integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==, - } - peerDependencies: - "@types/react": ^19.2.0 - - "@types/react@19.2.2": - resolution: - { - integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==, - } - - "@typescript-eslint/eslint-plugin@8.46.2": - resolution: - { - integrity: sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - "@typescript-eslint/parser": ^8.46.2 - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - - "@typescript-eslint/parser@8.46.2": - resolution: - { - integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - - "@typescript-eslint/project-service@8.46.2": - resolution: - { - integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - - "@typescript-eslint/scope-manager@8.46.2": - resolution: - { - integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@typescript-eslint/tsconfig-utils@8.46.2": - resolution: - { - integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - - "@typescript-eslint/type-utils@8.46.2": - resolution: - { - integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - - "@typescript-eslint/types@8.46.2": - resolution: - { - integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@typescript-eslint/typescript-estree@8.46.2": - resolution: - { - integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - - "@typescript-eslint/utils@8.46.2": - resolution: - { - integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - - "@typescript-eslint/visitor-keys@8.46.2": - resolution: - { - integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@vitejs/plugin-react-swc@4.2.0": - resolution: - { - integrity: sha512-/tesahXD1qpkGC6FzMoFOJj0RyZdw9xLELOL+6jbElwmWfwOnIVy+IfpY+o9JfD9PKaR/Eyb6DNrvbXpuvA+8Q==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - peerDependencies: - vite: ^4 || ^5 || ^6 || ^7 - - "@vitejs/plugin-react@5.1.0": - resolution: - { - integrity: sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - - acorn-jsx@5.3.2: - resolution: - { - integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, - } - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.15.0: - resolution: - { - integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==, - } - engines: { node: ">=0.4.0" } - hasBin: true - - ajv@6.12.6: - resolution: - { - integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, - } - - ansi-regex@5.0.1: - resolution: - { - integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, - } - engines: { node: ">=8" } - - ansi-styles@4.3.0: - resolution: - { - integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, - } - engines: { node: ">=8" } - - ansis@4.2.0: - resolution: - { - integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==, - } - engines: { node: ">=14" } - - anymatch@3.1.3: - resolution: - { - integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, - } - engines: { node: ">= 8" } - - argparse@2.0.1: - resolution: - { - integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, - } - - aria-hidden@1.2.6: - resolution: - { - integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==, - } - engines: { node: ">=10" } - - ast-types@0.16.1: - resolution: - { - integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==, - } - engines: { node: ">=4" } - - babel-dead-code-elimination@1.0.10: - resolution: - { - integrity: sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA==, - } - - balanced-match@1.0.2: - resolution: - { - integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, - } - - baseline-browser-mapping@2.8.23: - resolution: - { - integrity: sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==, - } - hasBin: true - - binary-extensions@2.3.0: - resolution: - { - integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==, - } - engines: { node: ">=8" } - - brace-expansion@1.1.12: - resolution: - { - integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, - } - - brace-expansion@2.0.2: - resolution: - { - integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==, - } - - braces@3.0.3: - resolution: - { - integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, - } - engines: { node: ">=8" } - - browserslist@4.27.0: - resolution: - { - integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==, - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } - hasBin: true - - callsites@3.1.0: - resolution: - { - integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, - } - engines: { node: ">=6" } - - caniuse-lite@1.0.30001753: - resolution: - { - integrity: sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==, - } - - chalk@4.1.2: - resolution: - { - integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, - } - engines: { node: ">=10" } - - chokidar@3.6.0: - resolution: - { - integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==, - } - engines: { node: ">= 8.10.0" } - - class-variance-authority@0.7.1: - resolution: - { - integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==, - } - - cliui@8.0.1: - resolution: - { - integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, - } - engines: { node: ">=12" } - - clsx@2.1.1: - resolution: - { - integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==, - } - engines: { node: ">=6" } - - color-convert@2.0.1: - resolution: - { - integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, - } - engines: { node: ">=7.0.0" } - - color-name@1.1.4: - resolution: - { - integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, - } - - concat-map@0.0.1: - resolution: - { - integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, - } - - convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } - - cookie-es@2.0.0: - resolution: - { - integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==, - } - - cross-spawn@7.0.6: - resolution: - { - integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, - } - engines: { node: ">= 8" } - - csstype@3.1.3: - resolution: - { - integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==, - } - - debug@4.4.3: - resolution: - { - integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, - } - engines: { node: ">=6.0" } - peerDependencies: - supports-color: "*" - peerDependenciesMeta: - supports-color: - optional: true - - deep-is@0.1.4: - resolution: - { - integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, - } - - detect-libc@2.1.2: - resolution: - { - integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, - } - engines: { node: ">=8" } - - detect-node-es@1.1.0: - resolution: - { - integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==, - } - - diff@8.0.2: - resolution: - { - integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==, - } - engines: { node: ">=0.3.1" } - - electron-to-chromium@1.5.244: - resolution: - { - integrity: sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==, - } - - emoji-regex@8.0.0: - resolution: - { - integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, - } - - enhanced-resolve@5.18.3: - resolution: - { - integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==, - } - engines: { node: ">=10.13.0" } - - esbuild@0.25.12: - resolution: - { - integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==, - } - engines: { node: ">=18" } - hasBin: true - - escalade@3.2.0: - resolution: - { - integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, - } - engines: { node: ">=6" } - - escape-string-regexp@4.0.0: - resolution: - { - integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, - } - engines: { node: ">=10" } - - eslint-plugin-react-hooks@5.2.0: - resolution: - { - integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==, - } - engines: { node: ">=10" } - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - - eslint-plugin-react-refresh@0.4.24: - resolution: - { - integrity: sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==, - } - peerDependencies: - eslint: ">=8.40" - - eslint-scope@8.4.0: - resolution: - { - integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - eslint-visitor-keys@3.4.3: - resolution: - { - integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - - eslint-visitor-keys@4.2.1: - resolution: - { - integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - eslint@9.39.0: - resolution: - { - integrity: sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - hasBin: true - peerDependencies: - jiti: "*" - peerDependenciesMeta: - jiti: - optional: true - - espree@10.4.0: - resolution: - { - integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - esprima@4.0.1: - resolution: - { - integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==, - } - engines: { node: ">=4" } - hasBin: true - - esquery@1.6.0: - resolution: - { - integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==, - } - engines: { node: ">=0.10" } - - esrecurse@4.3.0: - resolution: - { - integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, - } - engines: { node: ">=4.0" } - - estraverse@5.3.0: - resolution: - { - integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, - } - engines: { node: ">=4.0" } - - esutils@2.0.3: - resolution: - { - integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, - } - engines: { node: ">=0.10.0" } - - fast-deep-equal@3.1.3: - resolution: - { - integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, - } - - fast-glob@3.3.3: - resolution: - { - integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, - } - engines: { node: ">=8.6.0" } - - fast-json-stable-stringify@2.1.0: - resolution: - { - integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, - } - - fast-levenshtein@2.0.6: - resolution: - { - integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, - } - - fastq@1.19.1: - resolution: - { - integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==, - } - - fd-package-json@2.0.0: - resolution: - { - integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==, - } - - fdir@6.5.0: - resolution: - { - integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, - } - engines: { node: ">=12.0.0" } - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - file-entry-cache@8.0.0: - resolution: - { - integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, - } - engines: { node: ">=16.0.0" } - - fill-range@7.1.1: - resolution: - { - integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, - } - engines: { node: ">=8" } - - find-up@5.0.0: - resolution: - { - integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, - } - engines: { node: ">=10" } - - flat-cache@4.0.1: - resolution: - { - integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, - } - engines: { node: ">=16" } - - flatted@3.3.3: - resolution: - { - integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==, - } - - formatly@0.3.0: - resolution: - { - integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==, - } - engines: { node: ">=18.3.0" } - hasBin: true - - framer-motion@12.23.24: - resolution: - { - integrity: sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==, - } - peerDependencies: - "@emotion/is-prop-valid": "*" - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - "@emotion/is-prop-valid": - optional: true - react: - optional: true - react-dom: - optional: true - - fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } - os: [darwin] - - gensync@1.0.0-beta.2: - resolution: - { - integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, - } - engines: { node: ">=6.9.0" } - - get-caller-file@2.0.5: - resolution: - { - integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, - } - engines: { node: 6.* || 8.* || >= 10.* } - - get-nonce@1.0.1: - resolution: - { - integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==, - } - engines: { node: ">=6" } - - get-tsconfig@4.13.0: - resolution: - { - integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==, - } - - glob-parent@5.1.2: - resolution: - { - integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, - } - engines: { node: ">= 6" } - - glob-parent@6.0.2: - resolution: - { - integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, - } - engines: { node: ">=10.13.0" } - - globals@14.0.0: - resolution: - { - integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, - } - engines: { node: ">=18" } - - globals@16.5.0: - resolution: - { - integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==, - } - engines: { node: ">=18" } - - graceful-fs@4.2.11: - resolution: - { - integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, - } - - graphemer@1.4.0: - resolution: - { - integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==, - } - - has-flag@4.0.0: - resolution: - { - integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, - } - engines: { node: ">=8" } - - ignore@5.3.2: - resolution: - { - integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, - } - engines: { node: ">= 4" } - - ignore@7.0.5: - resolution: - { - integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==, - } - engines: { node: ">= 4" } - - import-fresh@3.3.1: - resolution: - { - integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, - } - engines: { node: ">=6" } - - imurmurhash@0.1.4: - resolution: - { - integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, - } - engines: { node: ">=0.8.19" } - - is-binary-path@2.1.0: - resolution: - { - integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==, - } - engines: { node: ">=8" } - - is-extglob@2.1.1: - resolution: - { - integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, - } - engines: { node: ">=0.10.0" } - - is-fullwidth-code-point@3.0.0: - resolution: - { - integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, - } - engines: { node: ">=8" } - - is-glob@4.0.3: - resolution: - { - integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, - } - engines: { node: ">=0.10.0" } - - is-number@7.0.0: - resolution: - { - integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, - } - engines: { node: ">=0.12.0" } - - isbot@5.1.31: - resolution: - { - integrity: sha512-DPgQshehErHAqSCKDb3rNW03pa2wS/v5evvUqtxt6TTnHRqAG8FdzcSSJs9656pK6Y+NT7K9R4acEYXLHYfpUQ==, - } - engines: { node: ">=18" } - - isexe@2.0.0: - resolution: - { - integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, - } - - jiti@2.6.1: - resolution: - { - integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==, - } - hasBin: true - - js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } - - js-yaml@4.1.0: - resolution: - { - integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==, - } - hasBin: true - - jsesc@3.1.0: - resolution: - { - integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==, - } - engines: { node: ">=6" } - hasBin: true - - json-buffer@3.0.1: - resolution: - { - integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, - } - - json-schema-traverse@0.4.1: - resolution: - { - integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, - } - - json-stable-stringify-without-jsonify@1.0.1: - resolution: - { - integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, - } - - json5@2.2.3: - resolution: - { - integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, - } - engines: { node: ">=6" } - hasBin: true - - keyv@4.5.4: - resolution: - { - integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, - } - - knip@5.67.1: - resolution: - { - integrity: sha512-U5AtiqnZAbWIxihs5wxFFEZlpKhzRLWlXSGwA79na7wvlX+MsE0rSuU6If+kl/A4o3TDzTtKGZ4SjeLyWkNR/A==, - } - engines: { node: ">=18.18.0" } - hasBin: true - peerDependencies: - "@types/node": ">=18" - typescript: ">=5.0.4 <7" - - levn@0.4.1: - resolution: - { - integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, - } - engines: { node: ">= 0.8.0" } - - lightningcss-android-arm64@1.30.2: - resolution: - { - integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==, - } - engines: { node: ">= 12.0.0" } - cpu: [arm64] - os: [android] - - lightningcss-darwin-arm64@1.30.2: - resolution: - { - integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==, - } - engines: { node: ">= 12.0.0" } - cpu: [arm64] - os: [darwin] - - lightningcss-darwin-x64@1.30.2: - resolution: - { - integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==, - } - engines: { node: ">= 12.0.0" } - cpu: [x64] - os: [darwin] - - lightningcss-freebsd-x64@1.30.2: - resolution: - { - integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==, - } - engines: { node: ">= 12.0.0" } - cpu: [x64] - os: [freebsd] - - lightningcss-linux-arm-gnueabihf@1.30.2: - resolution: - { - integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==, - } - engines: { node: ">= 12.0.0" } - cpu: [arm] - os: [linux] - - lightningcss-linux-arm64-gnu@1.30.2: - resolution: - { - integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==, - } - engines: { node: ">= 12.0.0" } - cpu: [arm64] - os: [linux] - - lightningcss-linux-arm64-musl@1.30.2: - resolution: - { - integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==, - } - engines: { node: ">= 12.0.0" } - cpu: [arm64] - os: [linux] - - lightningcss-linux-x64-gnu@1.30.2: - resolution: - { - integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==, - } - engines: { node: ">= 12.0.0" } - cpu: [x64] - os: [linux] - - lightningcss-linux-x64-musl@1.30.2: - resolution: - { - integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==, - } - engines: { node: ">= 12.0.0" } - cpu: [x64] - os: [linux] - - lightningcss-win32-arm64-msvc@1.30.2: - resolution: - { - integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==, - } - engines: { node: ">= 12.0.0" } - cpu: [arm64] - os: [win32] - - lightningcss-win32-x64-msvc@1.30.2: - resolution: - { - integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==, - } - engines: { node: ">= 12.0.0" } - cpu: [x64] - os: [win32] - - lightningcss@1.30.2: - resolution: - { - integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==, - } - engines: { node: ">= 12.0.0" } - - locate-path@6.0.0: - resolution: - { - integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, - } - engines: { node: ">=10" } - - lodash.merge@4.6.2: - resolution: - { - integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, - } - - lru-cache@5.1.1: - resolution: - { - integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, - } - - lucide-react@0.552.0: - resolution: - { - integrity: sha512-g9WCjmfwqbexSnZE+2cl21PCfXOcqnGeWeMTNAOGEfpPbm/ZF4YIq77Z8qWrxbu660EKuLB4nSLggoKnCb+isw==, - } - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - magic-string@0.30.21: - resolution: - { - integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, - } - - merge2@1.4.1: - resolution: - { - integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, - } - engines: { node: ">= 8" } - - micromatch@4.0.8: - resolution: - { - integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, - } - engines: { node: ">=8.6" } - - minimatch@3.1.2: - resolution: - { - integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, - } - - minimatch@9.0.5: - resolution: - { - integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==, - } - engines: { node: ">=16 || 14 >=14.17" } - - minimist@1.2.8: - resolution: - { - integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, - } - - motion-dom@12.23.23: - resolution: - { - integrity: sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==, - } - - motion-utils@12.23.6: - resolution: - { - integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==, - } - - motion@12.23.24: - resolution: - { - integrity: sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==, - } - peerDependencies: - "@emotion/is-prop-valid": "*" - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - "@emotion/is-prop-valid": - optional: true - react: - optional: true - react-dom: - optional: true - - ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } - - nanoid@3.3.11: - resolution: - { - integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } - hasBin: true - - natural-compare@1.4.0: - resolution: - { - integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, - } - - node-releases@2.0.27: - resolution: - { - integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==, - } - - normalize-path@3.0.0: - resolution: - { - integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, - } - engines: { node: ">=0.10.0" } - - optionator@0.9.4: - resolution: - { - integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, - } - engines: { node: ">= 0.8.0" } - - oxc-resolver@11.13.1: - resolution: - { - integrity: sha512-/MS37pbsjfdujmuiM/qONFToT8zjDh78xOhVOPStG7fiZlE0b8od8XOfLhqovL0NnMR0ojumTUWF4LK/U15qDQ==, - } - - p-limit@3.1.0: - resolution: - { - integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, - } - engines: { node: ">=10" } - - p-locate@5.0.0: - resolution: - { - integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, - } - engines: { node: ">=10" } - - parent-module@1.0.1: - resolution: - { - integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, - } - engines: { node: ">=6" } - - path-exists@4.0.0: - resolution: - { - integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, - } - engines: { node: ">=8" } - - path-key@3.1.1: - resolution: - { - integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, - } - engines: { node: ">=8" } - - pathe@2.0.3: - resolution: - { - integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, - } - - picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } - - picomatch@2.3.1: - resolution: - { - integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, - } - engines: { node: ">=8.6" } - - picomatch@4.0.3: - resolution: - { - integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, - } - engines: { node: ">=12" } - - postcss@8.5.6: - resolution: - { - integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==, - } - engines: { node: ^10 || ^12 || >=14 } - - prelude-ls@1.2.1: - resolution: - { - integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, - } - engines: { node: ">= 0.8.0" } - - prettier@3.6.2: - resolution: - { - integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==, - } - engines: { node: ">=14" } - hasBin: true - - punycode@2.3.1: - resolution: - { - integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, - } - engines: { node: ">=6" } - - queue-microtask@1.2.3: - resolution: - { - integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, - } - - radix-ui@1.4.3: - resolution: - { - integrity: sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA==, - } - 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 - - react-dom@19.2.0: - resolution: - { - integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==, - } - peerDependencies: - react: ^19.2.0 - - react-hook-form@7.66.0: - resolution: - { - integrity: sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==, - } - engines: { node: ">=18.0.0" } - peerDependencies: - react: ^16.8.0 || ^17 || ^18 || ^19 - - react-intersection-observer@10.0.0: - resolution: - { - integrity: sha512-JJRgcnFQoVXmbE5+GXr1OS1NDD1gHk0HyfpLcRf0575IbJz+io8yzs4mWVlfaqOQq1FiVjLvuYAdEEcrrCfveg==, - } - peerDependencies: - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - react-dom: - optional: true - - react-refresh@0.18.0: - resolution: - { - integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==, - } - engines: { node: ">=0.10.0" } - - react-remove-scroll-bar@2.3.8: - resolution: - { - integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==, - } - engines: { node: ">=10" } - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - - react-remove-scroll@2.7.1: - resolution: - { - integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==, - } - engines: { node: ">=10" } - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - react-style-singleton@2.2.3: - resolution: - { - integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==, - } - engines: { node: ">=10" } - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - react@19.2.0: - resolution: - { - integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==, - } - engines: { node: ">=0.10.0" } - - readdirp@3.6.0: - resolution: - { - integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, - } - engines: { node: ">=8.10.0" } - - recast@0.23.11: - resolution: - { - integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==, - } - engines: { node: ">= 4" } - - require-directory@2.1.1: - resolution: - { - integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, - } - engines: { node: ">=0.10.0" } - - resolve-from@4.0.0: - resolution: - { - integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, - } - engines: { node: ">=4" } - - resolve-pkg-maps@1.0.0: - resolution: - { - integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, - } - - reusify@1.1.0: - resolution: - { - integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, - } - engines: { iojs: ">=1.0.0", node: ">=0.10.0" } - - rolldown-vite@7.1.14: - resolution: - { - integrity: sha512-eSiiRJmovt8qDJkGyZuLnbxAOAdie6NCmmd0NkTC0RJI9duiSBTfr8X2mBYJOUFzxQa2USaHmL99J9uMxkjCyw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - hasBin: true - peerDependencies: - "@types/node": ^20.19.0 || >=22.12.0 - esbuild: ^0.25.0 - jiti: ">=1.21.0" - less: ^4.0.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: ">=0.54.8" - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - "@types/node": - optional: true - esbuild: - optional: true - jiti: - optional: true - less: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - rolldown@1.0.0-beta.41: - resolution: - { - integrity: sha512-U+NPR0Bkg3wm61dteD2L4nAM1U9dtaqVrpDXwC36IKRHpEO/Ubpid4Nijpa2imPchcVNHfxVFwSSMJdwdGFUbg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } - hasBin: true - - run-parallel@1.2.0: - resolution: - { - integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, - } - - scheduler@0.27.0: - resolution: - { - integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==, - } - - semver@6.3.1: - resolution: - { - integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, - } - hasBin: true - - semver@7.7.3: - resolution: - { - integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==, - } - engines: { node: ">=10" } - hasBin: true - - seroval-plugins@1.3.3: - resolution: - { - integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==, - } - engines: { node: ">=10" } - peerDependencies: - seroval: ^1.0 - - seroval@1.3.2: - resolution: - { - integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==, - } - engines: { node: ">=10" } - - shebang-command@2.0.0: - resolution: - { - integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, - } - engines: { node: ">=8" } - - shebang-regex@3.0.0: - resolution: - { - integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, - } - engines: { node: ">=8" } - - smol-toml@1.4.2: - resolution: - { - integrity: sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==, - } - engines: { node: ">= 18" } - - sonner@2.0.7: - resolution: - { - integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==, - } - peerDependencies: - react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc - react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc - - source-map-js@1.2.1: - resolution: - { - integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, - } - engines: { node: ">=0.10.0" } - - source-map@0.6.1: - resolution: - { - integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, - } - engines: { node: ">=0.10.0" } - - source-map@0.7.6: - resolution: - { - integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==, - } - engines: { node: ">= 12" } - - string-width@4.2.3: - resolution: - { - integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, - } - engines: { node: ">=8" } - - strip-ansi@6.0.1: - resolution: - { - integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, - } - engines: { node: ">=8" } - - strip-json-comments@3.1.1: - resolution: - { - integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, - } - engines: { node: ">=8" } - - strip-json-comments@5.0.2: - resolution: - { - integrity: sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==, - } - engines: { node: ">=14.16" } - - supports-color@7.2.0: - resolution: - { - integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, - } - engines: { node: ">=8" } - - tailwind-merge@3.3.1: - resolution: - { - integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==, - } - - tailwindcss@4.1.16: - resolution: - { - integrity: sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==, - } - - tapable@2.3.0: - resolution: - { - integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==, - } - engines: { node: ">=6" } - - tiny-invariant@1.3.3: - resolution: - { - integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==, - } - - tiny-warning@1.0.3: - resolution: - { - integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==, - } - - tinyglobby@0.2.15: - resolution: - { - integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, - } - engines: { node: ">=12.0.0" } - - to-regex-range@5.0.1: - resolution: - { - integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, - } - engines: { node: ">=8.0" } - - ts-api-utils@2.1.0: - resolution: - { - integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==, - } - engines: { node: ">=18.12" } - peerDependencies: - typescript: ">=4.8.4" - - tslib@2.8.1: - resolution: - { - integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, - } - - tsx@4.20.6: - resolution: - { - integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==, - } - engines: { node: ">=18.0.0" } - hasBin: true - - tw-animate-css@1.4.0: - resolution: - { - integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==, - } - - type-check@0.4.0: - resolution: - { - integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, - } - engines: { node: ">= 0.8.0" } - - typescript-eslint@8.46.2: - resolution: - { - integrity: sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - - typescript@5.9.3: - resolution: - { - integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, - } - engines: { node: ">=14.17" } - hasBin: true - - undici-types@7.16.0: - resolution: - { - integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==, - } - - unplugin@2.3.10: - resolution: - { - integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==, - } - engines: { node: ">=18.12.0" } - - update-browserslist-db@1.1.4: - resolution: - { - integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==, - } - hasBin: true - peerDependencies: - browserslist: ">= 4.21.0" - - uri-js@4.4.1: - resolution: - { - integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, - } - - use-callback-ref@1.3.3: - resolution: - { - integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==, - } - engines: { node: ">=10" } - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - use-sidecar@1.1.3: - resolution: - { - integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==, - } - engines: { node: ">=10" } - peerDependencies: - "@types/react": "*" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - "@types/react": - optional: true - - use-sync-external-store@1.6.0: - resolution: - { - integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==, - } - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - walk-up-path@4.0.0: - resolution: - { - integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==, - } - engines: { node: 20 || >=22 } - - webpack-virtual-modules@0.6.2: - resolution: - { - integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==, - } - - which@2.0.2: - resolution: - { - integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, - } - engines: { node: ">= 8" } - hasBin: true - - word-wrap@1.2.5: - resolution: - { - integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, - } - engines: { node: ">=0.10.0" } - - wrap-ansi@7.0.0: - resolution: - { - integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, - } - engines: { node: ">=10" } - - y18n@5.0.8: - resolution: - { - integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, - } - engines: { node: ">=10" } - - yallist@3.1.1: - resolution: - { - integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, - } - - yargs-parser@21.1.1: - resolution: - { - integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, - } - engines: { node: ">=12" } - - yargs@17.7.2: - resolution: - { - integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==, - } - engines: { node: ">=12" } - - yocto-queue@0.1.0: - resolution: - { - integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, - } - engines: { node: ">=10" } - - zod@3.25.76: - resolution: - { - integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==, - } - - zod@4.1.12: - resolution: - { - integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==, - } - -snapshots: - "@babel/code-frame@7.27.1": - dependencies: - "@babel/helper-validator-identifier": 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - "@babel/compat-data@7.28.5": {} - - "@babel/core@7.28.5": - dependencies: - "@babel/code-frame": 7.27.1 - "@babel/generator": 7.28.5 - "@babel/helper-compilation-targets": 7.27.2 - "@babel/helper-module-transforms": 7.28.3(@babel/core@7.28.5) - "@babel/helpers": 7.28.4 - "@babel/parser": 7.28.5 - "@babel/template": 7.27.2 - "@babel/traverse": 7.28.5 - "@babel/types": 7.28.5 - "@jridgewell/remapping": 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - "@babel/generator@7.28.5": - dependencies: - "@babel/parser": 7.28.5 - "@babel/types": 7.28.5 - "@jridgewell/gen-mapping": 0.3.13 - "@jridgewell/trace-mapping": 0.3.31 - jsesc: 3.1.0 - - "@babel/helper-annotate-as-pure@7.27.3": - dependencies: - "@babel/types": 7.28.5 - - "@babel/helper-compilation-targets@7.27.2": - dependencies: - "@babel/compat-data": 7.28.5 - "@babel/helper-validator-option": 7.27.1 - browserslist: 4.27.0 - lru-cache: 5.1.1 - semver: 6.3.1 - - "@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)": - dependencies: - "@babel/core": 7.28.5 - "@babel/helper-annotate-as-pure": 7.27.3 - "@babel/helper-member-expression-to-functions": 7.28.5 - "@babel/helper-optimise-call-expression": 7.27.1 - "@babel/helper-replace-supers": 7.27.1(@babel/core@7.28.5) - "@babel/helper-skip-transparent-expression-wrappers": 7.27.1 - "@babel/traverse": 7.28.5 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - "@babel/helper-globals@7.28.0": {} - - "@babel/helper-member-expression-to-functions@7.28.5": - dependencies: - "@babel/traverse": 7.28.5 - "@babel/types": 7.28.5 - transitivePeerDependencies: - - supports-color - - "@babel/helper-module-imports@7.27.1": - dependencies: - "@babel/traverse": 7.28.5 - "@babel/types": 7.28.5 - transitivePeerDependencies: - - supports-color - - "@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)": - dependencies: - "@babel/core": 7.28.5 - "@babel/helper-module-imports": 7.27.1 - "@babel/helper-validator-identifier": 7.28.5 - "@babel/traverse": 7.28.5 - transitivePeerDependencies: - - supports-color - - "@babel/helper-optimise-call-expression@7.27.1": - dependencies: - "@babel/types": 7.28.5 - - "@babel/helper-plugin-utils@7.27.1": {} - - "@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)": - dependencies: - "@babel/core": 7.28.5 - "@babel/helper-member-expression-to-functions": 7.28.5 - "@babel/helper-optimise-call-expression": 7.27.1 - "@babel/traverse": 7.28.5 - transitivePeerDependencies: - - supports-color - - "@babel/helper-skip-transparent-expression-wrappers@7.27.1": - dependencies: - "@babel/traverse": 7.28.5 - "@babel/types": 7.28.5 - transitivePeerDependencies: - - supports-color - - "@babel/helper-string-parser@7.27.1": {} - - "@babel/helper-validator-identifier@7.28.5": {} - - "@babel/helper-validator-option@7.27.1": {} - - "@babel/helpers@7.28.4": - dependencies: - "@babel/template": 7.27.2 - "@babel/types": 7.28.5 - - "@babel/parser@7.28.5": - dependencies: - "@babel/types": 7.28.5 - - "@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)": - dependencies: - "@babel/core": 7.28.5 - "@babel/helper-plugin-utils": 7.27.1 - - "@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)": - dependencies: - "@babel/core": 7.28.5 - "@babel/helper-plugin-utils": 7.27.1 - - "@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)": - dependencies: - "@babel/core": 7.28.5 - "@babel/helper-module-transforms": 7.28.3(@babel/core@7.28.5) - "@babel/helper-plugin-utils": 7.27.1 - transitivePeerDependencies: - - supports-color - - "@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)": - dependencies: - "@babel/core": 7.28.5 - "@babel/helper-plugin-utils": 7.27.1 - - "@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)": - dependencies: - "@babel/core": 7.28.5 - "@babel/helper-plugin-utils": 7.27.1 - - "@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)": - dependencies: - "@babel/core": 7.28.5 - "@babel/helper-annotate-as-pure": 7.27.3 - "@babel/helper-create-class-features-plugin": 7.28.5(@babel/core@7.28.5) - "@babel/helper-plugin-utils": 7.27.1 - "@babel/helper-skip-transparent-expression-wrappers": 7.27.1 - "@babel/plugin-syntax-typescript": 7.27.1(@babel/core@7.28.5) - transitivePeerDependencies: - - supports-color - - "@babel/preset-typescript@7.28.5(@babel/core@7.28.5)": - dependencies: - "@babel/core": 7.28.5 - "@babel/helper-plugin-utils": 7.27.1 - "@babel/helper-validator-option": 7.27.1 - "@babel/plugin-syntax-jsx": 7.27.1(@babel/core@7.28.5) - "@babel/plugin-transform-modules-commonjs": 7.27.1(@babel/core@7.28.5) - "@babel/plugin-transform-typescript": 7.28.5(@babel/core@7.28.5) - transitivePeerDependencies: - - supports-color - - "@babel/template@7.27.2": - dependencies: - "@babel/code-frame": 7.27.1 - "@babel/parser": 7.28.5 - "@babel/types": 7.28.5 - - "@babel/traverse@7.28.5": - dependencies: - "@babel/code-frame": 7.27.1 - "@babel/generator": 7.28.5 - "@babel/helper-globals": 7.28.0 - "@babel/parser": 7.28.5 - "@babel/template": 7.27.2 - "@babel/types": 7.28.5 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - "@babel/types@7.28.5": - dependencies: - "@babel/helper-string-parser": 7.27.1 - "@babel/helper-validator-identifier": 7.28.5 - - "@emnapi/core@1.6.0": - dependencies: - "@emnapi/wasi-threads": 1.1.0 - tslib: 2.8.1 - optional: true - - "@emnapi/runtime@1.6.0": - dependencies: - tslib: 2.8.1 - optional: true - - "@emnapi/wasi-threads@1.1.0": - dependencies: - tslib: 2.8.1 - optional: true - - "@esbuild/aix-ppc64@0.25.12": - optional: true - - "@esbuild/android-arm64@0.25.12": - optional: true - - "@esbuild/android-arm@0.25.12": - optional: true - - "@esbuild/android-x64@0.25.12": - optional: true - - "@esbuild/darwin-arm64@0.25.12": - optional: true - - "@esbuild/darwin-x64@0.25.12": - optional: true - - "@esbuild/freebsd-arm64@0.25.12": - optional: true - - "@esbuild/freebsd-x64@0.25.12": - optional: true - - "@esbuild/linux-arm64@0.25.12": - optional: true - - "@esbuild/linux-arm@0.25.12": - optional: true - - "@esbuild/linux-ia32@0.25.12": - optional: true - - "@esbuild/linux-loong64@0.25.12": - optional: true - - "@esbuild/linux-mips64el@0.25.12": - optional: true - - "@esbuild/linux-ppc64@0.25.12": - optional: true - - "@esbuild/linux-riscv64@0.25.12": - optional: true - - "@esbuild/linux-s390x@0.25.12": - optional: true - - "@esbuild/linux-x64@0.25.12": - optional: true - - "@esbuild/netbsd-arm64@0.25.12": - optional: true - - "@esbuild/netbsd-x64@0.25.12": - optional: true - - "@esbuild/openbsd-arm64@0.25.12": - optional: true - - "@esbuild/openbsd-x64@0.25.12": - optional: true - - "@esbuild/openharmony-arm64@0.25.12": - optional: true - - "@esbuild/sunos-x64@0.25.12": - optional: true - - "@esbuild/win32-arm64@0.25.12": - optional: true - - "@esbuild/win32-ia32@0.25.12": - optional: true - - "@esbuild/win32-x64@0.25.12": - optional: true - - "@eslint-community/eslint-utils@4.9.0(eslint@9.39.0(jiti@2.6.1))": - dependencies: - eslint: 9.39.0(jiti@2.6.1) - eslint-visitor-keys: 3.4.3 - - "@eslint-community/regexpp@4.12.2": {} - - "@eslint/config-array@0.21.1": - dependencies: - "@eslint/object-schema": 2.1.7 - debug: 4.4.3 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - "@eslint/config-helpers@0.4.2": - dependencies: - "@eslint/core": 0.17.0 - - "@eslint/core@0.17.0": - dependencies: - "@types/json-schema": 7.0.15 - - "@eslint/eslintrc@3.3.1": - dependencies: - ajv: 6.12.6 - debug: 4.4.3 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - "@eslint/js@9.39.0": {} - - "@eslint/object-schema@2.1.7": {} - - "@eslint/plugin-kit@0.4.1": - dependencies: - "@eslint/core": 0.17.0 - levn: 0.4.1 - - "@floating-ui/core@1.7.3": - dependencies: - "@floating-ui/utils": 0.2.10 - - "@floating-ui/dom@1.7.4": - dependencies: - "@floating-ui/core": 1.7.3 - "@floating-ui/utils": 0.2.10 - - "@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@floating-ui/dom": 1.7.4 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - - "@floating-ui/utils@0.2.10": {} - - "@hookform/resolvers@5.2.2(react-hook-form@7.66.0(react@19.2.0))": - dependencies: - "@standard-schema/utils": 0.3.0 - react-hook-form: 7.66.0(react@19.2.0) - - "@humanfs/core@0.19.1": {} - - "@humanfs/node@0.16.7": - dependencies: - "@humanfs/core": 0.19.1 - "@humanwhocodes/retry": 0.4.3 - - "@humanwhocodes/module-importer@1.0.1": {} - - "@humanwhocodes/retry@0.4.3": {} - - "@jridgewell/gen-mapping@0.3.13": - dependencies: - "@jridgewell/sourcemap-codec": 1.5.5 - "@jridgewell/trace-mapping": 0.3.31 - - "@jridgewell/remapping@2.3.5": - dependencies: - "@jridgewell/gen-mapping": 0.3.13 - "@jridgewell/trace-mapping": 0.3.31 - - "@jridgewell/resolve-uri@3.1.2": {} - - "@jridgewell/sourcemap-codec@1.5.5": {} - - "@jridgewell/trace-mapping@0.3.31": - dependencies: - "@jridgewell/resolve-uri": 3.1.2 - "@jridgewell/sourcemap-codec": 1.5.5 - - "@napi-rs/wasm-runtime@1.0.7": - dependencies: - "@emnapi/core": 1.6.0 - "@emnapi/runtime": 1.6.0 - "@tybys/wasm-util": 0.10.1 - optional: true - - "@nodelib/fs.scandir@2.1.5": - dependencies: - "@nodelib/fs.stat": 2.0.5 - run-parallel: 1.2.0 - - "@nodelib/fs.stat@2.0.5": {} - - "@nodelib/fs.walk@1.2.8": - dependencies: - "@nodelib/fs.scandir": 2.1.5 - fastq: 1.19.1 - - "@oxc-project/runtime@0.92.0": {} - - "@oxc-project/types@0.93.0": {} - - "@oxc-resolver/binding-android-arm-eabi@11.13.1": - optional: true - - "@oxc-resolver/binding-android-arm64@11.13.1": - optional: true - - "@oxc-resolver/binding-darwin-arm64@11.13.1": - optional: true - - "@oxc-resolver/binding-darwin-x64@11.13.1": - optional: true - - "@oxc-resolver/binding-freebsd-x64@11.13.1": - optional: true - - "@oxc-resolver/binding-linux-arm-gnueabihf@11.13.1": - optional: true - - "@oxc-resolver/binding-linux-arm-musleabihf@11.13.1": - optional: true - - "@oxc-resolver/binding-linux-arm64-gnu@11.13.1": - optional: true - - "@oxc-resolver/binding-linux-arm64-musl@11.13.1": - optional: true - - "@oxc-resolver/binding-linux-ppc64-gnu@11.13.1": - optional: true - - "@oxc-resolver/binding-linux-riscv64-gnu@11.13.1": - optional: true - - "@oxc-resolver/binding-linux-riscv64-musl@11.13.1": - optional: true - - "@oxc-resolver/binding-linux-s390x-gnu@11.13.1": - optional: true - - "@oxc-resolver/binding-linux-x64-gnu@11.13.1": - optional: true - - "@oxc-resolver/binding-linux-x64-musl@11.13.1": - optional: true - - "@oxc-resolver/binding-wasm32-wasi@11.13.1": - dependencies: - "@napi-rs/wasm-runtime": 1.0.7 - optional: true - - "@oxc-resolver/binding-win32-arm64-msvc@11.13.1": - optional: true - - "@oxc-resolver/binding-win32-ia32-msvc@11.13.1": - optional: true - - "@oxc-resolver/binding-win32-x64-msvc@11.13.1": - optional: true - - "@radix-ui/number@1.1.1": {} - - "@radix-ui/primitive@1.1.3": {} - - "@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-visually-hidden": 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-collapsible": 1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-collection": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-dialog": 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-slot": 1.2.3(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-is-hydrated": 0.1.0(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-previous": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-size": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-slot": 1.2.3(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@19.2.0)": - dependencies: - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-menu": 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-context@1.1.2(@types/react@19.2.2)(react@19.2.0)": - dependencies: - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-dismissable-layer": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-focus-guards": 1.1.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-focus-scope": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-portal": 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-slot": 1.2.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - aria-hidden: 1.2.6 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-direction@1.1.1(@types/react@19.2.2)(react@19.2.0)": - dependencies: - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-escape-keydown": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-menu": 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.2)(react@19.2.0)": - dependencies: - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-form@0.1.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-label": 2.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-dismissable-layer": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-popper": 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-portal": 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-id@1.1.1(@types/react@19.2.2)(react@19.2.0)": - dependencies: - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-label@2.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-collection": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-dismissable-layer": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-focus-guards": 1.1.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-focus-scope": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-popper": 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-portal": 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-roving-focus": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-slot": 1.2.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - aria-hidden: 1.2.6 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-collection": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-menu": 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-roving-focus": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-collection": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-dismissable-layer": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-previous": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-visually-hidden": 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/number": 1.1.1 - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-collection": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-roving-focus": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-effect-event": 0.0.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-is-hydrated": 0.1.0(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-effect-event": 0.0.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-is-hydrated": 0.1.0(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-dismissable-layer": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-focus-guards": 1.1.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-focus-scope": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-popper": 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-portal": 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-slot": 1.2.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - aria-hidden: 1.2.6 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@floating-ui/react-dom": 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-arrow": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-rect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-size": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/rect": 1.1.1 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-slot": 1.2.3(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-roving-focus": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-previous": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-size": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-collection": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/number": 1.1.1 - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-select@2.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/number": 1.1.1 - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-collection": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-dismissable-layer": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-focus-guards": 1.1.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-focus-scope": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-popper": 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-portal": 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-slot": 1.2.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-previous": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-visually-hidden": 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - aria-hidden: 1.2.6 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/number": 1.1.1 - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-collection": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-previous": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-size": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@19.2.0)": - dependencies: - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-previous": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-size": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-roving-focus": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-collection": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-dismissable-layer": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-portal": 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-visually-hidden": 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-roving-focus": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-toggle": 1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-roving-focus": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-separator": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-toggle-group": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-dismissable-layer": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-id": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-popper": 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-portal": 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-slot": 1.2.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-visually-hidden": 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.2)(react@19.2.0)": - dependencies: - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.2)(react@19.2.0)": - dependencies: - "@radix-ui/react-use-effect-event": 0.0.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.2)(react@19.2.0)": - dependencies: - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.2)(react@19.2.0)": - dependencies: - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.2)(react@19.2.0)": - dependencies: - react: 19.2.0 - use-sync-external-store: 1.6.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.2)(react@19.2.0)": - dependencies: - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-use-previous@1.1.1(@types/react@19.2.2)(react@19.2.0)": - dependencies: - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-use-rect@1.1.1(@types/react@19.2.2)(react@19.2.0)": - dependencies: - "@radix-ui/rect": 1.1.1 - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-use-size@1.1.1(@types/react@19.2.2)(react@19.2.0)": - dependencies: - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - react: 19.2.0 - optionalDependencies: - "@types/react": 19.2.2 - - "@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - "@radix-ui/rect@1.1.1": {} - - "@rolldown/binding-android-arm64@1.0.0-beta.41": - optional: true - - "@rolldown/binding-darwin-arm64@1.0.0-beta.41": - optional: true - - "@rolldown/binding-darwin-x64@1.0.0-beta.41": - optional: true - - "@rolldown/binding-freebsd-x64@1.0.0-beta.41": - optional: true - - "@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.41": - optional: true - - "@rolldown/binding-linux-arm64-gnu@1.0.0-beta.41": - optional: true - - "@rolldown/binding-linux-arm64-musl@1.0.0-beta.41": - optional: true - - "@rolldown/binding-linux-x64-gnu@1.0.0-beta.41": - optional: true - - "@rolldown/binding-linux-x64-musl@1.0.0-beta.41": - optional: true - - "@rolldown/binding-openharmony-arm64@1.0.0-beta.41": - optional: true - - "@rolldown/binding-wasm32-wasi@1.0.0-beta.41": - dependencies: - "@napi-rs/wasm-runtime": 1.0.7 - optional: true - - "@rolldown/binding-win32-arm64-msvc@1.0.0-beta.41": - optional: true - - "@rolldown/binding-win32-ia32-msvc@1.0.0-beta.41": - optional: true - - "@rolldown/binding-win32-x64-msvc@1.0.0-beta.41": - optional: true - - "@rolldown/pluginutils@1.0.0-beta.41": {} - - "@rolldown/pluginutils@1.0.0-beta.43": {} - - "@standard-schema/utils@0.3.0": {} - - "@swc/core-darwin-arm64@1.14.0": - optional: true - - "@swc/core-darwin-x64@1.14.0": - optional: true - - "@swc/core-linux-arm-gnueabihf@1.14.0": - optional: true - - "@swc/core-linux-arm64-gnu@1.14.0": - optional: true - - "@swc/core-linux-arm64-musl@1.14.0": - optional: true - - "@swc/core-linux-x64-gnu@1.14.0": - optional: true - - "@swc/core-linux-x64-musl@1.14.0": - optional: true - - "@swc/core-win32-arm64-msvc@1.14.0": - optional: true - - "@swc/core-win32-ia32-msvc@1.14.0": - optional: true - - "@swc/core-win32-x64-msvc@1.14.0": - optional: true - - "@swc/core@1.14.0": - dependencies: - "@swc/counter": 0.1.3 - "@swc/types": 0.1.25 - optionalDependencies: - "@swc/core-darwin-arm64": 1.14.0 - "@swc/core-darwin-x64": 1.14.0 - "@swc/core-linux-arm-gnueabihf": 1.14.0 - "@swc/core-linux-arm64-gnu": 1.14.0 - "@swc/core-linux-arm64-musl": 1.14.0 - "@swc/core-linux-x64-gnu": 1.14.0 - "@swc/core-linux-x64-musl": 1.14.0 - "@swc/core-win32-arm64-msvc": 1.14.0 - "@swc/core-win32-ia32-msvc": 1.14.0 - "@swc/core-win32-x64-msvc": 1.14.0 - - "@swc/counter@0.1.3": {} - - "@swc/types@0.1.25": - dependencies: - "@swc/counter": 0.1.3 - - "@tailwindcss/node@4.1.16": - dependencies: - "@jridgewell/remapping": 2.3.5 - enhanced-resolve: 5.18.3 - jiti: 2.6.1 - lightningcss: 1.30.2 - magic-string: 0.30.21 - source-map-js: 1.2.1 - tailwindcss: 4.1.16 - - "@tailwindcss/oxide-android-arm64@4.1.16": - optional: true - - "@tailwindcss/oxide-darwin-arm64@4.1.16": - optional: true - - "@tailwindcss/oxide-darwin-x64@4.1.16": - optional: true - - "@tailwindcss/oxide-freebsd-x64@4.1.16": - optional: true - - "@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16": - optional: true - - "@tailwindcss/oxide-linux-arm64-gnu@4.1.16": - optional: true - - "@tailwindcss/oxide-linux-arm64-musl@4.1.16": - optional: true - - "@tailwindcss/oxide-linux-x64-gnu@4.1.16": - optional: true - - "@tailwindcss/oxide-linux-x64-musl@4.1.16": - optional: true - - "@tailwindcss/oxide-wasm32-wasi@4.1.16": - optional: true - - "@tailwindcss/oxide-win32-arm64-msvc@4.1.16": - optional: true - - "@tailwindcss/oxide-win32-x64-msvc@4.1.16": - optional: true - - "@tailwindcss/oxide@4.1.16": - optionalDependencies: - "@tailwindcss/oxide-android-arm64": 4.1.16 - "@tailwindcss/oxide-darwin-arm64": 4.1.16 - "@tailwindcss/oxide-darwin-x64": 4.1.16 - "@tailwindcss/oxide-freebsd-x64": 4.1.16 - "@tailwindcss/oxide-linux-arm-gnueabihf": 4.1.16 - "@tailwindcss/oxide-linux-arm64-gnu": 4.1.16 - "@tailwindcss/oxide-linux-arm64-musl": 4.1.16 - "@tailwindcss/oxide-linux-x64-gnu": 4.1.16 - "@tailwindcss/oxide-linux-x64-musl": 4.1.16 - "@tailwindcss/oxide-wasm32-wasi": 4.1.16 - "@tailwindcss/oxide-win32-arm64-msvc": 4.1.16 - "@tailwindcss/oxide-win32-x64-msvc": 4.1.16 - - "@tailwindcss/vite@4.1.16(rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6))": - dependencies: - "@tailwindcss/node": 4.1.16 - "@tailwindcss/oxide": 4.1.16 - tailwindcss: 4.1.16 - vite: rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6) - - "@tanstack/history@1.133.28": {} - - "@tanstack/query-core@5.90.6": {} - - "@tanstack/react-query@5.90.6(react@19.2.0)": - dependencies: - "@tanstack/query-core": 5.90.6 - react: 19.2.0 - - "@tanstack/react-router@1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@tanstack/history": 1.133.28 - "@tanstack/react-store": 0.8.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@tanstack/router-core": 1.134.9 - isbot: 5.1.31 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - - "@tanstack/react-store@0.8.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)": - dependencies: - "@tanstack/store": 0.8.0 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - use-sync-external-store: 1.6.0(react@19.2.0) - - "@tanstack/router-cli@1.134.9": - dependencies: - "@tanstack/router-generator": 1.134.9 - chokidar: 3.6.0 - yargs: 17.7.2 - transitivePeerDependencies: - - supports-color - - "@tanstack/router-core@1.134.9": - dependencies: - "@tanstack/history": 1.133.28 - "@tanstack/store": 0.8.0 - cookie-es: 2.0.0 - seroval: 1.3.2 - seroval-plugins: 1.3.3(seroval@1.3.2) - tiny-invariant: 1.3.3 - tiny-warning: 1.0.3 - - "@tanstack/router-generator@1.134.9": - dependencies: - "@tanstack/router-core": 1.134.9 - "@tanstack/router-utils": 1.133.19 - "@tanstack/virtual-file-routes": 1.133.19 - prettier: 3.6.2 - recast: 0.23.11 - source-map: 0.7.6 - tsx: 4.20.6 - zod: 3.25.76 - transitivePeerDependencies: - - supports-color - - "@tanstack/router-plugin@1.134.9(@tanstack/react-router@1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6))": - dependencies: - "@babel/core": 7.28.5 - "@babel/plugin-syntax-jsx": 7.27.1(@babel/core@7.28.5) - "@babel/plugin-syntax-typescript": 7.27.1(@babel/core@7.28.5) - "@babel/template": 7.27.2 - "@babel/traverse": 7.28.5 - "@babel/types": 7.28.5 - "@tanstack/router-core": 1.134.9 - "@tanstack/router-generator": 1.134.9 - "@tanstack/router-utils": 1.133.19 - "@tanstack/virtual-file-routes": 1.133.19 - babel-dead-code-elimination: 1.0.10 - chokidar: 3.6.0 - unplugin: 2.3.10 - zod: 3.25.76 - optionalDependencies: - "@tanstack/react-router": 1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - vite: rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6) - transitivePeerDependencies: - - supports-color - - "@tanstack/router-utils@1.133.19": - dependencies: - "@babel/core": 7.28.5 - "@babel/generator": 7.28.5 - "@babel/parser": 7.28.5 - "@babel/preset-typescript": 7.28.5(@babel/core@7.28.5) - ansis: 4.2.0 - diff: 8.0.2 - pathe: 2.0.3 - tinyglobby: 0.2.15 - transitivePeerDependencies: - - supports-color - - "@tanstack/store@0.8.0": {} - - "@tanstack/virtual-file-routes@1.133.19": {} - - "@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3)": - dependencies: - "@trpc/server": 11.7.1(typescript@5.9.3) - typescript: 5.9.3 - - "@trpc/react-query@11.7.1(@tanstack/react-query@5.90.6(react@19.2.0))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3)": - dependencies: - "@tanstack/react-query": 5.90.6(react@19.2.0) - "@trpc/client": 11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3) - "@trpc/server": 11.7.1(typescript@5.9.3) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - typescript: 5.9.3 - - "@trpc/server@11.7.1(typescript@5.9.3)": - dependencies: - typescript: 5.9.3 - - "@tybys/wasm-util@0.10.1": - dependencies: - tslib: 2.8.1 - optional: true - - "@types/babel__core@7.20.5": - dependencies: - "@babel/parser": 7.28.5 - "@babel/types": 7.28.5 - "@types/babel__generator": 7.27.0 - "@types/babel__template": 7.4.4 - "@types/babel__traverse": 7.28.0 - - "@types/babel__generator@7.27.0": - dependencies: - "@babel/types": 7.28.5 - - "@types/babel__template@7.4.4": - dependencies: - "@babel/parser": 7.28.5 - "@babel/types": 7.28.5 - - "@types/babel__traverse@7.28.0": - dependencies: - "@babel/types": 7.28.5 - - "@types/estree@1.0.8": {} - - "@types/json-schema@7.0.15": {} - - "@types/node@24.9.2": - dependencies: - undici-types: 7.16.0 - - "@types/react-dom@19.2.2(@types/react@19.2.2)": - dependencies: - "@types/react": 19.2.2 - - "@types/react@19.2.2": - dependencies: - csstype: 3.1.3 - - "@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)": - dependencies: - "@eslint-community/regexpp": 4.12.2 - "@typescript-eslint/parser": 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3) - "@typescript-eslint/scope-manager": 8.46.2 - "@typescript-eslint/type-utils": 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3) - "@typescript-eslint/utils": 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3) - "@typescript-eslint/visitor-keys": 8.46.2 - eslint: 9.39.0(jiti@2.6.1) - graphemer: 1.4.0 - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)": - dependencies: - "@typescript-eslint/scope-manager": 8.46.2 - "@typescript-eslint/types": 8.46.2 - "@typescript-eslint/typescript-estree": 8.46.2(typescript@5.9.3) - "@typescript-eslint/visitor-keys": 8.46.2 - debug: 4.4.3 - eslint: 9.39.0(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/project-service@8.46.2(typescript@5.9.3)": - dependencies: - "@typescript-eslint/tsconfig-utils": 8.46.2(typescript@5.9.3) - "@typescript-eslint/types": 8.46.2 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/scope-manager@8.46.2": - dependencies: - "@typescript-eslint/types": 8.46.2 - "@typescript-eslint/visitor-keys": 8.46.2 - - "@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)": - dependencies: - typescript: 5.9.3 - - "@typescript-eslint/type-utils@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)": - dependencies: - "@typescript-eslint/types": 8.46.2 - "@typescript-eslint/typescript-estree": 8.46.2(typescript@5.9.3) - "@typescript-eslint/utils": 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3) - debug: 4.4.3 - eslint: 9.39.0(jiti@2.6.1) - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/types@8.46.2": {} - - "@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)": - dependencies: - "@typescript-eslint/project-service": 8.46.2(typescript@5.9.3) - "@typescript-eslint/tsconfig-utils": 8.46.2(typescript@5.9.3) - "@typescript-eslint/types": 8.46.2 - "@typescript-eslint/visitor-keys": 8.46.2 - debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)": - dependencies: - "@eslint-community/eslint-utils": 4.9.0(eslint@9.39.0(jiti@2.6.1)) - "@typescript-eslint/scope-manager": 8.46.2 - "@typescript-eslint/types": 8.46.2 - "@typescript-eslint/typescript-estree": 8.46.2(typescript@5.9.3) - eslint: 9.39.0(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/visitor-keys@8.46.2": - dependencies: - "@typescript-eslint/types": 8.46.2 - eslint-visitor-keys: 4.2.1 - - "@vitejs/plugin-react-swc@4.2.0(rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6))": - dependencies: - "@rolldown/pluginutils": 1.0.0-beta.43 - "@swc/core": 1.14.0 - vite: rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6) - transitivePeerDependencies: - - "@swc/helpers" - - "@vitejs/plugin-react@5.1.0(rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6))": - dependencies: - "@babel/core": 7.28.5 - "@babel/plugin-transform-react-jsx-self": 7.27.1(@babel/core@7.28.5) - "@babel/plugin-transform-react-jsx-source": 7.27.1(@babel/core@7.28.5) - "@rolldown/pluginutils": 1.0.0-beta.43 - "@types/babel__core": 7.20.5 - react-refresh: 0.18.0 - vite: rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6) - transitivePeerDependencies: - - supports-color - - acorn-jsx@5.3.2(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - - acorn@8.15.0: {} - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ansi-regex@5.0.1: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansis@4.2.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - argparse@2.0.1: {} - - aria-hidden@1.2.6: - dependencies: - tslib: 2.8.1 - - ast-types@0.16.1: - dependencies: - tslib: 2.8.1 - - babel-dead-code-elimination@1.0.10: - dependencies: - "@babel/core": 7.28.5 - "@babel/parser": 7.28.5 - "@babel/traverse": 7.28.5 - "@babel/types": 7.28.5 - transitivePeerDependencies: - - supports-color - - balanced-match@1.0.2: {} - - baseline-browser-mapping@2.8.23: {} - - binary-extensions@2.3.0: {} - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.27.0: - dependencies: - baseline-browser-mapping: 2.8.23 - caniuse-lite: 1.0.30001753 - electron-to-chromium: 1.5.244 - node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.27.0) - - callsites@3.1.0: {} - - caniuse-lite@1.0.30001753: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - class-variance-authority@0.7.1: - dependencies: - clsx: 2.1.1 - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clsx@2.1.1: {} - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - concat-map@0.0.1: {} - - convert-source-map@2.0.0: {} - - cookie-es@2.0.0: {} - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - csstype@3.1.3: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - deep-is@0.1.4: {} - - detect-libc@2.1.2: {} - - detect-node-es@1.1.0: {} - - diff@8.0.2: {} - - electron-to-chromium@1.5.244: {} - - emoji-regex@8.0.0: {} - - enhanced-resolve@5.18.3: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.0 - - esbuild@0.25.12: - optionalDependencies: - "@esbuild/aix-ppc64": 0.25.12 - "@esbuild/android-arm": 0.25.12 - "@esbuild/android-arm64": 0.25.12 - "@esbuild/android-x64": 0.25.12 - "@esbuild/darwin-arm64": 0.25.12 - "@esbuild/darwin-x64": 0.25.12 - "@esbuild/freebsd-arm64": 0.25.12 - "@esbuild/freebsd-x64": 0.25.12 - "@esbuild/linux-arm": 0.25.12 - "@esbuild/linux-arm64": 0.25.12 - "@esbuild/linux-ia32": 0.25.12 - "@esbuild/linux-loong64": 0.25.12 - "@esbuild/linux-mips64el": 0.25.12 - "@esbuild/linux-ppc64": 0.25.12 - "@esbuild/linux-riscv64": 0.25.12 - "@esbuild/linux-s390x": 0.25.12 - "@esbuild/linux-x64": 0.25.12 - "@esbuild/netbsd-arm64": 0.25.12 - "@esbuild/netbsd-x64": 0.25.12 - "@esbuild/openbsd-arm64": 0.25.12 - "@esbuild/openbsd-x64": 0.25.12 - "@esbuild/openharmony-arm64": 0.25.12 - "@esbuild/sunos-x64": 0.25.12 - "@esbuild/win32-arm64": 0.25.12 - "@esbuild/win32-ia32": 0.25.12 - "@esbuild/win32-x64": 0.25.12 - - escalade@3.2.0: {} - - escape-string-regexp@4.0.0: {} - - eslint-plugin-react-hooks@5.2.0(eslint@9.39.0(jiti@2.6.1)): - dependencies: - eslint: 9.39.0(jiti@2.6.1) - - eslint-plugin-react-refresh@0.4.24(eslint@9.39.0(jiti@2.6.1)): - dependencies: - eslint: 9.39.0(jiti@2.6.1) - - eslint-scope@8.4.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.1: {} - - eslint@9.39.0(jiti@2.6.1): - dependencies: - "@eslint-community/eslint-utils": 4.9.0(eslint@9.39.0(jiti@2.6.1)) - "@eslint-community/regexpp": 4.12.2 - "@eslint/config-array": 0.21.1 - "@eslint/config-helpers": 0.4.2 - "@eslint/core": 0.17.0 - "@eslint/eslintrc": 3.3.1 - "@eslint/js": 9.39.0 - "@eslint/plugin-kit": 0.4.1 - "@humanfs/node": 0.16.7 - "@humanwhocodes/module-importer": 1.0.1 - "@humanwhocodes/retry": 0.4.3 - "@types/estree": 1.0.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 2.6.1 - transitivePeerDependencies: - - supports-color - - espree@10.4.0: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 4.2.1 - - esprima@4.0.1: {} - - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - - esutils@2.0.3: {} - - fast-deep-equal@3.1.3: {} - - fast-glob@3.3.3: - dependencies: - "@nodelib/fs.stat": 2.0.5 - "@nodelib/fs.walk": 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - - fd-package-json@2.0.0: - dependencies: - walk-up-path: 4.0.0 - - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@4.0.1: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - - flatted@3.3.3: {} - - formatly@0.3.0: - dependencies: - fd-package-json: 2.0.0 - - framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - motion-dom: 12.23.23 - motion-utils: 12.23.6 - tslib: 2.8.1 - optionalDependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - - fsevents@2.3.3: - optional: true - - gensync@1.0.0-beta.2: {} - - get-caller-file@2.0.5: {} - - get-nonce@1.0.1: {} - - get-tsconfig@4.13.0: - dependencies: - resolve-pkg-maps: 1.0.0 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - globals@14.0.0: {} - - globals@16.5.0: {} - - graceful-fs@4.2.11: {} - - graphemer@1.4.0: {} - - has-flag@4.0.0: {} - - ignore@5.3.2: {} - - ignore@7.0.5: {} - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@3.0.0: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-number@7.0.0: {} - - isbot@5.1.31: {} - - isexe@2.0.0: {} - - jiti@2.6.1: {} - - js-tokens@4.0.0: {} - - js-yaml@4.1.0: - dependencies: - argparse: 2.0.1 - - jsesc@3.1.0: {} - - json-buffer@3.0.1: {} - - json-schema-traverse@0.4.1: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@2.2.3: {} - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - knip@5.67.1(@types/node@24.9.2)(typescript@5.9.3): - dependencies: - "@nodelib/fs.walk": 1.2.8 - "@types/node": 24.9.2 - fast-glob: 3.3.3 - formatly: 0.3.0 - jiti: 2.6.1 - js-yaml: 4.1.0 - minimist: 1.2.8 - oxc-resolver: 11.13.1 - picocolors: 1.1.1 - picomatch: 4.0.3 - smol-toml: 1.4.2 - strip-json-comments: 5.0.2 - typescript: 5.9.3 - zod: 4.1.12 - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - lightningcss-android-arm64@1.30.2: - optional: true - - lightningcss-darwin-arm64@1.30.2: - optional: true - - lightningcss-darwin-x64@1.30.2: - optional: true - - lightningcss-freebsd-x64@1.30.2: - optional: true - - lightningcss-linux-arm-gnueabihf@1.30.2: - optional: true - - lightningcss-linux-arm64-gnu@1.30.2: - optional: true - - lightningcss-linux-arm64-musl@1.30.2: - optional: true - - lightningcss-linux-x64-gnu@1.30.2: - optional: true - - lightningcss-linux-x64-musl@1.30.2: - optional: true - - lightningcss-win32-arm64-msvc@1.30.2: - optional: true - - lightningcss-win32-x64-msvc@1.30.2: - optional: true - - lightningcss@1.30.2: - dependencies: - detect-libc: 2.1.2 - optionalDependencies: - lightningcss-android-arm64: 1.30.2 - lightningcss-darwin-arm64: 1.30.2 - lightningcss-darwin-x64: 1.30.2 - lightningcss-freebsd-x64: 1.30.2 - lightningcss-linux-arm-gnueabihf: 1.30.2 - lightningcss-linux-arm64-gnu: 1.30.2 - lightningcss-linux-arm64-musl: 1.30.2 - lightningcss-linux-x64-gnu: 1.30.2 - lightningcss-linux-x64-musl: 1.30.2 - lightningcss-win32-arm64-msvc: 1.30.2 - lightningcss-win32-x64-msvc: 1.30.2 - - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - lodash.merge@4.6.2: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - lucide-react@0.552.0(react@19.2.0): - dependencies: - react: 19.2.0 - - magic-string@0.30.21: - dependencies: - "@jridgewell/sourcemap-codec": 1.5.5 - - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minimist@1.2.8: {} - - motion-dom@12.23.23: - dependencies: - motion-utils: 12.23.6 - - motion-utils@12.23.6: {} - - motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - framer-motion: 12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - tslib: 2.8.1 - optionalDependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - - ms@2.1.3: {} - - nanoid@3.3.11: {} - - natural-compare@1.4.0: {} - - node-releases@2.0.27: {} - - normalize-path@3.0.0: {} - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - oxc-resolver@11.13.1: - optionalDependencies: - "@oxc-resolver/binding-android-arm-eabi": 11.13.1 - "@oxc-resolver/binding-android-arm64": 11.13.1 - "@oxc-resolver/binding-darwin-arm64": 11.13.1 - "@oxc-resolver/binding-darwin-x64": 11.13.1 - "@oxc-resolver/binding-freebsd-x64": 11.13.1 - "@oxc-resolver/binding-linux-arm-gnueabihf": 11.13.1 - "@oxc-resolver/binding-linux-arm-musleabihf": 11.13.1 - "@oxc-resolver/binding-linux-arm64-gnu": 11.13.1 - "@oxc-resolver/binding-linux-arm64-musl": 11.13.1 - "@oxc-resolver/binding-linux-ppc64-gnu": 11.13.1 - "@oxc-resolver/binding-linux-riscv64-gnu": 11.13.1 - "@oxc-resolver/binding-linux-riscv64-musl": 11.13.1 - "@oxc-resolver/binding-linux-s390x-gnu": 11.13.1 - "@oxc-resolver/binding-linux-x64-gnu": 11.13.1 - "@oxc-resolver/binding-linux-x64-musl": 11.13.1 - "@oxc-resolver/binding-wasm32-wasi": 11.13.1 - "@oxc-resolver/binding-win32-arm64-msvc": 11.13.1 - "@oxc-resolver/binding-win32-ia32-msvc": 11.13.1 - "@oxc-resolver/binding-win32-x64-msvc": 11.13.1 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - path-exists@4.0.0: {} - - path-key@3.1.1: {} - - pathe@2.0.3: {} - - picocolors@1.1.1: {} - - picomatch@2.3.1: {} - - picomatch@4.0.3: {} - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - prelude-ls@1.2.1: {} - - prettier@3.6.2: {} - - punycode@2.3.1: {} - - queue-microtask@1.2.3: {} - - radix-ui@1.4.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-accessible-icon": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-accordion": 1.2.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-alert-dialog": 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-arrow": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-aspect-ratio": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-avatar": 1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-checkbox": 1.3.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-collapsible": 1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-collection": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context": 1.1.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-context-menu": 2.2.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-dialog": 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-direction": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-dismissable-layer": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-dropdown-menu": 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-focus-guards": 1.1.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-focus-scope": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-form": 0.1.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-hover-card": 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-label": 2.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-menu": 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-menubar": 1.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-navigation-menu": 1.2.14(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-one-time-password-field": 0.1.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-password-toggle-field": 0.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-popover": 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-popper": 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-portal": 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-presence": 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-progress": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-radio-group": 1.3.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-roving-focus": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-scroll-area": 1.2.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-select": 2.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-separator": 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-slider": 1.3.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-slot": 1.2.3(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-switch": 1.2.6(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-tabs": 1.1.13(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-toast": 1.2.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-toggle": 1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-toggle-group": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-toolbar": 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-tooltip": 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - "@radix-ui/react-use-callback-ref": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-effect-event": 0.0.2(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-escape-keydown": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-is-hydrated": 0.1.0(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-use-size": 1.1.1(@types/react@19.2.2)(react@19.2.0) - "@radix-ui/react-visually-hidden": 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - "@types/react-dom": 19.2.2(@types/react@19.2.2) - - react-dom@19.2.0(react@19.2.0): - dependencies: - react: 19.2.0 - scheduler: 0.27.0 - - react-hook-form@7.66.0(react@19.2.0): - dependencies: - react: 19.2.0 - - react-intersection-observer@10.0.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - react: 19.2.0 - optionalDependencies: - react-dom: 19.2.0(react@19.2.0) - - react-refresh@0.18.0: {} - - react-remove-scroll-bar@2.3.8(@types/react@19.2.2)(react@19.2.0): - dependencies: - react: 19.2.0 - react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.2.0) - tslib: 2.8.1 - optionalDependencies: - "@types/react": 19.2.2 - - react-remove-scroll@2.7.1(@types/react@19.2.2)(react@19.2.0): - dependencies: - react: 19.2.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.2)(react@19.2.0) - react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.2.0) - tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.2)(react@19.2.0) - use-sidecar: 1.1.3(@types/react@19.2.2)(react@19.2.0) - optionalDependencies: - "@types/react": 19.2.2 - - react-style-singleton@2.2.3(@types/react@19.2.2)(react@19.2.0): - dependencies: - get-nonce: 1.0.1 - react: 19.2.0 - tslib: 2.8.1 - optionalDependencies: - "@types/react": 19.2.2 - - react@19.2.0: {} - - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - recast@0.23.11: - dependencies: - ast-types: 0.16.1 - esprima: 4.0.1 - source-map: 0.6.1 - tiny-invariant: 1.3.3 - tslib: 2.8.1 - - require-directory@2.1.1: {} - - resolve-from@4.0.0: {} - - resolve-pkg-maps@1.0.0: {} - - reusify@1.1.0: {} - - rolldown-vite@7.1.14(@types/node@24.9.2)(esbuild@0.25.12)(jiti@2.6.1)(tsx@4.20.6): - dependencies: - "@oxc-project/runtime": 0.92.0 - fdir: 6.5.0(picomatch@4.0.3) - lightningcss: 1.30.2 - picomatch: 4.0.3 - postcss: 8.5.6 - rolldown: 1.0.0-beta.41 - tinyglobby: 0.2.15 - optionalDependencies: - "@types/node": 24.9.2 - esbuild: 0.25.12 - fsevents: 2.3.3 - jiti: 2.6.1 - tsx: 4.20.6 - - rolldown@1.0.0-beta.41: - dependencies: - "@oxc-project/types": 0.93.0 - "@rolldown/pluginutils": 1.0.0-beta.41 - ansis: 4.2.0 - optionalDependencies: - "@rolldown/binding-android-arm64": 1.0.0-beta.41 - "@rolldown/binding-darwin-arm64": 1.0.0-beta.41 - "@rolldown/binding-darwin-x64": 1.0.0-beta.41 - "@rolldown/binding-freebsd-x64": 1.0.0-beta.41 - "@rolldown/binding-linux-arm-gnueabihf": 1.0.0-beta.41 - "@rolldown/binding-linux-arm64-gnu": 1.0.0-beta.41 - "@rolldown/binding-linux-arm64-musl": 1.0.0-beta.41 - "@rolldown/binding-linux-x64-gnu": 1.0.0-beta.41 - "@rolldown/binding-linux-x64-musl": 1.0.0-beta.41 - "@rolldown/binding-openharmony-arm64": 1.0.0-beta.41 - "@rolldown/binding-wasm32-wasi": 1.0.0-beta.41 - "@rolldown/binding-win32-arm64-msvc": 1.0.0-beta.41 - "@rolldown/binding-win32-ia32-msvc": 1.0.0-beta.41 - "@rolldown/binding-win32-x64-msvc": 1.0.0-beta.41 - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - scheduler@0.27.0: {} - - semver@6.3.1: {} - - semver@7.7.3: {} - - seroval-plugins@1.3.3(seroval@1.3.2): - dependencies: - seroval: 1.3.2 - - seroval@1.3.2: {} - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - smol-toml@1.4.2: {} - - sonner@2.0.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0): - dependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - - source-map-js@1.2.1: {} - - source-map@0.6.1: {} - - source-map@0.7.6: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-json-comments@3.1.1: {} - - strip-json-comments@5.0.2: {} - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - tailwind-merge@3.3.1: {} - - tailwindcss@4.1.16: {} - - tapable@2.3.0: {} - - tiny-invariant@1.3.3: {} - - tiny-warning@1.0.3: {} - - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - ts-api-utils@2.1.0(typescript@5.9.3): - dependencies: - typescript: 5.9.3 - - tslib@2.8.1: {} - - tsx@4.20.6: - dependencies: - esbuild: 0.25.12 - get-tsconfig: 4.13.0 - optionalDependencies: - fsevents: 2.3.3 - - tw-animate-css@1.4.0: {} - - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - typescript-eslint@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3): - dependencies: - "@typescript-eslint/eslint-plugin": 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3) - "@typescript-eslint/parser": 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3) - "@typescript-eslint/typescript-estree": 8.46.2(typescript@5.9.3) - "@typescript-eslint/utils": 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.0(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - typescript@5.9.3: {} - - undici-types@7.16.0: {} - - unplugin@2.3.10: - dependencies: - "@jridgewell/remapping": 2.3.5 - acorn: 8.15.0 - picomatch: 4.0.3 - webpack-virtual-modules: 0.6.2 - - update-browserslist-db@1.1.4(browserslist@4.27.0): - dependencies: - browserslist: 4.27.0 - escalade: 3.2.0 - picocolors: 1.1.1 - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - use-callback-ref@1.3.3(@types/react@19.2.2)(react@19.2.0): - dependencies: - react: 19.2.0 - tslib: 2.8.1 - optionalDependencies: - "@types/react": 19.2.2 - - use-sidecar@1.1.3(@types/react@19.2.2)(react@19.2.0): - dependencies: - detect-node-es: 1.1.0 - react: 19.2.0 - tslib: 2.8.1 - optionalDependencies: - "@types/react": 19.2.2 - - use-sync-external-store@1.6.0(react@19.2.0): - dependencies: - react: 19.2.0 - - walk-up-path@4.0.0: {} - - webpack-virtual-modules@0.6.2: {} - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - word-wrap@1.2.5: {} - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - y18n@5.0.8: {} - - yallist@3.1.1: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - yocto-queue@0.1.0: {} - - zod@3.25.76: {} - - zod@4.1.12: {} From b0f36d597d55dc8286d489a4a505deb11b97f366 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 11:56:12 -0500 Subject: [PATCH 28/41] fix(docker): use tsup bundler and fix double-migration bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #75 ## Root Causes Identified 1. **Lockfile mismatch**: Docker build context was set to individual packages but pnpm workspaces require monorepo root context 2. **ESM import issues**: TypeScript compilation without bundling resulted in missing `.js` extensions in imports (Node ESM requirement) 3. **Double-migration bug**: When tsup bundled migrate-local.ts into node.ts, the CLI check (import.meta.url) executed twice, causing migrations to run twice and server to hang ## Solutions Implemented ### 1. Monorepo Context (from previous commit 23e386b) - Changed docker-compose.yml context from `./packages/*` to `.` (root) - Updated Dockerfiles to copy workspace files - Removed outdated individual package lockfiles - Added packages/*/pnpm-lock.yaml to .gitignore ### 2. Add tsup Bundler - Added tsup as dev dependency for proper ESM bundling - Created tsup.config.ts to bundle node.ts and migrate-local.ts - tsup handles ESM imports correctly (no .js extension issues) - Optimized bundle: 525KB for node.ts entry point - Updated package.json build script to use tsup ### 3. Fix Double-Migration Bug - Added check in migrate-local.ts to prevent CLI code from running when bundled into dist/entries/node.js - CLI check now excludes execution when argv[1] contains "dist/entries" - Migrations now run exactly once during server startup ### 4. Update Dockerfile CMD - Changed from running separate migration + server to just server - Migrations now run automatically inside node.ts (as designed) - Removed redundant migration command from CMD ## Testing โœ… Local build: `pnpm --filter @tuvixrss/api build` succeeds โœ… Local run: Server starts, migrations run once, cron initializes โœ… Docker build: `docker compose build` succeeds โœ… Docker run: Containers healthy, API responds on :3001 โœ… Health check: `curl http://localhost:3001/health` returns OK ## Changes - packages/api/Dockerfile: Use tsup-bundled code, simplified CMD - packages/api/package.json: Add tsup dependency, update build script - packages/api/tsup.config.ts: Configure bundling for node.ts + migrate-local.ts - packages/api/src/db/migrate-local.ts: Fix double-execution bug - pnpm-lock.yaml: Add tsup dependencies ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/Dockerfile | 6 +- packages/api/package.json | 4 +- packages/api/src/db/migrate-local.ts | 9 +- packages/api/tsup.config.ts | 27 ++ pnpm-lock.yaml | 419 +++++++++++++++++++++++++++ 5 files changed, 460 insertions(+), 5 deletions(-) create mode 100644 packages/api/tsup.config.ts diff --git a/packages/api/Dockerfile b/packages/api/Dockerfile index e6a414e3..f72cc6e1 100644 --- a/packages/api/Dockerfile +++ b/packages/api/Dockerfile @@ -19,7 +19,7 @@ RUN pnpm install --frozen-lockfile --prod=false COPY packages/api ./packages/api COPY packages/tricorder ./packages/tricorder -# Build the application +# Build the application with tsup (bundles and resolves ESM) RUN pnpm --filter @tuvixrss/api build # Production stage @@ -54,5 +54,5 @@ EXPOSE 3001 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1 -# Run migrations then start the server -CMD ["sh", "-c", "node packages/api/dist/db/migrate-local.js && node packages/api/dist/entries/node.js"] +# Start the server (migrations run automatically inside node.ts) +CMD ["node", "packages/api/dist/entries/node.js"] diff --git a/packages/api/package.json b/packages/api/package.json index eea29c71..fd8c4bad 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -14,7 +14,8 @@ "scripts": { "dev": "tsx watch --env-file=.env --env-file=../../.env src/entries/node.ts", "dev:workers": "wrangler dev --persist-to .wrangler/state", - "build": "tsc", + "build": "tsup", + "build:tsc": "tsc", "start": "node dist/entries/node.js", "type-check": "tsc --noEmit", "test": "vitest run", @@ -79,6 +80,7 @@ "knip": "^5.70.1", "prettier": "^3.6.2", "react": "^19.2.0", + "tsup": "^8.5.1", "tsx": "^4.20.6", "typescript": "^5.9.3", "vitest": "^4.0.13", diff --git a/packages/api/src/db/migrate-local.ts b/packages/api/src/db/migrate-local.ts index 1bb5ee47..7c44d15d 100644 --- a/packages/api/src/db/migrate-local.ts +++ b/packages/api/src/db/migrate-local.ts @@ -87,7 +87,14 @@ export async function runMigrationsIfNeeded( } // CLI entry point - run migrations if this file is executed directly -if (import.meta.url === `file://${process.argv[1]}`) { +// Note: This check is disabled when bundled to prevent double-execution +const isMainModule = + typeof process !== "undefined" && + import.meta.url === `file://${process.argv[1]}` && + // Additional check: only run if not bundled (dist/entries would indicate bundled) + !process.argv[1]?.includes("dist/entries"); + +if (isMainModule) { runMigrationsIfNeeded() .then(() => { process.exit(0); diff --git a/packages/api/tsup.config.ts b/packages/api/tsup.config.ts new file mode 100644 index 00000000..5727c6ba --- /dev/null +++ b/packages/api/tsup.config.ts @@ -0,0 +1,27 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: { + "entries/node": "src/entries/node.ts", + "db/migrate-local": "src/db/migrate-local.ts", + }, + format: ["esm"], + target: "node20", + platform: "node", + outDir: "dist", + clean: true, + sourcemap: false, + minify: false, + bundle: true, + external: [ + // External packages that shouldn't be bundled + "better-sqlite3", + "bcrypt", + ], + noExternal: [ + // Bundle everything else including workspace packages + "@tuvixrss/tricorder", + ], + splitting: false, + treeshake: true, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 688fc626..c1db3cb8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -136,6 +136,9 @@ importers: react: specifier: ^19.2.0 version: 19.2.0 + tsup: + specifier: ^8.5.1 + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3) tsx: specifier: ^4.20.6 version: 4.20.6 @@ -3234,6 +3237,116 @@ packages: rollup: optional: true + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + cpu: [x64] + os: [win32] + '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -3857,6 +3970,9 @@ packages: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -4035,10 +4151,20 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.25.0' + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + caching-transform@4.0.0: resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==} engines: {node: '>=8'} @@ -4160,6 +4286,10 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + common-tags@1.8.2: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} @@ -4179,6 +4309,9 @@ packages: resolution: {integrity: sha512-JBSrutapCafTrddF9dH3lc7+T2tBycGF4uPkI4Js+g4vLLEhG6RZcFi3aJd5zntdf5tQxAejJt8dihkoQ/eSJw==} engines: {node: '>=20'} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} @@ -4885,6 +5018,9 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -5465,6 +5601,10 @@ packages: jose@6.1.2: resolution: {integrity: sha512-MpcPtHLE5EmztuFIqB0vzHAWJPpmN1E6L4oo+kze56LIs3MyXIj9ZHMDxqOvkP38gBR7K1v3jqd4WU2+nrfONQ==} + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -5630,9 +5770,17 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -5795,6 +5943,9 @@ packages: mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mlly@1.8.0: + resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} @@ -5839,6 +5990,9 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -6109,6 +6263,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + pkce-challenge@5.0.1: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} @@ -6117,6 +6275,9 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} @@ -6124,6 +6285,24 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + postcss-selector-parser@6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} engines: {node: '>=4'} @@ -6550,6 +6729,11 @@ packages: engines: {node: '>=10.0.0'} hasBin: true + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rou3@0.5.1: resolution: {integrity: sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ==} @@ -6899,6 +7083,11 @@ packages: babel-plugin-macros: optional: true + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} @@ -6962,6 +7151,13 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -7026,6 +7222,9 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-morph@26.0.0: resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==} @@ -7036,6 +7235,25 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsup@8.5.1: + resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + tsx@4.20.6: resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} engines: {node: '>=18.0.0'} @@ -7098,6 +7316,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + uint8array-extras@1.5.0: resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} engines: {node: '>=18'} @@ -10371,6 +10592,72 @@ snapshots: optionalDependencies: rollup: 2.79.2 + '@rollup/rollup-android-arm-eabi@4.53.3': + optional: true + + '@rollup/rollup-android-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-x64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.53.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.3': + optional: true + '@sec-ant/readable-stream@0.4.1': {} '@selderee/plugin-htmlparser2@0.11.0': @@ -11105,6 +11392,8 @@ snapshots: ansis@4.2.0: {} + any-promise@1.3.0: {} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 @@ -11311,8 +11600,15 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bundle-require@5.1.0(esbuild@0.27.0): + dependencies: + esbuild: 0.27.0 + load-tsconfig: 0.2.5 + bytes@3.1.2: {} + cac@6.7.14: {} + caching-transform@4.0.0: dependencies: hasha: 5.2.2 @@ -11432,6 +11728,8 @@ snapshots: commander@2.20.3: {} + commander@4.1.1: {} + common-tags@1.8.2: {} commondir@1.0.1: {} @@ -11459,6 +11757,8 @@ snapshots: semver: 7.7.3 uint8array-extras: 1.5.0 + confbox@0.1.8: {} + confbox@0.2.2: {} consola@3.4.2: {} @@ -12171,6 +12471,12 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.21 + mlly: 1.8.0 + rollup: 4.53.3 + flat-cache@4.0.1: dependencies: flatted: 3.3.3 @@ -12721,6 +13027,8 @@ snapshots: jose@6.1.2: {} + joycon@3.1.1: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -12872,8 +13180,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 + lilconfig@3.1.3: {} + lines-and-columns@1.2.4: {} + load-tsconfig@0.2.5: {} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -13017,6 +13329,13 @@ snapshots: mkdirp-classic@0.5.3: {} + mlly@1.8.0: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + module-details-from-path@1.0.4: {} motion-dom@12.23.23: @@ -13064,6 +13383,12 @@ snapshots: mute-stream@2.0.0: {} + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.11: {} nanostores@1.1.0: {} @@ -13361,12 +13686,20 @@ snapshots: picomatch@4.0.3: {} + pirates@4.0.7: {} + pkce-challenge@5.0.1: {} pkg-dir@4.2.0: dependencies: find-up: 4.1.0 + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.0 + pathe: 2.0.3 + pkg-types@2.3.0: dependencies: confbox: 0.2.2 @@ -13375,6 +13708,14 @@ snapshots: possible-typed-array-names@1.1.0: {} + postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 2.6.1 + postcss: 8.5.6 + tsx: 4.20.6 + postcss-selector-parser@6.0.10: dependencies: cssesc: 3.0.0 @@ -13860,6 +14201,34 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + rollup@4.53.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 + fsevents: 2.3.3 + rou3@0.5.1: {} router@2.2.0: @@ -14349,6 +14718,16 @@ snapshots: optionalDependencies: '@babel/core': 7.28.5 + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.15 + ts-interface-checker: 0.1.13 + supports-color@10.2.2: {} supports-color@7.2.0: @@ -14419,6 +14798,14 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + tiny-invariant@1.3.3: {} tiny-warning@1.0.3: {} @@ -14468,6 +14855,8 @@ snapshots: dependencies: typescript: 5.9.3 + ts-interface-checker@0.1.13: {} + ts-morph@26.0.0: dependencies: '@ts-morph/common': 0.27.0 @@ -14481,6 +14870,34 @@ snapshots: tslib@2.8.1: {} + tsup@8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3): + dependencies: + bundle-require: 5.1.0(esbuild@0.27.0) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.3 + esbuild: 0.27.0 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6) + resolve-from: 5.0.0 + rollup: 4.53.3 + source-map: 0.7.6 + sucrase: 3.35.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.6 + typescript: 5.9.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + tsx@4.20.6: dependencies: esbuild: 0.27.0 @@ -14562,6 +14979,8 @@ snapshots: typescript@5.9.3: {} + ufo@1.6.1: {} + uint8array-extras@1.5.0: {} unbox-primitive@1.1.0: From 6b74bdaf7f5d8fe2f5f406b60908c033bbb8ebe7 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 12:43:59 -0500 Subject: [PATCH 29/41] chore: optimize Docker configuration for security and efficiency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security improvements: - Run containers as non-root users (uid 1001) - API runs as 'nodejs' user - App runs as 'nginx-app' user Build optimization: - Add .dockerignore files (root and API package) - Reduce build context by excluding unnecessary files - Build tricorder before API in multi-stage build Health checks: - Add explicit health check to app Dockerfile - Consistent health check configuration across services Image sizes: - API: 553MB (increased from 357MB due to security layer overhead) - App: 60.9MB (optimized from 57.1MB with health checks) ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .dockerignore | 54 ++++++++++++++++++++++++++++++++++++++ packages/api/.dockerignore | 9 +++++++ packages/api/Dockerfile | 11 ++++++++ packages/app/Dockerfile | 15 +++++++++++ 4 files changed, 89 insertions(+) create mode 100644 .dockerignore create mode 100644 packages/api/.dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..a5d79f97 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,54 @@ +# Dependencies +node_modules/ +**/node_modules/ + +# Build output +dist/ +**/dist/ +build/ +**/.output/ + +# Development +.git/ +.github/ +.vscode/ +.idea/ +*.md +docs/ + +# Testing +**/__tests__/ +**/*.test.ts +**/*.spec.ts +coverage/ +**/coverage/ +.nyc_output/ + +# Environment +.env +.env.* +!.env.example + +# Logs +*.log +logs/ + +# Cache +.cache/ +**/.cache/ +.parcel-cache/ +.wrangler/ +**/.wrangler/ + +# Temp files +tmp/ +temp/ +*.tmp +.DS_Store + +# Application specific +packages/app/dist/ +packages/api/dist/ +data/ +*.db +*.db-* diff --git a/packages/api/.dockerignore b/packages/api/.dockerignore new file mode 100644 index 00000000..e701c28b --- /dev/null +++ b/packages/api/.dockerignore @@ -0,0 +1,9 @@ +node_modules/ +dist/ +coverage/ +*.test.ts +*.spec.ts +.env +.env.* +!.env.example +*.log diff --git a/packages/api/Dockerfile b/packages/api/Dockerfile index f72cc6e1..aeee880c 100644 --- a/packages/api/Dockerfile +++ b/packages/api/Dockerfile @@ -19,6 +19,9 @@ RUN pnpm install --frozen-lockfile --prod=false COPY packages/api ./packages/api COPY packages/tricorder ./packages/tricorder +# Build tricorder first (dependency of api) +RUN pnpm --filter @tuvixrss/tricorder build + # Build the application with tsup (bundles and resolves ESM) RUN pnpm --filter @tuvixrss/api build @@ -47,6 +50,14 @@ COPY --from=builder /app/packages/api/drizzle ./packages/api/drizzle # Create data directory for SQLite database RUN mkdir -p /app/data +# Create non-root user and set ownership +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nodejs -u 1001 && \ + chown -R nodejs:nodejs /app && \ + chown -R nodejs:nodejs /app/data + +USER nodejs + # Expose the API port EXPOSE 3001 diff --git a/packages/app/Dockerfile b/packages/app/Dockerfile index 7d266f16..808abedb 100644 --- a/packages/app/Dockerfile +++ b/packages/app/Dockerfile @@ -57,6 +57,21 @@ RUN echo 'server { \ } \ }' > /etc/nginx/conf.d/default.conf +# Create non-root user for nginx +RUN addgroup -g 1001 -S nginx-app && \ + adduser -S nginx-app -u 1001 && \ + chown -R nginx-app:nginx-app /usr/share/nginx/html && \ + chown -R nginx-app:nginx-app /var/cache/nginx && \ + chown -R nginx-app:nginx-app /var/log/nginx && \ + touch /var/run/nginx.pid && \ + chown -R nginx-app:nginx-app /var/run/nginx.pid + +USER nginx-app + EXPOSE 80 +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:80/health || exit 1 + CMD ["nginx", "-g", "daemon off;"] From 6987010f6667579cbdffb15b1098b4df149b2b82 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 12:53:52 -0500 Subject: [PATCH 30/41] fix: optimize Docker production stage to prevent permission layer bloat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical Fix: - Create non-root user BEFORE copying files (was causing 196MB layer) - Use --chown flag during COPY operations instead of chown after - This prevents Docker copy-on-write from duplicating the entire /app directory Results: - API image: 357MB (down from 553MB with previous optimization) - User creation layer: 3.22kB (down from 196MB) - All functionality verified working Technical Details: - The issue was that running `chown -R nodejs:nodejs /app` after copying files created a new 196MB layer because Docker's copy-on-write system duplicates all files when permissions change - By creating the user first and using COPY --chown, we set permissions during the copy operation, avoiding the duplication Security maintained: - Still runs as non-root user (nodejs:1001) - Health checks working - Containers start successfully ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/Dockerfile | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/packages/api/Dockerfile b/packages/api/Dockerfile index aeee880c..d27b7da3 100644 --- a/packages/api/Dockerfile +++ b/packages/api/Dockerfile @@ -28,33 +28,29 @@ RUN pnpm --filter @tuvixrss/api build # Production stage FROM node:20-alpine +# Create non-root user FIRST (before any files to avoid permission layer bloat) +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nodejs -u 1001 + WORKDIR /app -# Install pnpm +# Install pnpm for production dependencies RUN npm install -g pnpm@10.19.0 -# Copy workspace files -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ - -# Copy package files -COPY packages/api/package.json ./packages/api/ -COPY packages/tricorder/package.json ./packages/tricorder/ +# Copy package files with ownership +COPY --chown=nodejs:nodejs package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY --chown=nodejs:nodejs packages/api/package.json ./packages/api/ +COPY --chown=nodejs:nodejs packages/tricorder/package.json ./packages/tricorder/ # Install production dependencies only RUN pnpm install --frozen-lockfile --prod -# Copy built files from builder -COPY --from=builder /app/packages/api/dist ./packages/api/dist -COPY --from=builder /app/packages/api/drizzle ./packages/api/drizzle +# Copy built artifacts with correct ownership +COPY --from=builder --chown=nodejs:nodejs /app/packages/api/dist ./packages/api/dist +COPY --from=builder --chown=nodejs:nodejs /app/packages/api/drizzle ./packages/api/drizzle -# Create data directory for SQLite database -RUN mkdir -p /app/data - -# Create non-root user and set ownership -RUN addgroup -g 1001 -S nodejs && \ - adduser -S nodejs -u 1001 && \ - chown -R nodejs:nodejs /app && \ - chown -R nodejs:nodejs /app/data +# Create data directory with ownership +RUN mkdir -p /app/data && chown nodejs:nodejs /app/data USER nodejs From 100af7943011235fd5908f151cd5f56092fef741 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 13:46:52 -0500 Subject: [PATCH 31/41] ci: add Docker build and test workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive CI/CD testing for Docker Compose deployment: GitHub Actions Workflow (.github/workflows/docker-test.yml): - Build validation for both API and App images - Image size enforcement (API < 400MB, App < 100MB) - Health check verification with 60s timeout - Endpoint testing (API and App health checks) - Security validation (non-root user verification) - Database migration verification - Smoke tests for basic functionality - Detailed logging and cleanup on failure Test Configuration (docker-compose.test.yml): - Faster health check intervals for CI (5s vs 30s) - Test-specific environment variables - Extended retries for reliability Local Testing Script (scripts/test-docker.sh): - Run the same CI tests locally before pushing - Colorized output for easy debugging - Automatic cleanup on exit/interrupt - Shows container stats and leaves services running Package Scripts: - `pnpm docker:test` - Run local Docker tests - `pnpm docker:test:ci` - Run with CI configuration Triggers: - Pull requests affecting Docker-related files - Pushes to main or dev branches Expected CI runtime: 3-5 minutes ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/docker-test.yml | 236 ++++++++++++++++++++++++++++++ docker-compose.test.yml | 26 ++++ package.json | 2 + scripts/test-docker.sh | 182 +++++++++++++++++++++++ 4 files changed, 446 insertions(+) create mode 100644 .github/workflows/docker-test.yml create mode 100644 docker-compose.test.yml create mode 100755 scripts/test-docker.sh diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml new file mode 100644 index 00000000..e3575029 --- /dev/null +++ b/.github/workflows/docker-test.yml @@ -0,0 +1,236 @@ +name: Docker Build & Test + +on: + pull_request: + paths: + - 'packages/api/**' + - 'packages/app/**' + - 'packages/tricorder/**' + - 'docker-compose.yml' + - 'docker-compose.test.yml' + - '**/Dockerfile' + - '.dockerignore' + - 'package.json' + - 'pnpm-lock.yaml' + push: + branches: + - main + - dev + +env: + # CI-specific environment variables + BETTER_AUTH_SECRET: test-secret-for-ci-must-be-at-least-32-characters-long-xyz + CORS_ORIGIN: http://localhost:5173 + DATABASE_PATH: /app/data/test.db + +jobs: + docker-build-and-test: + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker images + run: | + echo "๐Ÿ“ฆ Building Docker images..." + docker compose build + env: + COMPOSE_DOCKER_CLI_BUILD: 1 + DOCKER_BUILDKIT: 1 + + - name: Check image sizes + run: | + echo "๐Ÿ“ Checking image sizes..." + + # Get image sizes + API_SIZE_RAW=$(docker images --format "{{.Size}}" tuvixrss-api:latest) + APP_SIZE_RAW=$(docker images --format "{{.Size}}" tuvixrss-app:latest) + + echo "API Image: ${API_SIZE_RAW}" + echo "App Image: ${APP_SIZE_RAW}" + + # Extract numeric size (handle both MB and GB) + API_SIZE=$(echo "$API_SIZE_RAW" | sed 's/MB//' | sed 's/GB/*1024/' | bc 2>/dev/null || echo "$API_SIZE_RAW" | sed 's/[^0-9.]//g') + APP_SIZE=$(echo "$APP_SIZE_RAW" | sed 's/MB//' | sed 's/GB/*1024/' | bc 2>/dev/null || echo "$APP_SIZE_RAW" | sed 's/[^0-9.]//g') + + # Convert to integer for comparison + API_SIZE_INT=$(printf "%.0f" "$API_SIZE" 2>/dev/null || echo "0") + APP_SIZE_INT=$(printf "%.0f" "$APP_SIZE" 2>/dev/null || echo "0") + + # Check size limits (API: 400MB, App: 100MB) + if [ "$API_SIZE_INT" -gt 400 ]; then + echo "โŒ API image too large: ${API_SIZE_RAW} (limit: 400MB)" + exit 1 + fi + + if [ "$APP_SIZE_INT" -gt 100 ]; then + echo "โŒ App image too large: ${APP_SIZE_RAW} (limit: 100MB)" + exit 1 + fi + + echo "โœ… Image sizes within limits" + echo "api_size=${API_SIZE_RAW}" >> $GITHUB_OUTPUT + echo "app_size=${APP_SIZE_RAW}" >> $GITHUB_OUTPUT + + - name: Start Docker Compose services + run: | + echo "๐Ÿš€ Starting Docker Compose services..." + docker compose up -d + + echo "โณ Waiting for containers to start..." + sleep 5 + + docker ps -a + + - name: Wait for API to be healthy + run: | + echo "โณ Waiting for API to be healthy..." + timeout 60 bash -c 'until docker inspect --format="{{.State.Health.Status}}" tuvix-api 2>/dev/null | grep -q "healthy"; do + echo "API status: $(docker inspect --format="{{.State.Health.Status}}" tuvix-api 2>/dev/null || echo "not found")" + sleep 2 + done' + echo "โœ… API is healthy" + + - name: Wait for App to be healthy + run: | + echo "โณ Waiting for App to be healthy..." + timeout 60 bash -c 'until docker inspect --format="{{.State.Health.Status}}" tuvix-app 2>/dev/null | grep -q "healthy"; do + echo "App status: $(docker inspect --format="{{.State.Health.Status}}" tuvix-app 2>/dev/null || echo "not found")" + sleep 2 + done' + echo "โœ… App is healthy" + + - name: Test API health endpoint + run: | + echo "๐Ÿ” Testing API health endpoint..." + RESPONSE=$(curl -s http://localhost:3001/health) + echo "Response: $RESPONSE" + + if echo "$RESPONSE" | grep -q '"status":"ok"'; then + echo "โœ… API health check passed" + else + echo "โŒ API health check failed" + exit 1 + fi + + - name: Test App health endpoint + run: | + echo "๐Ÿ” Testing App health endpoint..." + RESPONSE=$(curl -s http://localhost:5173/health) + echo "Response: $RESPONSE" + + if echo "$RESPONSE" | grep -q 'ok'; then + echo "โœ… App health check passed" + else + echo "โŒ App health check failed" + exit 1 + fi + + - name: Verify containers run as non-root + run: | + echo "๐Ÿ”’ Verifying security (non-root users)..." + + API_USER=$(docker exec tuvix-api id -u) + APP_USER=$(docker exec tuvix-app id -u) + + echo "API running as uid: $API_USER" + echo "App running as uid: $APP_USER" + + if [ "$API_USER" = "0" ]; then + echo "โŒ API running as root" + exit 1 + fi + + if [ "$APP_USER" = "0" ]; then + echo "โŒ App running as root" + exit 1 + fi + + if [ "$API_USER" != "1001" ]; then + echo "โš ๏ธ Warning: API not running as expected user (expected: 1001, got: $API_USER)" + fi + + if [ "$APP_USER" != "1001" ]; then + echo "โš ๏ธ Warning: App not running as expected user (expected: 1001, got: $APP_USER)" + fi + + echo "โœ… All containers running as non-root" + + - name: Verify database migrations + run: | + echo "๐Ÿ—„๏ธ Verifying database migrations..." + + # Check if database exists + if docker exec tuvix-api test -f /app/data/test.db; then + echo "โœ… Database file created" + docker exec tuvix-api ls -lh /app/data/test.db + else + echo "โŒ Database file not found" + exit 1 + fi + + # Check migration logs + if docker logs tuvix-api 2>&1 | grep -q "Migrations complete\|โœ… Migrations completed"; then + echo "โœ… Migrations executed successfully" + else + echo "โŒ Migrations not found in logs" + docker logs tuvix-api 2>&1 | grep -i migration || echo "No migration logs found" + exit 1 + fi + + - name: Run smoke tests + run: | + echo "๐Ÿงช Running smoke tests..." + + # Test that API is responding + curl -f http://localhost:3001/health || exit 1 + + # Test that App is serving content + curl -f http://localhost:5173/ || exit 1 + + # Test that App serves static assets + curl -f http://localhost:5173/health || exit 1 + + echo "โœ… All smoke tests passed" + + - name: Show container stats + if: always() + run: | + echo "๐Ÿ“Š Container statistics:" + docker stats --no-stream + + echo "" + echo "๐Ÿ“‹ Container details:" + docker ps -a + + - name: Show logs on failure + if: failure() + run: | + echo "====================" + echo "=== API Logs ===" + echo "====================" + docker logs tuvix-api + + echo "" + echo "====================" + echo "=== App Logs ===" + echo "====================" + docker logs tuvix-app + + echo "" + echo "====================" + echo "=== Docker Compose Logs ===" + echo "====================" + docker compose logs + + - name: Cleanup + if: always() + run: | + echo "๐Ÿงน Cleaning up..." + docker compose down -v + docker system prune -af --volumes diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 00000000..c9f1e735 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,26 @@ +# Docker Compose configuration for CI/CD testing +# This file extends docker-compose.yml with test-specific settings +# +# Usage: +# docker compose -f docker-compose.yml -f docker-compose.test.yml up + +services: + api: + environment: + # Override for testing + - NODE_ENV=test + - DATABASE_PATH=/app/data/test.db + # Faster health checks for CI + healthcheck: + interval: 5s + timeout: 3s + start_period: 10s + retries: 10 + + app: + # Faster health checks for CI + healthcheck: + interval: 5s + timeout: 3s + start_period: 5s + retries: 10 diff --git a/package.json b/package.json index 0430fb67..3c78240b 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "docker:down": "docker compose down", "docker:logs": "docker compose logs -f", "docker:up": "docker compose up -d", + "docker:test": "bash scripts/test-docker.sh", + "docker:test:ci": "docker compose -f docker-compose.yml -f docker-compose.test.yml up --abort-on-container-exit", "format": "pnpm run format:root && pnpm --filter @tuvixrss/tricorder format && pnpm --filter @tuvixrss/api format && pnpm --filter @tuvixrss/app format", "format:check": "pnpm run format:root:check && pnpm --filter @tuvixrss/tricorder format:check && pnpm --filter @tuvixrss/api format:check && pnpm --filter @tuvixrss/app format:check", "format:root": "prettier --write \"*.{js,json,md}\" \"scripts/*.js\"", diff --git a/scripts/test-docker.sh b/scripts/test-docker.sh new file mode 100755 index 00000000..b4939f01 --- /dev/null +++ b/scripts/test-docker.sh @@ -0,0 +1,182 @@ +#!/bin/bash +# Docker testing script for local development +# Tests Docker Compose setup with health checks and validation +# +# Usage: +# ./scripts/test-docker.sh +# pnpm docker:test + +set -e # Exit on error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Functions +log_info() { + echo -e "${BLUE}โ„น๏ธ $1${NC}" +} + +log_success() { + echo -e "${GREEN}โœ… $1${NC}" +} + +log_warning() { + echo -e "${YELLOW}โš ๏ธ $1${NC}" +} + +log_error() { + echo -e "${RED}โŒ $1${NC}" +} + +# Cleanup function +cleanup() { + log_info "Cleaning up..." + docker compose down -v 2>/dev/null || true +} + +# Set trap to cleanup on exit +trap cleanup EXIT INT TERM + +# Main script +main() { + echo "=========================================" + echo "๐Ÿณ Docker Compose Test Suite" + echo "=========================================" + echo "" + + # Check if Docker is running + if ! docker info > /dev/null 2>&1; then + log_error "Docker is not running. Please start Docker and try again." + exit 1 + fi + log_success "Docker is running" + + # Build images + log_info "Building Docker images..." + if docker compose build; then + log_success "Images built successfully" + else + log_error "Failed to build images" + exit 1 + fi + + # Check image sizes + log_info "Checking image sizes..." + API_SIZE=$(docker images --format "{{.Size}}" tuvixrss-api:latest) + APP_SIZE=$(docker images --format "{{.Size}}" tuvixrss-app:latest) + + echo " API: ${API_SIZE}" + echo " App: ${APP_SIZE}" + log_success "Image sizes checked" + + # Start services + log_info "Starting Docker Compose services..." + export BETTER_AUTH_SECRET="test-secret-for-local-testing-minimum-32-chars-required" + export DATABASE_PATH="/app/data/test.db" + + if docker compose up -d; then + log_success "Services started" + else + log_error "Failed to start services" + exit 1 + fi + + # Wait for API health + log_info "Waiting for API to be healthy (max 60s)..." + if timeout 60 bash -c 'until docker inspect --format="{{.State.Health.Status}}" tuvix-api 2>/dev/null | grep -q "healthy"; do sleep 2; done'; then + log_success "API is healthy" + else + log_error "API failed to become healthy" + docker logs tuvix-api + exit 1 + fi + + # Wait for App health + log_info "Waiting for App to be healthy (max 60s)..." + if timeout 60 bash -c 'until docker inspect --format="{{.State.Health.Status}}" tuvix-app 2>/dev/null | grep -q "healthy"; do sleep 2; done'; then + log_success "App is healthy" + else + log_error "App failed to become healthy" + docker logs tuvix-app + exit 1 + fi + + # Test API endpoint + log_info "Testing API health endpoint..." + RESPONSE=$(curl -s http://localhost:3001/health) + if echo "$RESPONSE" | grep -q '"status":"ok"'; then + log_success "API health check passed" + echo " Response: $RESPONSE" + else + log_error "API health check failed" + echo " Response: $RESPONSE" + exit 1 + fi + + # Test App endpoint + log_info "Testing App health endpoint..." + RESPONSE=$(curl -s http://localhost:5173/health) + if echo "$RESPONSE" | grep -q 'ok'; then + log_success "App health check passed" + echo " Response: $RESPONSE" + else + log_error "App health check failed" + echo " Response: $RESPONSE" + exit 1 + fi + + # Verify non-root users + log_info "Verifying security (non-root users)..." + API_USER=$(docker exec tuvix-api id -u) + APP_USER=$(docker exec tuvix-app id -u) + + if [ "$API_USER" = "0" ] || [ "$APP_USER" = "0" ]; then + log_error "Containers running as root!" + echo " API: uid $API_USER" + echo " App: uid $APP_USER" + exit 1 + else + log_success "All containers running as non-root" + echo " API: uid $API_USER (nodejs)" + echo " App: uid $APP_USER (nginx-app)" + fi + + # Verify database + log_info "Verifying database migrations..." + if docker exec tuvix-api test -f /app/data/test.db; then + log_success "Database file exists" + docker exec tuvix-api ls -lh /app/data/test.db + else + log_warning "Database file not found (may not have created yet)" + fi + + if docker logs tuvix-api 2>&1 | grep -q "Migrations complete\|โœ… Migrations completed"; then + log_success "Migrations executed" + else + log_warning "Migration logs not found (check manually if needed)" + fi + + # Show container stats + log_info "Container statistics:" + docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" + + echo "" + echo "=========================================" + log_success "All tests passed!" + echo "=========================================" + echo "" + echo "Services are running at:" + echo " API: http://localhost:3001" + echo " App: http://localhost:5173" + echo "" + echo "To stop services, run:" + echo " docker compose down" + echo "" +} + +# Run main function +main "$@" From 7d06d4632157389ac594e5797caca1a738ddb7d3 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 13:55:46 -0500 Subject: [PATCH 32/41] chore: update deps --- package.json | 4 +- pnpm-lock.yaml | 176 +++++++++++++++++++++++++++++-------------------- 2 files changed, 107 insertions(+), 73 deletions(-) diff --git a/package.json b/package.json index 3c78240b..1de15f7c 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "devDependencies": { "concurrently": "^9.2.1", "nyc": "^17.1.0", - "prettier": "^3.6.2", - "shadcn": "^3.5.0" + "prettier": "^3.7.4", + "shadcn": "^3.5.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1db3cb8..6abcadae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,11 +19,11 @@ importers: specifier: ^17.1.0 version: 17.1.0 prettier: - specifier: ^3.6.2 - version: 3.6.2 + specifier: ^3.7.4 + version: 3.7.4 shadcn: - specifier: ^3.5.0 - version: 3.5.0(@types/node@24.10.1)(typescript@5.9.3) + specifier: ^3.5.1 + version: 3.5.1(@types/node@24.10.1)(typescript@5.9.3) packages/api: dependencies: @@ -147,7 +147,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.13 - version: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6) + version: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.4(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6) wrangler: specifier: ^4.50.0 version: 4.50.0(@cloudflare/workers-types@4.20251121.0) @@ -340,7 +340,7 @@ importers: version: 1.1.0(rolldown-vite@7.2.7(@types/node@24.10.1)(esbuild@0.27.0)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6))(workbox-build@7.3.0(@types/babel__core@7.20.5))(workbox-window@7.4.0) vitest: specifier: ^4.0.13 - version: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6) + version: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.4(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6) workbox-window: specifier: ^7.4.0 version: 7.4.0 @@ -377,7 +377,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.13 - version: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6) + version: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.4(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6) packages: @@ -1648,11 +1648,12 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@modelcontextprotocol/sdk@1.22.0': - resolution: {integrity: sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==} + '@modelcontextprotocol/sdk@1.24.3': + resolution: {integrity: sha512-YgSHW29fuzKKAHTGe9zjNoo+yF8KaQPzDC2W9Pv41E7/57IfY+AMGJ/aDFlgTLcVVELoggKE4syABCE75u3NCw==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 peerDependenciesMeta: '@cfworker/json-schema': optional: true @@ -4066,8 +4067,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.8.31: - resolution: {integrity: sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==} + baseline-browser-mapping@2.9.2: + resolution: {integrity: sha512-PxSsosKQjI38iXkmb3d0Y32efqyA0uW4s41u4IVBsLlWLhCiYNpH/AfNOVWRqCQBlD8TFJTz6OUWNd4DFJCnmw==} hasBin: true bcrypt@6.0.0: @@ -4126,8 +4127,8 @@ packages: blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + body-parser@2.2.1: + resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} engines: {node: '>=18'} brace-expansion@1.1.12: @@ -4140,8 +4141,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.0: - resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -4192,6 +4193,9 @@ packages: caniuse-lite@1.0.30001756: resolution: {integrity: sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==} + caniuse-lite@1.0.30001759: + resolution: {integrity: sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==} + chai@6.2.1: resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} engines: {node: '>=18'} @@ -4348,6 +4352,10 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + core-js-compat@3.47.0: resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} @@ -4696,8 +4704,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.259: - resolution: {integrity: sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==} + electron-to-chromium@1.5.264: + resolution: {integrity: sha512-1tEf0nLgltC3iy9wtlYDlQDc5Rg9lEKVjEmIHJ21rI9OcqkvD45K1oyNIRA4rR1z3LgJ7KeGzEBojVcV6m4qjA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -4907,8 +4915,8 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - execa@9.6.0: - resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} engines: {node: ^18.19.0 || >=20.5.0} exit-hook@2.2.1: @@ -4929,8 +4937,8 @@ packages: peerDependencies: express: '>= 4.11' - express@5.1.0: - resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} exsolve@1.0.8: @@ -5002,9 +5010,9 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@2.1.0: - resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} - engines: {node: '>= 0.8'} + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} find-cache-dir@3.3.2: resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} @@ -5601,6 +5609,9 @@ packages: jose@6.1.2: resolution: {integrity: sha512-MpcPtHLE5EmztuFIqB0vzHAWJPpmN1E6L4oo+kze56LIs3MyXIj9ZHMDxqOvkP38gBR7K1v3jqd4WU2+nrfONQ==} + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -5976,8 +5987,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msw@2.12.3: - resolution: {integrity: sha512-/5rpGC0eK8LlFqsHaBmL19/PVKxu/CCt8pO1vzp9X6SDLsRDh/Ccudkf3Ur5lyaKxJz9ndAx+LaThdv0ySqB6A==} + msw@2.12.4: + resolution: {integrity: sha512-rHNiVfTyKhzc0EjoXUBVGteNKBevdjOlVC6GlIRXpy+/3LHEIGRovnB5WPjcvmNODVQ1TNFnoa7wsGbd0V3epg==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -6175,8 +6186,8 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - package-manager-detector@1.5.0: - resolution: {integrity: sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==} + package-manager-detector@1.6.0: + resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} @@ -6345,6 +6356,11 @@ packages: engines: {node: '>=14'} hasBin: true + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + engines: {node: '>=14'} + hasBin: true + pretty-bytes@5.6.0: resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} engines: {node: '>=6'} @@ -6829,8 +6845,8 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - shadcn@3.5.0: - resolution: {integrity: sha512-5f9tn7gHOiI8CqJ8LGrUEmX6dOivGgSbHkMCeOMCzxHZy9cPwyuRXt7ZyjwukO8QH9kGqi6oiKOpfvTXNGBP4g==} + shadcn@3.5.1: + resolution: {integrity: sha512-yLbIDouYv8Xz25BxV/GAGC/46R7/oNwoXIs/IFIYXK47+fKcFIYzThtBqJwFEZTzkkvqJCo+MBg0K9QLTmhFmQ==} hasBin: true sharp@0.33.5: @@ -7281,6 +7297,10 @@ packages: resolution: {integrity: sha512-xxCJm+Bckc6kQBknN7i9fnP/xobQRsRQxR01CztFkp/h++yfVxUUcmMgfR2HttJx/dpWjS9ubVuyspJv24Q9DA==} engines: {node: '>=20'} + type-fest@5.3.0: + resolution: {integrity: sha512-d9CwU93nN0IA1QL+GSNDdwLAu1Ew5ZjTwupvedwg3WdfoH6pIDvYQ2hV0Uc2nKBLPq7NB5apCx57MLS5qlmO5g==} + engines: {node: '>=20'} + type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -7383,8 +7403,8 @@ packages: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} engines: {node: '>=4'} - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + update-browserslist-db@1.2.2: + resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -7780,7 +7800,7 @@ snapshots: dependencies: ansis: 4.2.0 fzf: 0.5.2 - package-manager-detector: 1.5.0 + package-manager-detector: 1.6.0 tinyexec: 1.0.2 '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': @@ -7862,7 +7882,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.0 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -9061,7 +9081,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@modelcontextprotocol/sdk@1.22.0': + '@modelcontextprotocol/sdk@1.24.3(zod@3.25.76)': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) @@ -9070,8 +9090,9 @@ snapshots: cross-spawn: 7.0.6 eventsource: 3.0.7 eventsource-parser: 3.0.6 - express: 5.1.0 - express-rate-limit: 7.5.1(express@5.1.0) + express: 5.2.1 + express-rate-limit: 7.5.1(express@5.2.1) + jose: 6.1.3 pkce-challenge: 5.0.1 raw-body: 3.0.2 zod: 3.25.76 @@ -11274,7 +11295,7 @@ snapshots: magicast: 0.5.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6) + vitest: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.4(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6) transitivePeerDependencies: - supports-color @@ -11287,13 +11308,13 @@ snapshots: chai: 6.2.1 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.13(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(rolldown-vite@7.1.14(@types/node@24.10.1)(esbuild@0.27.0)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6))': + '@vitest/mocker@4.0.13(msw@2.12.4(@types/node@24.10.1)(typescript@5.9.3))(rolldown-vite@7.1.14(@types/node@24.10.1)(esbuild@0.27.0)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6))': dependencies: '@vitest/spy': 4.0.13 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.12.3(@types/node@24.10.1)(typescript@5.9.3) + msw: 2.12.4(@types/node@24.10.1)(typescript@5.9.3) vite: rolldown-vite@7.1.14(@types/node@24.10.1)(esbuild@0.27.0)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6) '@vitest/pretty-format@4.0.13': @@ -11322,7 +11343,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6) + vitest: 4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.4(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6) '@vitest/utils@4.0.13': dependencies: @@ -11502,7 +11523,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.8.31: {} + baseline-browser-mapping@2.9.2: {} bcrypt@6.0.0: dependencies: @@ -11558,13 +11579,13 @@ snapshots: blake3-wasm@2.1.5: {} - body-parser@2.2.0: + body-parser@2.2.1: dependencies: bytes: 3.1.2 content-type: 1.0.5 debug: 4.4.3 http-errors: 2.0.1 - iconv-lite: 0.6.3 + iconv-lite: 0.7.0 on-finished: 2.4.1 qs: 6.14.0 raw-body: 3.0.2 @@ -11585,13 +11606,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.0: + browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.8.31 - caniuse-lite: 1.0.30001756 - electron-to-chromium: 1.5.259 + baseline-browser-mapping: 2.9.2 + caniuse-lite: 1.0.30001759 + electron-to-chromium: 1.5.264 node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.28.0) + update-browserslist-db: 1.2.2(browserslist@4.28.1) buffer-from@1.1.2: {} @@ -11639,6 +11660,8 @@ snapshots: caniuse-lite@1.0.30001756: {} + caniuse-lite@1.0.30001759: {} + chai@6.2.1: {} chalk@4.1.2: @@ -11779,9 +11802,11 @@ snapshots: cookie@1.0.2: {} + cookie@1.1.1: {} + core-js-compat@3.47.0: dependencies: - browserslist: 4.28.0 + browserslist: 4.28.1 cors@2.8.5: dependencies: @@ -12017,7 +12042,7 @@ snapshots: dependencies: jake: 10.9.4 - electron-to-chromium@1.5.259: {} + electron-to-chromium@1.5.264: {} emoji-regex@10.6.0: {} @@ -12321,7 +12346,7 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - execa@9.6.0: + execa@9.6.1: dependencies: '@sindresorhus/merge-streams': 4.0.0 cross-spawn: 7.0.6 @@ -12342,23 +12367,24 @@ snapshots: expect-type@1.2.2: {} - express-rate-limit@7.5.1(express@5.1.0): + express-rate-limit@7.5.1(express@5.2.1): dependencies: - express: 5.1.0 + express: 5.2.1 - express@5.1.0: + express@5.2.1: dependencies: accepts: 2.0.0 - body-parser: 2.2.0 + body-parser: 2.2.1 content-disposition: 1.0.1 content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 debug: 4.4.3 + depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 2.1.0 + finalhandler: 2.1.1 fresh: 2.0.0 http-errors: 2.0.1 merge-descriptors: 2.0.0 @@ -12444,7 +12470,7 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@2.1.0: + finalhandler@2.1.1: dependencies: debug: 4.4.3 encodeurl: 2.0.0 @@ -13027,6 +13053,8 @@ snapshots: jose@6.1.2: {} + jose@6.1.3: {} + joycon@3.1.1: {} js-tokens@4.0.0: {} @@ -13356,13 +13384,13 @@ snapshots: ms@2.1.3: {} - msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3): + msw@2.12.4(@types/node@24.10.1)(typescript@5.9.3): dependencies: '@inquirer/confirm': 5.1.21(@types/node@24.10.1) '@mswjs/interceptors': 0.40.0 '@open-draft/deferred-promise': 2.2.0 '@types/statuses': 2.0.6 - cookie: 1.0.2 + cookie: 1.1.1 graphql: 16.12.0 headers-polyfill: 4.0.3 is-node-process: 1.2.0 @@ -13373,7 +13401,7 @@ snapshots: statuses: 2.0.2 strict-event-emitter: 0.5.1 tough-cookie: 6.0.0 - type-fest: 5.2.0 + type-fest: 5.3.0 until-async: 3.0.2 yargs: 17.7.2 optionalDependencies: @@ -13615,7 +13643,7 @@ snapshots: package-json-from-dist@1.0.1: {} - package-manager-detector@1.5.0: {} + package-manager-detector@1.6.0: {} parent-module@1.0.1: dependencies: @@ -13762,6 +13790,8 @@ snapshots: prettier@3.6.2: {} + prettier@3.7.4: {} + pretty-bytes@5.6.0: {} pretty-bytes@6.1.1: {} @@ -14358,7 +14388,7 @@ snapshots: setprototypeof@1.2.0: {} - shadcn@3.5.0(@types/node@24.10.1)(typescript@5.9.3): + shadcn@3.5.1(@types/node@24.10.1)(typescript@5.9.3): dependencies: '@antfu/ni': 25.0.0 '@babel/core': 7.28.5 @@ -14366,20 +14396,20 @@ snapshots: '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) '@dotenvx/dotenvx': 1.51.1 - '@modelcontextprotocol/sdk': 1.22.0 - browserslist: 4.28.0 + '@modelcontextprotocol/sdk': 1.24.3(zod@3.25.76) + browserslist: 4.28.1 commander: 14.0.2 cosmiconfig: 9.0.0(typescript@5.9.3) dedent: 1.7.0 deepmerge: 4.3.1 diff: 8.0.2 - execa: 9.6.0 + execa: 9.6.1 fast-glob: 3.3.3 fs-extra: 11.3.2 fuzzysort: 3.1.0 https-proxy-agent: 7.0.6 kleur: 4.1.5 - msw: 2.12.3(@types/node@24.10.1)(typescript@5.9.3) + msw: 2.12.4(@types/node@24.10.1)(typescript@5.9.3) node-fetch: 3.3.2 ora: 8.2.0 postcss: 8.5.6 @@ -14923,6 +14953,10 @@ snapshots: dependencies: tagged-tag: 1.0.0 + type-fest@5.3.0: + dependencies: + tagged-tag: 1.0.0 + type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -15032,9 +15066,9 @@ snapshots: upath@1.2.0: {} - update-browserslist-db@1.1.4(browserslist@4.28.0): + update-browserslist-db@1.2.2(browserslist@4.28.1): dependencies: - browserslist: 4.28.0 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -15111,10 +15145,10 @@ snapshots: transitivePeerDependencies: - supports-color - vitest@4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6): + vitest@4.0.13(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(@vitest/ui@4.0.13)(esbuild@0.27.0)(jiti@2.6.1)(jsdom@27.2.0)(msw@2.12.4(@types/node@24.10.1)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6): dependencies: '@vitest/expect': 4.0.13 - '@vitest/mocker': 4.0.13(msw@2.12.3(@types/node@24.10.1)(typescript@5.9.3))(rolldown-vite@7.1.14(@types/node@24.10.1)(esbuild@0.27.0)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6)) + '@vitest/mocker': 4.0.13(msw@2.12.4(@types/node@24.10.1)(typescript@5.9.3))(rolldown-vite@7.1.14(@types/node@24.10.1)(esbuild@0.27.0)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6)) '@vitest/pretty-format': 4.0.13 '@vitest/runner': 4.0.13 '@vitest/snapshot': 4.0.13 From aaca18599b843616b6123e763a01895a0beb2918 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 14:10:32 -0500 Subject: [PATCH 33/41] chore: add GitHub Actions version check script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add script to check for outdated GitHub Actions locally without waiting for Dependabot's monthly schedule. Usage: ./scripts/check-gha-versions.sh ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- scripts/check-gha-versions.sh | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100755 scripts/check-gha-versions.sh diff --git a/scripts/check-gha-versions.sh b/scripts/check-gha-versions.sh new file mode 100755 index 00000000..07407402 --- /dev/null +++ b/scripts/check-gha-versions.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Check GitHub Actions for updates +# Usage: ./scripts/check-gha-versions.sh + +set -e + +echo "Checking GitHub Actions versions..." +echo "" + +# Extract all action versions from workflows +grep -r "uses: " .github/workflows/ | grep -v "^#" | sed 's/.*uses: //' | sort -u | while read -r action; do + # Split action@version + action_name=$(echo "$action" | cut -d'@' -f1) + current_version=$(echo "$action" | cut -d'@' -f2) + + # Get owner and repo + owner=$(echo "$action_name" | cut -d'/' -f1) + repo=$(echo "$action_name" | cut -d'/' -f2) + + # Fetch latest version from GitHub API + latest=$(curl -s "https://api.github.com/repos/$owner/$repo/releases/latest" | grep '"tag_name"' | cut -d'"' -f4) + + if [ -z "$latest" ]; then + echo "โš ๏ธ $action_name@$current_version (no releases found)" + elif [ "$current_version" = "$latest" ]; then + echo "โœ… $action_name@$current_version (up to date)" + else + echo "โฌ†๏ธ $action_name@$current_version โ†’ $latest (update available)" + fi +done + +echo "" +echo "To update manually, edit .github/workflows/*.yml" +echo "Or wait for Dependabot's monthly check" From e7fc6c99fc1ea1db91e00e1bc9cf5155db875a7a Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 14:10:41 -0500 Subject: [PATCH 34/41] ci: update GitHub Actions to latest major versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update GitHub Actions dependencies: - actions/checkout@v4 โ†’ @v6 - actions/github-script@v7 โ†’ @v8 - actions/setup-node@v5 โ†’ @v6 - actions/upload-artifact@v4 โ†’ @v5 Major version pinning allows automatic security patches while preventing breaking changes. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/ci-reusable.yml | 6 +++--- .github/workflows/ci-tricorder.yml | 14 +++++++------- .github/workflows/docker-test.yml | 2 +- .github/workflows/publish-tricorder.yml | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci-reusable.yml b/.github/workflows/ci-reusable.yml index 6e8c5d53..9d927eca 100644 --- a/.github/workflows/ci-reusable.yml +++ b/.github/workflows/ci-reusable.yml @@ -111,7 +111,7 @@ jobs: - name: Upload API test coverage if: ${{ inputs.upload_coverage }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ inputs.pr_number && format('api-coverage-pr-{0}', inputs.pr_number) || 'api-coverage' }} path: packages/api/coverage @@ -145,7 +145,7 @@ jobs: - name: Upload App test coverage if: ${{ inputs.upload_coverage }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ inputs.pr_number && format('app-coverage-pr-{0}', inputs.pr_number) || 'app-coverage' }} path: packages/app/coverage @@ -185,7 +185,7 @@ jobs: - name: Upload build artifacts if: ${{ inputs.upload_artifacts }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: build-artifacts path: | diff --git a/.github/workflows/ci-tricorder.yml b/.github/workflows/ci-tricorder.yml index 900f5ad3..9a337511 100644 --- a/.github/workflows/ci-tricorder.yml +++ b/.github/workflows/ci-tricorder.yml @@ -46,7 +46,7 @@ jobs: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' @@ -70,7 +70,7 @@ jobs: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' @@ -94,7 +94,7 @@ jobs: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' @@ -118,7 +118,7 @@ jobs: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' @@ -130,7 +130,7 @@ jobs: run: pnpm --filter @tuvixrss/tricorder test:coverage - name: Upload coverage - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: tricorder-coverage path: packages/tricorder/coverage @@ -158,7 +158,7 @@ jobs: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' @@ -178,7 +178,7 @@ jobs: echo "โœ… Package is ready for publishing" - name: Upload build artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: tricorder-dist path: packages/tricorder/dist diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index e3575029..1141d7ff 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/.github/workflows/publish-tricorder.yml b/.github/workflows/publish-tricorder.yml index d77005f5..80e8c347 100644 --- a/.github/workflows/publish-tricorder.yml +++ b/.github/workflows/publish-tricorder.yml @@ -71,7 +71,7 @@ jobs: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' @@ -100,7 +100,7 @@ jobs: npm pack --dry-run - name: Upload build artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: tricorder-dist path: packages/tricorder/dist @@ -125,7 +125,7 @@ jobs: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js with NPM registry - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' @@ -206,7 +206,7 @@ jobs: - name: Create GitHub Release if: startsWith(github.ref, 'refs/tags/') && steps.check_version.outputs.already_published == 'false' - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const tagRef = context.ref.replace('refs/tags/', ''); From 98fe7267b8b3f244b14157ceeafd859dd2a2dd31 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 14:41:09 -0500 Subject: [PATCH 35/41] fix(docker): increase health check start period to 30s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The API container needs time to: - Run database migrations - Initialize Sentry - Start cron jobs - Boot the Hono server Increased from 5s to 30s to prevent false health check failures in CI environments. Also fixed test-docker.sh to work on macOS (no timeout command). ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/Dockerfile | 2 +- scripts/test-docker.sh | 34 ++++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/api/Dockerfile b/packages/api/Dockerfile index d27b7da3..771da193 100644 --- a/packages/api/Dockerfile +++ b/packages/api/Dockerfile @@ -58,7 +58,7 @@ USER nodejs EXPOSE 3001 # Health check -HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ +HEALTHCHECK --interval=30s --timeout=3s --start-period=30s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1 # Start the server (migrations run automatically inside node.ts) diff --git a/scripts/test-docker.sh b/scripts/test-docker.sh index b4939f01..61deac8a 100755 --- a/scripts/test-docker.sh +++ b/scripts/test-docker.sh @@ -87,23 +87,29 @@ main() { # Wait for API health log_info "Waiting for API to be healthy (max 60s)..." - if timeout 60 bash -c 'until docker inspect --format="{{.State.Health.Status}}" tuvix-api 2>/dev/null | grep -q "healthy"; do sleep 2; done'; then - log_success "API is healthy" - else - log_error "API failed to become healthy" - docker logs tuvix-api - exit 1 - fi + SECONDS=0 + until docker inspect --format="{{.State.Health.Status}}" tuvix-api 2>/dev/null | grep -q "healthy"; do + if [ $SECONDS -gt 60 ]; then + log_error "API failed to become healthy after 60s" + docker logs tuvix-api + exit 1 + fi + sleep 2 + done + log_success "API is healthy" # Wait for App health log_info "Waiting for App to be healthy (max 60s)..." - if timeout 60 bash -c 'until docker inspect --format="{{.State.Health.Status}}" tuvix-app 2>/dev/null | grep -q "healthy"; do sleep 2; done'; then - log_success "App is healthy" - else - log_error "App failed to become healthy" - docker logs tuvix-app - exit 1 - fi + SECONDS=0 + until docker inspect --format="{{.State.Health.Status}}" tuvix-app 2>/dev/null | grep -q "healthy"; do + if [ $SECONDS -gt 60 ]; then + log_error "App failed to become healthy after 60s" + docker logs tuvix-app + exit 1 + fi + sleep 2 + done + log_success "App is healthy" # Test API endpoint log_info "Testing API health endpoint..." From 0edc0843500a1cb51192252ad14abe966cf03f26 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 14:55:14 -0500 Subject: [PATCH 36/41] fix(docker): update docker-compose health check timing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docker-compose.yml health check configuration overrides the Dockerfile HEALTHCHECK. Updated both to use consistent timing: - API: 30s start period (needs time for migrations + Sentry + cron) - App: 10s start period (nginx starts faster) This fixes the CI failure where docker compose up -d was exiting because the API was marked unhealthy before fully starting. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 58582c99..32fad177 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,7 +23,7 @@ services: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"] interval: 30s timeout: 3s - start_period: 5s + start_period: 30s retries: 3 networks: - tuvix-network @@ -43,7 +43,7 @@ services: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/health"] interval: 30s timeout: 3s - start_period: 5s + start_period: 10s retries: 3 depends_on: api: From 93d228291bab3b51deb8189382e28e7b74d351d3 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 15:04:53 -0500 Subject: [PATCH 37/41] ci(docker): add detailed debugging for health check failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Start API and App containers separately (not docker compose up -d) - Show container status and logs immediately after starting - Poll health status every 2s with timestamp logging - Show API logs every 10s while waiting - Increased API wait time to 90s (was 60s implicit) - Show full logs on failure for better debugging This allows us to see exactly what's happening during startup in CI and diagnose why health checks are failing. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/docker-test.yml | 89 +++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index 1141d7ff..fb499fb2 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -77,33 +77,90 @@ jobs: echo "api_size=${API_SIZE_RAW}" >> $GITHUB_OUTPUT echo "app_size=${APP_SIZE_RAW}" >> $GITHUB_OUTPUT - - name: Start Docker Compose services + - name: Start API container run: | - echo "๐Ÿš€ Starting Docker Compose services..." - docker compose up -d - - echo "โณ Waiting for containers to start..." - sleep 5 + echo "๐Ÿš€ Starting API container..." + docker compose up -d api --wait=false + echo "๐Ÿ“‹ Container status:" docker ps -a + echo "" + echo "๐Ÿ” Initial API logs:" + sleep 2 + docker logs tuvix-api 2>&1 | head -30 || echo "No logs yet" + - name: Wait for API to be healthy run: | - echo "โณ Waiting for API to be healthy..." - timeout 60 bash -c 'until docker inspect --format="{{.State.Health.Status}}" tuvix-api 2>/dev/null | grep -q "healthy"; do - echo "API status: $(docker inspect --format="{{.State.Health.Status}}" tuvix-api 2>/dev/null || echo "not found")" + echo "โณ Waiting for API to be healthy (max 90s)..." + SECONDS=0 + MAX_WAIT=90 + + while [ $SECONDS -lt $MAX_WAIT ]; do + STATUS=$(docker inspect --format="{{.State.Health.Status}}" tuvix-api 2>/dev/null || echo "none") + echo "[$SECONDS s] API health status: $STATUS" + + if [ "$STATUS" = "healthy" ]; then + echo "โœ… API is healthy!" + break + fi + + # Show recent logs every 10 seconds + if [ $((SECONDS % 10)) -eq 0 ]; then + echo "๐Ÿ“œ Recent API logs:" + docker logs tuvix-api 2>&1 | tail -20 + fi + sleep 2 - done' - echo "โœ… API is healthy" + done + + if [ $SECONDS -ge $MAX_WAIT ]; then + echo "โŒ API failed to become healthy after ${MAX_WAIT}s" + echo "" + echo "๐Ÿ“‹ Container status:" + docker ps -a + echo "" + echo "๐Ÿ“œ Full API logs:" + docker logs tuvix-api 2>&1 + exit 1 + fi + + - name: Start App container + run: | + echo "๐Ÿš€ Starting App container..." + docker compose up -d app + + echo "๐Ÿ“‹ Container status:" + docker ps -a - name: Wait for App to be healthy run: | - echo "โณ Waiting for App to be healthy..." - timeout 60 bash -c 'until docker inspect --format="{{.State.Health.Status}}" tuvix-app 2>/dev/null | grep -q "healthy"; do - echo "App status: $(docker inspect --format="{{.State.Health.Status}}" tuvix-app 2>/dev/null || echo "not found")" + echo "โณ Waiting for App to be healthy (max 60s)..." + SECONDS=0 + MAX_WAIT=60 + + while [ $SECONDS -lt $MAX_WAIT ]; do + STATUS=$(docker inspect --format="{{.State.Health.Status}}" tuvix-app 2>/dev/null || echo "none") + echo "[$SECONDS s] App health status: $STATUS" + + if [ "$STATUS" = "healthy" ]; then + echo "โœ… App is healthy!" + break + fi + sleep 2 - done' - echo "โœ… App is healthy" + done + + if [ $SECONDS -ge $MAX_WAIT ]; then + echo "โŒ App failed to become healthy after ${MAX_WAIT}s" + echo "" + echo "๐Ÿ“‹ Container status:" + docker ps -a + echo "" + echo "๐Ÿ“œ Full App logs:" + docker logs tuvix-app 2>&1 + exit 1 + fi - name: Test API health endpoint run: | From dc5e031364f282560b898e6aec3f161cc9149540 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 15:14:41 -0500 Subject: [PATCH 38/41] fix(ci): create data directory before starting containers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The real issue was a permissions problem with the SQLite database: - Container runs as uid 1001 (nodejs user) - Volume mount ./data:/app/data didn't exist in CI - When Docker creates the mount point, it's owned by root - nodejs user can't write to /app/data/tuvix.db Solution: Create ./data directory with 777 permissions before starting containers, allowing uid 1001 to write the database file. This fixes the "SqliteError: unable to open database file" crash that was causing the health check failures. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/docker-test.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index fb499fb2..2d090955 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -77,6 +77,14 @@ jobs: echo "api_size=${API_SIZE_RAW}" >> $GITHUB_OUTPUT echo "app_size=${APP_SIZE_RAW}" >> $GITHUB_OUTPUT + - name: Prepare data directory + run: | + echo "๐Ÿ“ Creating data directory with proper permissions..." + mkdir -p ./data + # Set permissions so container user (uid 1001) can write + chmod 777 ./data + ls -la ./data + - name: Start API container run: | echo "๐Ÿš€ Starting API container..." From ba6d7ff9cd1c00fae6cf5074071620929795d8c9 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 18:16:36 -0500 Subject: [PATCH 39/41] fix(docker): increase health check frequency for faster CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed health check interval from 30s to 5s for both containers. This allows health status to update quickly instead of waiting 30s between each check. With the old config: - start_period: 10s - interval: 30s - retries: 3 - = could take 100+ seconds to become healthy With new config: - start_period: 10-30s - interval: 5s - retries: 3 - = becomes healthy in 10-40 seconds Also increased App wait timeout to 120s to be safe. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/docker-test.yml | 4 ++-- docker-compose.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index 2d090955..ad1731e8 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -143,9 +143,9 @@ jobs: - name: Wait for App to be healthy run: | - echo "โณ Waiting for App to be healthy (max 60s)..." + echo "โณ Waiting for App to be healthy (max 120s)..." SECONDS=0 - MAX_WAIT=60 + MAX_WAIT=120 while [ $SECONDS -lt $MAX_WAIT ]; do STATUS=$(docker inspect --format="{{.State.Health.Status}}" tuvix-app 2>/dev/null || echo "none") diff --git a/docker-compose.yml b/docker-compose.yml index 32fad177..6abc2070 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,7 @@ services: - SENTRY_RELEASE=${SENTRY_RELEASE:-} healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"] - interval: 30s + interval: 5s timeout: 3s start_period: 30s retries: 3 @@ -41,7 +41,7 @@ services: - "5173:80" healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/health"] - interval: 30s + interval: 5s timeout: 3s start_period: 10s retries: 3 From a4e6b3f651689dcb4a48a31fbd7f1d787be31f38 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 18:49:04 -0500 Subject: [PATCH 40/41] fix(docker): use non-privileged port 8080 for nginx app container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change nginx to listen on port 8080 instead of 80 (non-root compatible) - Update port mapping from 5173:80 to 5173:8080 - Fix health checks to use explicit IPv4 (127.0.0.1) instead of localhost - Move non-root user creation before nginx config for clarity - Add manual health check test in CI workflow This fixes the app container health check failures while maintaining security by running as non-root user (uid 1001). ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/docker-test.yml | 5 +++++ docker-compose.yml | 4 ++-- packages/app/Dockerfile | 25 +++++++++++++------------ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml index ad1731e8..15d4381f 100644 --- a/.github/workflows/docker-test.yml +++ b/.github/workflows/docker-test.yml @@ -141,6 +141,11 @@ jobs: echo "๐Ÿ“‹ Container status:" docker ps -a + echo "" + echo "๐Ÿงช Testing health endpoint manually:" + sleep 2 + docker exec tuvix-app wget -O- http://127.0.0.1:8080/health || echo "Manual health check failed" + - name: Wait for App to be healthy run: | echo "โณ Waiting for App to be healthy (max 120s)..." diff --git a/docker-compose.yml b/docker-compose.yml index 6abc2070..6819b5ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,9 +38,9 @@ services: container_name: tuvix-app restart: unless-stopped ports: - - "5173:80" + - "5173:8080" healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/health"] + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:8080/health"] interval: 5s timeout: 3s start_period: 10s diff --git a/packages/app/Dockerfile b/packages/app/Dockerfile index 808abedb..094110ce 100644 --- a/packages/app/Dockerfile +++ b/packages/app/Dockerfile @@ -32,9 +32,19 @@ FROM nginx:alpine # Copy built files from builder COPY --from=builder /app/packages/app/dist /usr/share/nginx/html +# Create non-root user for nginx +RUN addgroup -g 1001 -S nginx-app && \ + adduser -S nginx-app -u 1001 && \ + chown -R nginx-app:nginx-app /usr/share/nginx/html && \ + chown -R nginx-app:nginx-app /var/cache/nginx && \ + chown -R nginx-app:nginx-app /var/log/nginx && \ + touch /var/run/nginx.pid && \ + chown -R nginx-app:nginx-app /var/run/nginx.pid + # Create nginx configuration for SPA routing and health check +# Listen on port 8080 (non-privileged) since we run as non-root user RUN echo 'server { \ - listen 80; \ + listen 8080; \ server_name _; \ root /usr/share/nginx/html; \ index index.html; \ @@ -57,21 +67,12 @@ RUN echo 'server { \ } \ }' > /etc/nginx/conf.d/default.conf -# Create non-root user for nginx -RUN addgroup -g 1001 -S nginx-app && \ - adduser -S nginx-app -u 1001 && \ - chown -R nginx-app:nginx-app /usr/share/nginx/html && \ - chown -R nginx-app:nginx-app /var/cache/nginx && \ - chown -R nginx-app:nginx-app /var/log/nginx && \ - touch /var/run/nginx.pid && \ - chown -R nginx-app:nginx-app /var/run/nginx.pid - USER nginx-app -EXPOSE 80 +EXPOSE 8080 # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:80/health || exit 1 + CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:8080/health || exit 1 CMD ["nginx", "-g", "daemon off;"] From 032e9b777a33acb8cb0edf8214e7e3f6332efb88 Mon Sep 17 00:00:00 2001 From: KyleTryon Date: Thu, 4 Dec 2025 19:24:23 -0500 Subject: [PATCH 41/41] fix(docker): respect DATABASE_PATH environment variable in docker-compose MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change hardcoded DATABASE_PATH to use environment variable with fallback - Allows CI to use /app/data/test.db while local dev uses /app/data/tuvix.db - Fixes CI database verification step that was looking for test.db This resolves the "Database file not found" error in CI workflow. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6819b5ee..81800ba4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: volumes: - ./data:/app/data environment: - - DATABASE_PATH=/app/data/tuvix.db + - DATABASE_PATH=${DATABASE_PATH:-/app/data/tuvix.db} - PORT=3001 - NODE_ENV=production - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-change-me-in-production}