diff --git a/backend/src/api/controllers/result.ts b/backend/src/api/controllers/result.ts index 4e7cacdaaf2b..36115da1c202 100644 --- a/backend/src/api/controllers/result.ts +++ b/backend/src/api/controllers/result.ts @@ -64,6 +64,7 @@ import { MonkeyRequest } from "../types"; import { getFunbox, checkCompatibility } from "@monkeytype/funbox"; import { tryCatch } from "@monkeytype/util/trycatch"; import { getCachedConfiguration } from "../../init/configuration"; +import { allTimeLeaderboardCache } from "../../utils/all-time-leaderboard-cache"; try { if (!anticheatImplemented()) throw new Error("undefined"); @@ -534,6 +535,13 @@ export async function addResult( }, dailyLeaderboardsConfig, ); + try { + allTimeLeaderboardCache.clear(); + console.log("All-time leaderboard cache cleared"); + } catch (error) { + console.warn("Cache clear failed (non-critical):", error); + } + if ( dailyLeaderboardRank >= 1 && dailyLeaderboardRank <= 10 && diff --git a/backend/src/api/controllers/user.ts b/backend/src/api/controllers/user.ts index 964602852f10..7ecb862a94a9 100644 --- a/backend/src/api/controllers/user.ts +++ b/backend/src/api/controllers/user.ts @@ -4,6 +4,7 @@ import MonkeyError, { isFirebaseError, } from "../../utils/error"; import { MonkeyResponse } from "../../utils/monkey-response"; +import { getCached, setCached, invalidateUserCache } from "../../utils/cache"; import * as DiscordUtils from "../../utils/discord"; import { buildAgentLog, @@ -914,6 +915,10 @@ export async function getProfile( ): Promise { const { uidOrName } = req.params; + const cacheKey = `user:profile:${uidOrName}`; + const cached = await getCached(cacheKey); + if (cached !== null) return cached; + const user = req.query.isUid ? await UserDAL.getUser(uidOrName, "get user profile") : await UserDAL.getUserByName(uidOrName, "get user profile"); @@ -987,7 +992,11 @@ export async function getProfile( }; if (banned) { - return new MonkeyResponse("Profile retrived: banned user", baseProfile); + await setCached( + cacheKey, + new MonkeyResponse("Profile retrieved: banned user", baseProfile), + ); + return new MonkeyResponse("Profile retrieved: banned user", baseProfile); } const allTimeLbs = await getAllTimeLbs(user.uid); @@ -1005,6 +1014,10 @@ export async function getProfile( } else { delete profileData.testActivity; } + await setCached( + cacheKey, + new MonkeyResponse("Profile retrieved", profileData), + ); return new MonkeyResponse("Profile retrieved", profileData); } @@ -1050,6 +1063,7 @@ export async function updateProfile( }; await UserDAL.updateProfile(uid, profileDetailsUpdates, user.inventory); + await invalidateUserCache(uid); return new MonkeyResponse("Profile updated", profileDetailsUpdates); } diff --git a/backend/src/credentials/.gitkeep b/backend/src/credentials/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/backend/src/dal/leaderboards.ts b/backend/src/dal/leaderboards.ts index f2a48658f6af..ec4a020e833a 100644 --- a/backend/src/dal/leaderboards.ts +++ b/backend/src/dal/leaderboards.ts @@ -15,6 +15,8 @@ import { DBUser, getUsersCollection } from "./user"; import MonkeyError from "../utils/error"; import { aggregateWithAcceptedConnections } from "./connections"; +import { allTimeLeaderboardCache } from "../utils/all-time-leaderboard-cache"; + export type DBLeaderboardEntry = LeaderboardEntry & { _id: ObjectId; }; @@ -46,6 +48,14 @@ export async function get( throw new MonkeyError(500, "Invalid page or pageSize"); } + if (page === 0 && pageSize === 50 && uid === undefined) { + const cached = allTimeLeaderboardCache.get({ mode, language }); + if (cached) { + console.log("✅ Cache HIT - leaderboards"); + return cached.data as DBLeaderboardEntry[]; + } + } + const skip = page * pageSize; const limit = pageSize; @@ -83,12 +93,23 @@ export async function get( leaderboard = leaderboard.map((it) => omit(it, ["isPremium"])); } + if (page === 0 && pageSize === 50 && uid === undefined) { + try { + allTimeLeaderboardCache.set( + { mode, language }, + leaderboard, + await getCount(mode, mode2, language), + ); + console.log(" Cache SET - leaderboards"); + } catch (error) { + console.warn("Cache set failed:", error); + } + } + return leaderboard; } catch (e) { - // oxlint-disable-next-line no-unsafe-member-access - if (e.error === 175) { + if ((e as unknown as { error: number }).error === 175) { //QueryPlanKilled, collection was removed during the query - return false; } throw e; } @@ -162,10 +183,8 @@ export async function getRank( return results[0] ?? null; } } catch (e) { - // oxlint-disable-next-line no-unsafe-member-access - if (e.error === 175) { + if ((e as unknown as { error: number }).error === 175) { //QueryPlanKilled, collection was removed during the query - return false; } throw e; } @@ -393,8 +412,8 @@ async function createIndex( Logger.warning(`Index ${key} not matching, dropping and recreating...`); const existingIndex = (await getUsersCollection().listIndexes().toArray()) - // oxlint-disable-next-line no-unsafe-member-access - .map((it) => it.name as string) + + .map((it: unknown) => (it as { name: string }).name) .find((it) => it.startsWith(key)); if (existingIndex !== undefined && existingIndex !== null) { diff --git a/backend/src/utils/all-time-leaderboard-cache.ts b/backend/src/utils/all-time-leaderboard-cache.ts new file mode 100644 index 000000000000..436140455b5a --- /dev/null +++ b/backend/src/utils/all-time-leaderboard-cache.ts @@ -0,0 +1,40 @@ +type AllTimeCacheKey = { + mode: string; + language: string; +}; + +type CacheEntry = { + data: unknown[]; + count: number; + timestamp: number; +}; + +class AllTimeLeaderboardCache { + private cache = new Map(); + private readonly TTL = 900_000; // == 15 minutes of TTL + + private getKey({ mode, language }: AllTimeCacheKey): string { + return `alltime-lb:${mode}:${language}`; + } + + get(key: AllTimeCacheKey): CacheEntry | null { + const cacheKey = this.getKey(key); + const entry = this.cache.get(cacheKey); + + if (!entry || Date.now() - entry.timestamp > this.TTL) { + if (entry) this.cache.delete(cacheKey); + return null; + } + return entry; + } + + set(key: AllTimeCacheKey, data: unknown[], count: number): void { + this.cache.set(this.getKey(key), { data, count, timestamp: Date.now() }); + } + + clear(): void { + this.cache.clear(); + } +} + +export const allTimeLeaderboardCache = new AllTimeLeaderboardCache(); diff --git a/backend/src/utils/cache.ts b/backend/src/utils/cache.ts new file mode 100644 index 000000000000..3245b8921e83 --- /dev/null +++ b/backend/src/utils/cache.ts @@ -0,0 +1,42 @@ +import { getConnection } from "../init/redis"; + +const CACHE_PREFIX = "cache:"; +const TTL = 300; // == 5 minutes + +export async function getCached(key: string): Promise { + const redis = getConnection(); + if (!redis) return null; + + try { + const data = await redis.get(`${CACHE_PREFIX}${key}`); + if (data === null || data === undefined || data === "") return null; + return JSON.parse(data) as T; + } catch { + return null; + } +} + +export async function setCached(key: string, data: T): Promise { + const redis = getConnection(); + if (!redis) return; + + try { + await redis.setex(`${CACHE_PREFIX}${key}`, TTL, JSON.stringify(data)); + } catch (err) { + console.error("Cache set failed:", err); + } +} + +export async function invalidateUserCache(userId: string): Promise { + const redis = getConnection(); + if (!redis) return; + + try { + const keys = await redis.keys(`${CACHE_PREFIX}user:profile:${userId}*`); + if (keys.length > 0) { + await redis.del(keys); + } + } catch (err) { + console.error("Cache invalidation failed:", err); + } +} diff --git a/package.json b/package.json index 3a6f76666466..a7cc6acb3cee 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "oxlint": "1.40.0", "oxlint-tsgolint": "0.11.1", "prettier": "3.7.1", - "turbo": "2.5.6", + "turbo": "2.7.5", "vitest": "4.0.15" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4cafa9d41fa5..47207ac80ae4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: version: link:packages/release '@vitest/coverage-v8': specifier: 4.0.15 - version: 4.0.15(vitest@4.0.15(@types/node@20.5.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)) + version: 4.0.15(vitest@4.0.15(@types/node@20.5.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1)) conventional-changelog: specifier: 6.0.0 version: 6.0.0(conventional-commits-filter@5.0.0) @@ -48,11 +48,11 @@ importers: specifier: 3.7.1 version: 3.7.1 turbo: - specifier: 2.5.6 - version: 2.5.6 + specifier: 2.7.5 + version: 2.7.5 vitest: specifier: 4.0.15 - version: 4.0.15(@types/node@20.5.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + version: 4.0.15(@types/node@20.5.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) backend: dependencies: @@ -224,7 +224,7 @@ importers: version: 10.0.0 '@vitest/coverage-v8': specifier: 4.0.15 - version: 4.0.15(vitest@4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)) + version: 4.0.15(vitest@4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1)) concurrently: specifier: 8.2.2 version: 8.2.2 @@ -254,7 +254,7 @@ importers: version: 5.9.3 vitest: specifier: 4.0.15 - version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) frontend: dependencies: @@ -510,7 +510,7 @@ importers: version: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1)) vitest: specifier: 4.0.15 - version: 4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) + version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) packages/contracts: dependencies: @@ -547,7 +547,7 @@ importers: version: 5.9.3 vitest: specifier: 4.0.15 - version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) packages/funbox: dependencies: @@ -581,7 +581,7 @@ importers: version: 5.9.3 vitest: specifier: 4.0.15 - version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) packages/oxlint-config: {} @@ -636,7 +636,7 @@ importers: version: 5.9.3 vitest: specifier: 4.0.15 - version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) packages/tsup-config: dependencies: @@ -684,7 +684,7 @@ importers: version: 5.9.3 vitest: specifier: 4.0.15 - version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) zod: specifier: 3.23.8 version: 3.23.8 @@ -6589,6 +6589,7 @@ packages: json-ptr@3.1.1: resolution: {integrity: sha512-SiSJQ805W1sDUCD1+/t1/1BIrveq2Fe9HJqENxZmMCILmrPI7WhS/pePpIOx85v6/H2z1Vy7AI08GV2TzfXocg==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. json-schema-to-ts@2.7.2: resolution: {integrity: sha512-R1JfqKqbBR4qE8UyBR56Ms30LL62/nlhoz+1UkfI/VE7p54Awu919FZ6ZUPG8zIa3XB65usPJgr1ONVncUGSaQ==} @@ -9126,6 +9127,7 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me tcp-port-used@1.0.2: resolution: {integrity: sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==} @@ -9150,11 +9152,6 @@ packages: engines: {node: '>=10'} hasBin: true - terser@5.44.1: - resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} - engines: {node: '>=10'} - hasBin: true - terser@5.46.0: resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} engines: {node: '>=10'} @@ -9381,38 +9378,38 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - turbo-darwin-64@2.5.6: - resolution: {integrity: sha512-3C1xEdo4aFwMJAPvtlPqz1Sw/+cddWIOmsalHFMrsqqydcptwBfu26WW2cDm3u93bUzMbBJ8k3zNKFqxJ9ei2A==} + turbo-darwin-64@2.7.5: + resolution: {integrity: sha512-nN3wfLLj4OES/7awYyyM7fkU8U8sAFxsXau2bYJwAWi6T09jd87DgHD8N31zXaJ7LcpyppHWPRI2Ov9MuZEwnQ==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.5.6: - resolution: {integrity: sha512-LyiG+rD7JhMfYwLqB6k3LZQtYn8CQQUePbpA8mF/hMLPAekXdJo1g0bUPw8RZLwQXUIU/3BU7tXENvhSGz5DPA==} + turbo-darwin-arm64@2.7.5: + resolution: {integrity: sha512-wCoDHMiTf3FgLAbZHDDx/unNNonSGhsF5AbbYODbxnpYyoKDpEYacUEPjZD895vDhNvYCH0Nnk24YsP4n/cD6g==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.5.6: - resolution: {integrity: sha512-GOcUTT0xiT/pSnHL4YD6Yr3HreUhU8pUcGqcI2ksIF9b2/r/kRHwGFcsHgpG3+vtZF/kwsP0MV8FTlTObxsYIA==} + turbo-linux-64@2.7.5: + resolution: {integrity: sha512-KKPvhOmJMmzWj/yjeO4LywkQ85vOJyhru7AZk/+c4B6OUh/odQ++SiIJBSbTG2lm1CuV5gV5vXZnf/2AMlu3Zg==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.5.6: - resolution: {integrity: sha512-10Tm15bruJEA3m0V7iZcnQBpObGBcOgUcO+sY7/2vk1bweW34LMhkWi8svjV9iDF68+KJDThnYDlYE/bc7/zzQ==} + turbo-linux-arm64@2.7.5: + resolution: {integrity: sha512-8PIva4L6BQhiPikUTds9lSFSHXVDAsEvV6QUlgwPsXrtXVQMVi6Sv9p+IxtlWQFvGkdYJUgX9GnK2rC030Xcmw==} cpu: [arm64] os: [linux] - turbo-windows-64@2.5.6: - resolution: {integrity: sha512-FyRsVpgaj76It0ludwZsNN40ytHN+17E4PFJyeliBEbxrGTc5BexlXVpufB7XlAaoaZVxbS6KT8RofLfDRyEPg==} + turbo-windows-64@2.7.5: + resolution: {integrity: sha512-rupskv/mkIUgQXzX/wUiK00mKMorQcK8yzhGFha/D5lm05FEnLx8dsip6rWzMcVpvh+4GUMA56PgtnOgpel2AA==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.5.6: - resolution: {integrity: sha512-j/tWu8cMeQ7HPpKri6jvKtyXg9K1gRyhdK4tKrrchH8GNHscPX/F71zax58yYtLRWTiK04zNzPcUJuoS0+v/+Q==} + turbo-windows-arm64@2.7.5: + resolution: {integrity: sha512-G377Gxn6P42RnCzfMyDvsqQV7j69kVHKlhz9J4RhtJOB5+DyY4yYh/w0oTIxZQ4JRMmhjwLu3w9zncMoQ6nNDw==} cpu: [arm64] os: [win32] - turbo@2.5.6: - resolution: {integrity: sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w==} + turbo@2.7.5: + resolution: {integrity: sha512-7Imdmg37joOloTnj+DPrab9hIaQcDdJ5RwSzcauo/wMOSAgO+A/I/8b3hsGGs6PWQz70m/jkPgdqWsfNKtwwDQ==} hasBin: true tweetnacl@0.14.5: @@ -13468,7 +13465,7 @@ snapshots: '@typescript-eslint/types': 8.52.0 eslint-visitor-keys: 4.2.1 - '@vitest/coverage-v8@4.0.15(vitest@4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))': + '@vitest/coverage-v8@4.0.15(vitest@4.0.15(@types/node@20.5.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.15 @@ -13481,24 +13478,7 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) - transitivePeerDependencies: - - supports-color - - '@vitest/coverage-v8@4.0.15(vitest@4.0.15(@types/node@20.5.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))': - dependencies: - '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.0.15 - 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.15(@types/node@20.5.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + vitest: 4.0.15(@types/node@20.5.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -13515,7 +13495,7 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) + vitest: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -13528,21 +13508,13 @@ snapshots: chai: 6.2.1 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.15(vite@7.1.12(@types/node@20.5.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))': + '@vitest/mocker@4.0.15(vite@7.1.12(@types/node@20.5.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1))': dependencies: '@vitest/spy': 4.0.15 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.1.12(@types/node@20.5.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) - - '@vitest/mocker@4.0.15(vite@7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))': - dependencies: - '@vitest/spy': 4.0.15 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + vite: 7.1.12(@types/node@20.5.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) '@vitest/mocker@4.0.15(vite@7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1))': dependencies: @@ -20022,14 +19994,6 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 - terser@5.44.1: - dependencies: - '@jridgewell/source-map': 0.3.11 - acorn: 8.15.0 - commander: 2.20.3 - source-map-support: 0.5.21 - optional: true - terser@5.46.0: dependencies: '@jridgewell/source-map': 0.3.11 @@ -20255,32 +20219,32 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - turbo-darwin-64@2.5.6: + turbo-darwin-64@2.7.5: optional: true - turbo-darwin-arm64@2.5.6: + turbo-darwin-arm64@2.7.5: optional: true - turbo-linux-64@2.5.6: + turbo-linux-64@2.7.5: optional: true - turbo-linux-arm64@2.5.6: + turbo-linux-arm64@2.7.5: optional: true - turbo-windows-64@2.5.6: + turbo-windows-64@2.7.5: optional: true - turbo-windows-arm64@2.5.6: + turbo-windows-arm64@2.7.5: optional: true - turbo@2.5.6: + turbo@2.7.5: optionalDependencies: - turbo-darwin-64: 2.5.6 - turbo-darwin-arm64: 2.5.6 - turbo-linux-64: 2.5.6 - turbo-linux-arm64: 2.5.6 - turbo-windows-64: 2.5.6 - turbo-windows-arm64: 2.5.6 + turbo-darwin-64: 2.7.5 + turbo-darwin-arm64: 2.7.5 + turbo-linux-64: 2.7.5 + turbo-linux-arm64: 2.7.5 + turbo-windows-64: 2.7.5 + turbo-windows-arm64: 2.7.5 tweetnacl@0.14.5: {} @@ -20596,7 +20560,7 @@ snapshots: transitivePeerDependencies: - supports-color - vite@7.1.12(@types/node@20.5.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1): + vite@7.1.12(@types/node@20.5.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1): dependencies: esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) @@ -20610,25 +20574,7 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 sass: 1.70.0 - terser: 5.44.1 - tsx: 4.16.2 - yaml: 2.8.1 - - vite@7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1): - dependencies: - esbuild: 0.25.11 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.52.5 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 24.9.1 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.30.2 - sass: 1.70.0 - terser: 5.44.1 + terser: 5.46.0 tsx: 4.16.2 yaml: 2.8.1 @@ -20654,10 +20600,10 @@ snapshots: optionalDependencies: vite: 7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) - vitest@4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1): + vitest@4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1): dependencies: '@vitest/expect': 4.0.15 - '@vitest/mocker': 4.0.15(vite@7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)) + '@vitest/mocker': 4.0.15(vite@7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1)) '@vitest/pretty-format': 4.0.15 '@vitest/runner': 4.0.15 '@vitest/snapshot': 4.0.15 @@ -20674,7 +20620,7 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + vite: 7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.8.0 @@ -20694,10 +20640,10 @@ snapshots: - tsx - yaml - vitest@4.0.15(@types/node@20.5.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1): + vitest@4.0.15(@types/node@20.5.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1): dependencies: '@vitest/expect': 4.0.15 - '@vitest/mocker': 4.0.15(vite@7.1.12(@types/node@20.5.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)) + '@vitest/mocker': 4.0.15(vite@7.1.12(@types/node@20.5.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1)) '@vitest/pretty-format': 4.0.15 '@vitest/runner': 4.0.15 '@vitest/snapshot': 4.0.15 @@ -20714,7 +20660,7 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.1.12(@types/node@20.5.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1) + vite: 7.1.12(@types/node@20.5.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.5.1 @@ -20733,45 +20679,6 @@ snapshots: - tsx - yaml - vitest@4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1): - dependencies: - '@vitest/expect': 4.0.15 - '@vitest/mocker': 4.0.15(vite@7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1)) - '@vitest/pretty-format': 4.0.15 - '@vitest/runner': 4.0.15 - '@vitest/snapshot': 4.0.15 - '@vitest/spy': 4.0.15 - '@vitest/utils': 4.0.15 - 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: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.70.0)(terser@5.46.0)(tsx@4.16.2)(yaml@2.8.1) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 24.9.1 - happy-dom: 20.0.10 - jsdom: 27.4.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - vlq@0.2.3: {} w3c-xmlserializer@5.0.0: