diff --git a/.gitignore b/.gitignore index f4b3edd2..1dfe4546 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,7 @@ yarn-error.log* .cursor/ .vscode/ + +# Docker Files +docker-compose.override.yml +compose.override.yml \ No newline at end of file diff --git a/Dockerfile.dev b/Dockerfile.dev index 2e4b9893..18dd04f8 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -35,7 +35,7 @@ ENV CHOKIDAR_USEPOLLING=true COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY turbo.json ./ -# Copy package.json files from all workspaces +# Copy package.json files from all workspaces (needed for dependency installation) COPY apps/frontend/package.json ./apps/frontend/ COPY apps/backend/package.json ./apps/backend/ COPY packages/eslint-config/package.json ./packages/eslint-config/ @@ -46,8 +46,10 @@ COPY packages/zod-types/package.json ./packages/zod-types/ # Install all dependencies (including dev dependencies) RUN pnpm install -# Create necessary directories for volume mounts -RUN mkdir -p apps/frontend apps/backend packages/trpc packages/zod-types packages/eslint-config packages/typescript-config +# Copy source code directories (will be overridden by volume mounts, but ensures structure exists) +# This ensures the directory structure is present even if volumes don't mount correctly +COPY apps ./apps +COPY packages ./packages # Expose ports EXPOSE 12008 12009 diff --git a/apps/backend/package.json b/apps/backend/package.json index 980d2e7e..bf58645e 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -13,6 +13,9 @@ "clean": "rm -rf dist", "lint": "eslint . --max-warnings 0", "lint:fix": "eslint . --fix", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", "db:generate": "dotenv -e ../../.env -- drizzle-kit generate", "db:generate:dev": "dotenv -e ../../.env.local -- drizzle-kit generate", "db:migrate": "dotenv -e ../../.env -- drizzle-kit migrate", @@ -47,12 +50,14 @@ "@types/node": "^20.0.0", "@types/pg": "^8.15.4", "@types/shell-quote": "^1.7.5", + "@vitest/coverage-v8": "^4.0.0", "dotenv-cli": "^8.0.0", "drizzle-kit": "^0.31.1", "eslint": "^9.28.0", "tsup": "^8.5.0", "tsx": "^4.20.3", - "typescript": "^5.0.0" + "typescript": "^5.0.0", + "vitest": "^4.0.0" }, "keywords": [], "author": "", diff --git a/apps/backend/src/db/repositories/tools.repo.ts b/apps/backend/src/db/repositories/tools.repo.ts index ef88d7c0..a8e4558d 100644 --- a/apps/backend/src/db/repositories/tools.repo.ts +++ b/apps/backend/src/db/repositories/tools.repo.ts @@ -3,7 +3,7 @@ import { ToolCreateInput, ToolUpsertInput, } from "@repo/zod-types"; -import { eq, sql } from "drizzle-orm"; +import { and, eq, notInArray, sql } from "drizzle-orm"; import { db } from "../index"; import { toolsTable } from "../schema"; @@ -40,7 +40,7 @@ export class ToolsRepository { })); // Batch insert all tools with upsert - return await db + const result = await db .insert(toolsTable) .values(toolsToInsert) .onConflictDoUpdate({ @@ -52,6 +52,8 @@ export class ToolsRepository { }, }) .returning(); + + return result; } async findByUuid(uuid: string): Promise { @@ -72,6 +74,62 @@ export class ToolsRepository { return deletedTool; } + + /** + * Delete tools that are no longer present in the current tool list + * @param mcpServerUuid - UUID of the MCP server + * @param currentToolNames - Array of tool names that currently exist in the MCP server + * @returns Array of deleted tools + */ + async deleteObsoleteTools( + mcpServerUuid: string, + currentToolNames: string[], + ): Promise { + if (currentToolNames.length === 0) { + // If no tools are provided, delete all tools for this server + return await db + .delete(toolsTable) + .where(eq(toolsTable.mcp_server_uuid, mcpServerUuid)) + .returning(); + } + + // Delete tools that are in DB but not in current tool list + return await db + .delete(toolsTable) + .where( + and( + eq(toolsTable.mcp_server_uuid, mcpServerUuid), + notInArray(toolsTable.name, currentToolNames), + ), + ) + .returning(); + } + + /** + * Sync tools for a server: upsert current tools and delete obsolete ones + * @param input - Tool upsert input containing tools and server UUID + * @returns Object with upserted and deleted tools + */ + async syncTools(input: ToolUpsertInput): Promise<{ + upserted: DatabaseTool[]; + deleted: DatabaseTool[]; + }> { + const currentToolNames = input.tools.map((tool) => tool.name); + + // First, delete obsolete tools + const deleted = await this.deleteObsoleteTools( + input.mcpServerUuid, + currentToolNames, + ); + + // Then, upsert current tools + let upserted: DatabaseTool[] = []; + if (input.tools.length > 0) { + upserted = await this.bulkUpsert(input); + } + + return { upserted, deleted }; + } } export const toolsRepository = new ToolsRepository(); diff --git a/apps/backend/src/lib/metamcp/metamcp-proxy.ts b/apps/backend/src/lib/metamcp/metamcp-proxy.ts index 90a91097..728e6d87 100644 --- a/apps/backend/src/lib/metamcp/metamcp-proxy.ts +++ b/apps/backend/src/lib/metamcp/metamcp-proxy.ts @@ -26,6 +26,7 @@ import { configService } from "../config.service"; import { ConnectedClient } from "./client"; import { getMcpServers } from "./fetch-metamcp"; import { mcpServerPool } from "./mcp-server-pool"; +import { toolsSyncCache } from "./tools-sync-cache"; import { createFilterCallToolMiddleware, createFilterListToolsMiddleware, @@ -143,6 +144,8 @@ export const createServer = async ( request, context, ) => { + console.log("[DEBUG-TOOLS] 🔍 tools/list called for namespace:", namespaceUuid); + const startTime = performance.now(); const serverParams = await getMcpServers( context.namespaceUuid, includeInactiveServers, @@ -155,10 +158,15 @@ export const createServer = async ( // We'll filter servers during processing after getting sessions to check actual MCP server names const allServerEntries = Object.entries(serverParams); + console.log(`[DEBUG-TOOLS] 📋 Processing ${allServerEntries.length} servers`); + await Promise.allSettled( allServerEntries.map(async ([mcpServerUuid, params]) => { + console.log(`[DEBUG-TOOLS] 🔧 Server: ${params.name || mcpServerUuid}`); + // Skip if we've already visited this server to prevent circular references if (visitedServers.has(mcpServerUuid)) { + console.log(`[DEBUG-TOOLS] ⏭️ Skipping already visited: ${params.name}`); return; } const session = await mcpServerPool.getSession( @@ -167,7 +175,10 @@ export const createServer = async ( params, namespaceUuid, ); - if (!session) return; + if (!session) { + console.log(`[DEBUG-TOOLS] ❌ No session for: ${params.name}`); + return; + } // Now check for self-referencing using the actual MCP server name const serverVersion = session.client.getServerVersion(); @@ -201,6 +212,7 @@ export const createServer = async ( const allServerTools: Tool[] = []; let cursor: string | undefined = undefined; let hasMore = true; + const toolFetchStart = performance.now(); while (hasMore) { const result: z.infer = @@ -222,12 +234,20 @@ export const createServer = async ( cursor = result.nextCursor; hasMore = !!result.nextCursor; } + + console.log(`[DEBUG-TOOLS] ⏱️ Fetched ${allServerTools.length} tools from ${serverName} in ${(performance.now() - toolFetchStart).toFixed(2)}ms`); // Save original tools to database (before middleware processing) // This ensures we only save the actual tool names, not override names // Filter out tools that are overrides of existing tools to prevent duplicates - if (allServerTools.length > 0) { - try { + try { + // PERFORMANCE OPTIMIZATION: Check hash FIRST to avoid expensive operations + const toolNames = allServerTools.map((tool) => tool.name); + const hasChanged = toolsSyncCache.hasChanged(mcpServerUuid, toolNames); + + console.log(`[DEBUG-TOOLS] 🔍 Hash check for ${serverName}: ${hasChanged ? 'CHANGED' : 'UNCHANGED'}`); + + if (hasChanged) { const toolsToSave = await filterOutOverrideTools( allServerTools, namespaceUuid, @@ -235,17 +255,21 @@ export const createServer = async ( ); if (toolsToSave.length > 0) { - await toolsImplementations.create({ + // Update cache + toolsSyncCache.update(mcpServerUuid, toolNames); + + // Sync with cleanup + await toolsImplementations.sync({ tools: toolsToSave, mcpServerUuid: mcpServerUuid, }); } - } catch (dbError) { - console.error( - `Error saving tools to database for server ${serverName}:`, - dbError, - ); } + } catch (dbError) { + console.error( + `Error syncing tools to database for server ${serverName}:`, + dbError, + ); } // Use original tools for client response (middleware will be applied later) @@ -268,6 +292,9 @@ export const createServer = async ( }), ); + const totalTime = performance.now() - startTime; + console.log(`[DEBUG-TOOLS] ✅ tools/list completed in ${totalTime.toFixed(2)}ms, returning ${allTools.length} tools`); + return { tools: allTools }; }; diff --git a/apps/backend/src/lib/metamcp/tools-sync-cache.test.ts b/apps/backend/src/lib/metamcp/tools-sync-cache.test.ts new file mode 100644 index 00000000..512aa6f6 --- /dev/null +++ b/apps/backend/src/lib/metamcp/tools-sync-cache.test.ts @@ -0,0 +1,244 @@ +import { describe, it, expect, beforeEach } from "vitest"; +import { ToolsSyncCache } from "./tools-sync-cache"; + +describe("ToolsSyncCache", () => { + let cache: ToolsSyncCache; + + beforeEach(() => { + cache = new ToolsSyncCache(); + }); + + describe("hashTools", () => { + it("should generate consistent hash for same tools", () => { + const tools = ["tool1", "tool2", "tool3"]; + const hash1 = cache.hashTools(tools); + const hash2 = cache.hashTools(tools); + + expect(hash1).toBe(hash2); + }); + + it("should generate same hash regardless of order", () => { + const tools1 = ["tool1", "tool2", "tool3"]; + const tools2 = ["tool3", "tool1", "tool2"]; + const hash1 = cache.hashTools(tools1); + const hash2 = cache.hashTools(tools2); + + expect(hash1).toBe(hash2); + }); + + it("should generate different hash for different tools", () => { + const tools1 = ["tool1", "tool2"]; + const tools2 = ["tool1", "tool3"]; + const hash1 = cache.hashTools(tools1); + const hash2 = cache.hashTools(tools2); + + expect(hash1).not.toBe(hash2); + }); + + it("should handle empty array", () => { + const hash = cache.hashTools([]); + expect(hash).toBeDefined(); + expect(typeof hash).toBe("string"); + }); + }); + + describe("hasChanged", () => { + it("should return true when no cache exists", () => { + const tools = ["tool1", "tool2"]; + const changed = cache.hasChanged("server-uuid-1", tools); + + expect(changed).toBe(true); + }); + + it("should return false when tools are unchanged", () => { + const tools = ["tool1", "tool2"]; + const serverUuid = "server-uuid-1"; + + cache.update(serverUuid, tools); + const changed = cache.hasChanged(serverUuid, tools); + + expect(changed).toBe(false); + }); + + it("should return true when tools have changed", () => { + const serverUuid = "server-uuid-1"; + const tools1 = ["tool1", "tool2"]; + const tools2 = ["tool1", "tool2", "tool3"]; + + cache.update(serverUuid, tools1); + const changed = cache.hasChanged(serverUuid, tools2); + + expect(changed).toBe(true); + }); + + it("should return false when tools are reordered but same", () => { + const serverUuid = "server-uuid-1"; + const tools1 = ["tool1", "tool2", "tool3"]; + const tools2 = ["tool3", "tool1", "tool2"]; + + cache.update(serverUuid, tools1); + const changed = cache.hasChanged(serverUuid, tools2); + + expect(changed).toBe(false); + }); + }); + + describe("shouldSync", () => { + it("should return true and update cache on first call", () => { + const tools = ["tool1", "tool2"]; + const serverUuid = "server-uuid-1"; + + const shouldSync = cache.shouldSync(serverUuid, tools); + + expect(shouldSync).toBe(true); + expect(cache.hasChanged(serverUuid, tools)).toBe(false); + }); + + it("should return false on second call with same tools", () => { + const tools = ["tool1", "tool2"]; + const serverUuid = "server-uuid-1"; + + cache.shouldSync(serverUuid, tools); + const shouldSync = cache.shouldSync(serverUuid, tools); + + expect(shouldSync).toBe(false); + }); + + it("should return true when tools change", () => { + const serverUuid = "server-uuid-1"; + const tools1 = ["tool1", "tool2"]; + const tools2 = ["tool1", "tool2", "tool3"]; + + cache.shouldSync(serverUuid, tools1); + const shouldSync = cache.shouldSync(serverUuid, tools2); + + expect(shouldSync).toBe(true); + }); + + it("should handle multiple servers independently", () => { + const tools1 = ["tool1", "tool2"]; + const tools2 = ["tool3", "tool4"]; + + const shouldSync1 = cache.shouldSync("server-1", tools1); + const shouldSync2 = cache.shouldSync("server-2", tools2); + + expect(shouldSync1).toBe(true); + expect(shouldSync2).toBe(true); + + // Second calls should not need sync + expect(cache.shouldSync("server-1", tools1)).toBe(false); + expect(cache.shouldSync("server-2", tools2)).toBe(false); + }); + }); + + describe("clear", () => { + it("should clear specific server cache", () => { + const tools = ["tool1", "tool2"]; + cache.update("server-1", tools); + cache.update("server-2", tools); + + cache.clear("server-1"); + + expect(cache.hasChanged("server-1", tools)).toBe(true); + expect(cache.hasChanged("server-2", tools)).toBe(false); + }); + + it("should clear all cache when no uuid provided", () => { + const tools = ["tool1", "tool2"]; + cache.update("server-1", tools); + cache.update("server-2", tools); + + cache.clear(); + + expect(cache.hasChanged("server-1", tools)).toBe(true); + expect(cache.hasChanged("server-2", tools)).toBe(true); + }); + }); + + describe("getStats", () => { + it("should return empty stats initially", () => { + const stats = cache.getStats(); + + expect(stats.size).toBe(0); + expect(stats.servers).toEqual([]); + }); + + it("should return correct stats after updates", () => { + cache.update("server-1", ["tool1"]); + cache.update("server-2", ["tool2"]); + + const stats = cache.getStats(); + + expect(stats.size).toBe(2); + expect(stats.servers).toContain("server-1"); + expect(stats.servers).toContain("server-2"); + }); + }); + + describe("real-world scenarios", () => { + it("should handle tool removal correctly", () => { + const serverUuid = "server-uuid-1"; + const toolsInitial = ["tool1", "tool2", "tool3"]; + const toolsAfterRemoval = ["tool1", "tool2"]; + + // First sync + expect(cache.shouldSync(serverUuid, toolsInitial)).toBe(true); + + // Tools removed + expect(cache.shouldSync(serverUuid, toolsAfterRemoval)).toBe(true); + + // No further changes + expect(cache.shouldSync(serverUuid, toolsAfterRemoval)).toBe(false); + }); + + it("should handle all tools removed (empty array)", () => { + const serverUuid = "server-uuid-1"; + const toolsInitial = ["tool1", "tool2"]; + const toolsEmpty: string[] = []; + + // First sync with tools + expect(cache.shouldSync(serverUuid, toolsInitial)).toBe(true); + + // All tools removed + expect(cache.shouldSync(serverUuid, toolsEmpty)).toBe(true); + + // Still empty + expect(cache.shouldSync(serverUuid, toolsEmpty)).toBe(false); + }); + + it("should handle tool addition correctly", () => { + const serverUuid = "server-uuid-1"; + const toolsInitial = ["tool1"]; + const toolsAfterAddition = ["tool1", "tool2", "tool3"]; + + // First sync + expect(cache.shouldSync(serverUuid, toolsInitial)).toBe(true); + + // Tools added + expect(cache.shouldSync(serverUuid, toolsAfterAddition)).toBe(true); + + // No further changes + expect(cache.shouldSync(serverUuid, toolsAfterAddition)).toBe(false); + }); + + it("should handle rapid reconnections efficiently", () => { + const serverUuid = "server-uuid-1"; + const tools = ["tool1", "tool2", "tool3"]; + + // Simulate 100 rapid reconnections with same tools + let syncCount = 0; + for (let i = 0; i < 100; i++) { + if (cache.shouldSync(serverUuid, tools)) { + syncCount++; + } + } + + // Only the first call should trigger sync + expect(syncCount).toBe(1); + }); + }); +}); + + + + diff --git a/apps/backend/src/lib/metamcp/tools-sync-cache.ts b/apps/backend/src/lib/metamcp/tools-sync-cache.ts new file mode 100644 index 00000000..fdb99587 --- /dev/null +++ b/apps/backend/src/lib/metamcp/tools-sync-cache.ts @@ -0,0 +1,81 @@ +import crypto from "crypto"; + +/** + * Simple in-memory cache for tool synchronization + * Tracks the hash of tools per MCP server to avoid unnecessary DB operations + */ +export class ToolsSyncCache { + private cache: Map = new Map(); + + /** + * Generate a hash from tool names + * Only tool names are used since they uniquely identify tools per server + */ + hashTools(toolNames: string[]): string { + // Sort to ensure consistent hash regardless of order + const sorted = [...toolNames].sort(); + const joined = sorted.join("|"); + return crypto.createHash("sha256").update(joined).digest("hex"); + } + + /** + * Check if tools have changed since last sync + * @returns true if tools changed or no cache exists, false if unchanged + */ + hasChanged(mcpServerUuid: string, toolNames: string[]): boolean { + const currentHash = this.hashTools(toolNames); + const cachedHash = this.cache.get(mcpServerUuid); + + return cachedHash !== currentHash; + } + + /** + * Update the cache with current tool state + */ + update(mcpServerUuid: string, toolNames: string[]): void { + const hash = this.hashTools(toolNames); + this.cache.set(mcpServerUuid, hash); + } + + /** + * Check if sync is needed and update cache if it is + * @returns true if sync needed, false if cache hit + */ + shouldSync(mcpServerUuid: string, toolNames: string[]): boolean { + const needsSync = this.hasChanged(mcpServerUuid, toolNames); + + if (needsSync) { + this.update(mcpServerUuid, toolNames); + } + + return needsSync; + } + + /** + * Clear cache for specific server or entire cache + */ + clear(mcpServerUuid?: string): void { + if (mcpServerUuid) { + this.cache.delete(mcpServerUuid); + } else { + this.cache.clear(); + } + } + + /** + * Get cache statistics + */ + getStats(): { + size: number; + servers: string[]; + } { + return { + size: this.cache.size, + servers: Array.from(this.cache.keys()), + }; + } +} + +// Singleton instance +export const toolsSyncCache = new ToolsSyncCache(); + diff --git a/apps/backend/src/trpc/tools.impl.ts b/apps/backend/src/trpc/tools.impl.ts index 9f8f0822..ce0448af 100644 --- a/apps/backend/src/trpc/tools.impl.ts +++ b/apps/backend/src/trpc/tools.impl.ts @@ -8,6 +8,7 @@ import { z } from "zod"; import { toolsRepository } from "../db/repositories"; import { ToolsSerializer } from "../db/serializers"; +import { toolsSyncCache } from "../lib/metamcp/tools-sync-cache"; export const toolsImplementations = { getByMcpServerUuid: async ( @@ -64,4 +65,61 @@ export const toolsImplementations = { }; } }, + + + /** + * Smart sync with hash-check and cleanup + * Only syncs if tools have actually changed (performance optimized) + */ + sync: async ( + input: z.infer, + ): Promise> => { + try { + if (!input.tools || input.tools.length === 0) { + return { + success: true as const, + count: 0, + message: "No tools to sync", + }; + } + + // Check if tools changed using hash + const toolNames = input.tools.map((tool) => tool.name); + const hasChanged = toolsSyncCache.hasChanged(input.mcpServerUuid, toolNames); + + if (hasChanged) { + // Update cache + toolsSyncCache.update(input.mcpServerUuid, toolNames); + + // Perform sync with cleanup + const { upserted, deleted } = await toolsRepository.syncTools({ + tools: input.tools, + mcpServerUuid: input.mcpServerUuid, + }); + + const message = deleted.length > 0 + ? `Successfully synced ${upserted.length} tools (removed ${deleted.length} obsolete)` + : `Successfully synced ${upserted.length} tools`; + + return { + success: true as const, + count: upserted.length, + message, + }; + } else { + return { + success: true as const, + count: input.tools.length, + message: "Tools unchanged, skipped sync", + }; + } + } catch (error) { + console.error("Error syncing tools to database:", error); + return { + success: false as const, + count: 0, + error: error instanceof Error ? error.message : "Internal server error", + }; + } + }, }; diff --git a/apps/backend/vitest.config.ts b/apps/backend/vitest.config.ts new file mode 100644 index 00000000..50981aeb --- /dev/null +++ b/apps/backend/vitest.config.ts @@ -0,0 +1,23 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globals: true, + environment: "node", + coverage: { + provider: "v8", + reporter: ["text", "json", "html"], + exclude: [ + "node_modules/", + "dist/", + "**/*.config.ts", + "**/*.spec.ts", + "**/*.test.ts", + ], + }, + }, +}); + + + + diff --git a/apps/frontend/app/[locale]/(sidebar)/mcp-servers/[uuid]/components/tool-management.tsx b/apps/frontend/app/[locale]/(sidebar)/mcp-servers/[uuid]/components/tool-management.tsx index 3e73450e..c31f5ed8 100644 --- a/apps/frontend/app/[locale]/(sidebar)/mcp-servers/[uuid]/components/tool-management.tsx +++ b/apps/frontend/app/[locale]/(sidebar)/mcp-servers/[uuid]/components/tool-management.tsx @@ -63,8 +63,8 @@ export function ToolManagement({ const dbTools: Tool[] = dbToolsResponse?.success ? dbToolsResponse.data : []; - // Save tools to database mutation - const saveToolsMutation = trpc.frontend.tools.create.useMutation({ + // Save tools to database mutation (with cleanup) + const saveToolsMutation = trpc.frontend.tools.sync.useMutation({ onSuccess: (response) => { if (response.success) { const foundCount = mcpTools.length; diff --git a/packages/trpc/src/routers/frontend/tools.ts b/packages/trpc/src/routers/frontend/tools.ts index e5c5069d..1e571a36 100644 --- a/packages/trpc/src/routers/frontend/tools.ts +++ b/packages/trpc/src/routers/frontend/tools.ts @@ -9,6 +9,7 @@ export const createToolsRouter = < TImplementations extends { getByMcpServerUuid: (input: any) => Promise; create: (input: any) => Promise; + sync: (input: any) => Promise; }, >( implementations: TImplementations, @@ -21,11 +22,18 @@ export const createToolsRouter = < return implementations.getByMcpServerUuid(input); }), - // Protected: Save tools to database + // Protected: Save tools to database (upsert only, no cleanup) create: protectedProcedure .input(CreateToolRequestSchema) .mutation(async ({ input }) => { return implementations.create(input); }), + + // Protected: Sync tools with cleanup (removes obsolete tools) + sync: protectedProcedure + .input(CreateToolRequestSchema) + .mutation(async ({ input }) => { + return implementations.sync(input); + }), }); }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b17b1259..c50c8016 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,6 +102,9 @@ importers: '@types/shell-quote': specifier: ^1.7.5 version: 1.7.5 + '@vitest/coverage-v8': + specifier: ^4.0.0 + version: 4.0.14(vitest@4.0.14(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3)) dotenv-cli: specifier: ^8.0.0 version: 8.0.0 @@ -120,6 +123,9 @@ importers: typescript: specifier: ^5.0.0 version: 5.8.2 + vitest: + specifier: ^4.0.0 + version: 4.0.14(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3) apps/frontend: dependencies: @@ -397,10 +403,31 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.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/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.27.6': resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@better-auth/utils@0.2.5': resolution: {integrity: sha512-uI2+/8h/zVsH8RrYdG8eUErbuGBk16rZKQfz8CjxQOyCE6v7BqFYEbFwvOkvl1KbUdxhqOnXp78+uE5h8qVEgQ==} @@ -929,9 +956,15 @@ packages: '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@levischuck/tiny-cbor@0.2.11': resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==} @@ -1608,6 +1641,9 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} @@ -1757,6 +1793,9 @@ packages: '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -1766,6 +1805,9 @@ packages: '@types/cross-spawn@6.0.6': resolution: {integrity: sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} @@ -1885,6 +1927,44 @@ packages: resolution: {integrity: sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitest/coverage-v8@4.0.14': + resolution: {integrity: sha512-EYHLqN/BY6b47qHH7gtMxAg++saoGmsjWmAq9MlXxAz4M0NcHh9iOyKhBZyU4yxZqOd8Xnqp80/5saeitz4Cng==} + peerDependencies: + '@vitest/browser': 4.0.14 + vitest: 4.0.14 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@4.0.14': + resolution: {integrity: sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==} + + '@vitest/mocker@4.0.14': + resolution: {integrity: sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.14': + resolution: {integrity: sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ==} + + '@vitest/runner@4.0.14': + resolution: {integrity: sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw==} + + '@vitest/snapshot@4.0.14': + resolution: {integrity: sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag==} + + '@vitest/spy@4.0.14': + resolution: {integrity: sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg==} + + '@vitest/utils@4.0.14': + resolution: {integrity: sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw==} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -1967,6 +2047,13 @@ packages: resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} engines: {node: '>=12.0.0'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-v8-to-istanbul@0.3.8: + resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==} + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -2038,6 +2125,10 @@ packages: caniuse-lite@1.0.30001713: resolution: {integrity: sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==} + chai@6.2.1: + resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2343,6 +2434,9 @@ packages: resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -2475,6 +2569,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -2495,6 +2592,10 @@ packages: resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} engines: {node: ^18.19.0 || >=20.5.0} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + express-rate-limit@7.5.0: resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} engines: {node: '>= 16'} @@ -2539,6 +2640,15 @@ packages: picomatch: optional: true + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -2704,6 +2814,9 @@ packages: highlightjs-vue@1.0.0: resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -2886,6 +2999,22 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} @@ -2911,6 +3040,9 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -3045,6 +3177,16 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.1: + resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -3191,6 +3333,9 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -3303,6 +3448,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} @@ -3627,6 +3776,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -3666,6 +3818,9 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -3674,6 +3829,9 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} @@ -3777,6 +3935,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} @@ -3784,6 +3945,14 @@ packages: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -3961,6 +4130,80 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vite@7.2.6: + resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.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 + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.0.14: + resolution: {integrity: sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.14 + '@vitest/browser-preview': 4.0.14 + '@vitest/browser-webdriverio': 4.0.14 + '@vitest/ui': 4.0.14 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -3988,6 +4231,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -4054,8 +4302,23 @@ snapshots: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + '@babel/runtime@7.27.6': {} + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bcoe/v8-coverage@1.0.2': {} + '@better-auth/utils@0.2.5': dependencies: typescript: 5.8.2 @@ -4413,11 +4676,18 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@levischuck/tiny-cbor@0.2.11': {} '@modelcontextprotocol/sdk@1.16.0': @@ -5057,6 +5327,8 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@standard-schema/spec@1.0.0': {} + '@standard-schema/utils@0.3.0': {} '@swc/helpers@0.5.15': @@ -5185,6 +5457,11 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 20.19.1 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + '@types/connect@3.4.38': dependencies: '@types/node': 20.19.1 @@ -5197,6 +5474,8 @@ snapshots: dependencies: '@types/node': 22.15.3 + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.7': {} '@types/estree@1.0.8': {} @@ -5362,6 +5641,62 @@ snapshots: '@typescript-eslint/types': 8.33.0 eslint-visitor-keys: 4.2.0 + '@vitest/coverage-v8@4.0.14(vitest@4.0.14(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3))': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.14 + ast-v8-to-istanbul: 0.3.8 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magicast: 0.5.1 + obug: 2.1.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.14(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@4.0.14': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.14 + '@vitest/utils': 4.0.14 + chai: 6.2.1 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.14(vite@7.2.6(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3))': + dependencies: + '@vitest/spy': 4.0.14 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.6(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3) + + '@vitest/pretty-format@4.0.14': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.14': + dependencies: + '@vitest/utils': 4.0.14 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.14': + dependencies: + '@vitest/pretty-format': 4.0.14 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.14': {} + + '@vitest/utils@4.0.14': + dependencies: + '@vitest/pretty-format': 4.0.14 + tinyrainbow: 3.0.3 + accepts@2.0.0: dependencies: mime-types: 3.0.1 @@ -5475,6 +5810,14 @@ snapshots: pvutils: 1.1.3 tslib: 2.8.1 + assertion-error@2.0.1: {} + + ast-v8-to-istanbul@0.3.8: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 9.0.1 + async-function@1.0.0: {} available-typed-arrays@1.0.7: @@ -5568,6 +5911,8 @@ snapshots: caniuse-lite@1.0.30001713: {} + chai@6.2.1: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -5829,6 +6174,8 @@ snapshots: iterator.prototype: 1.1.5 safe-array-concat: 1.1.3 + es-module-lexer@1.7.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -6038,6 +6385,10 @@ snapshots: estraverse@5.3.0: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + esutils@2.0.3: {} etag@1.8.1: {} @@ -6063,6 +6414,8 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.1 + expect-type@1.2.2: {} + express-rate-limit@7.5.0(express@5.1.0): dependencies: express: 5.1.0 @@ -6135,6 +6488,10 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -6312,6 +6669,8 @@ snapshots: highlightjs-vue@1.0.0: {} + html-escaper@2.0.2: {} + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -6484,6 +6843,27 @@ snapshots: isexe@2.0.0: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + debug: 4.4.1 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 @@ -6509,6 +6889,8 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -6617,6 +6999,20 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.1: + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + math-intrinsics@1.1.0: {} media-typer@1.1.0: {} @@ -6753,6 +7149,8 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + obug@2.1.1: {} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -6861,6 +7259,8 @@ snapshots: picomatch@4.0.2: {} + picomatch@4.0.3: {} + pirates@4.0.7: {} pkce-challenge@5.0.0: {} @@ -7249,6 +7649,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@4.1.0: {} simple-swizzle@0.2.2: @@ -7285,10 +7687,14 @@ snapshots: split2@4.2.0: {} + stackback@0.0.2: {} + statuses@2.0.1: {} statuses@2.0.2: {} + std-env@3.10.0: {} + stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 @@ -7416,6 +7822,8 @@ snapshots: dependencies: any-promise: 1.3.0 + tinybench@2.9.0: {} + tinyexec@0.3.2: {} tinyglobby@0.2.14: @@ -7423,6 +7831,13 @@ snapshots: fdir: 6.4.6(picomatch@4.0.2) picomatch: 4.0.2 + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinyrainbow@3.0.3: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -7604,6 +8019,58 @@ snapshots: vary@1.1.2: {} + vite@7.2.6(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3): + dependencies: + esbuild: 0.25.5 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.44.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.1 + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.30.1 + tsx: 4.20.3 + + vitest@4.0.14(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3): + dependencies: + '@vitest/expect': 4.0.14 + '@vitest/mocker': 4.0.14(vite@7.2.6(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3)) + '@vitest/pretty-format': 4.0.14 + '@vitest/runner': 4.0.14 + '@vitest/snapshot': 4.0.14 + '@vitest/spy': 4.0.14 + '@vitest/utils': 4.0.14 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.2.6(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.19.1 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + webidl-conversions@4.0.2: {} whatwg-url@7.1.0: @@ -7657,6 +8124,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} wrap-ansi@7.0.0: