diff --git a/apps/web/package.json b/apps/web/package.json index a9b1f0aeab..027ed2c445 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -101,6 +101,7 @@ "@types/js-cookie": "3.0.6", "@types/js-yaml": "4.0.9", "@types/mdx": "2.0.13", + "@upstash/redis": "^1.38.0", "@vercel/functions": "3.4.6", "@vercel/otel": "2.1.2", "@workos-inc/node": "catalog:", diff --git a/apps/web/src/lib/ai-gateway/experiments/pick-variant.test.ts b/apps/web/src/lib/ai-gateway/experiments/pick-variant.test.ts index 20dce833db..8ec51627b2 100644 --- a/apps/web/src/lib/ai-gateway/experiments/pick-variant.test.ts +++ b/apps/web/src/lib/ai-gateway/experiments/pick-variant.test.ts @@ -20,7 +20,11 @@ const upstreamB = { internal_id: 'partner-checkpoint-b', base_url: 'https://partner.example.com/v1', }; -const redisIt = process.env.REDIS_URL ? it : it.skip; +const redisIt = + (process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN) || + (process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN) + ? it + : it.skip; beforeEach(async () => { await cleanupDbForTest(); diff --git a/apps/web/src/lib/redis.ts b/apps/web/src/lib/redis.ts index 6965e5b3ca..f8887aa7fd 100644 --- a/apps/web/src/lib/redis.ts +++ b/apps/web/src/lib/redis.ts @@ -1,28 +1,23 @@ -import { createClient } from 'redis'; +import { Redis } from '@upstash/redis'; import { captureException } from '@sentry/nextjs'; import type { RedisKey } from '@/lib/redis-keys'; -type RedisClient = ReturnType; type RedisOperation = 'get' | 'getdel' | 'set' | 'del'; -type RedisTimeoutPhase = 'connect' | 'command'; +type RedisConfig = { + url: string; + token: string; + source: 'upstash' | 'vercel-kv'; +}; -// TCP handshake + TLS negotiation can take a moment on a cold connection. -// Redis official docs recommend 1-3s for connect (redis.io/docs/latest/develop/clients). -const CONNECT_TIMEOUT_MS = 1_500; - -// Simple GET/SET commands complete in sub-millisecond; anything over 200ms -// means Redis is overloaded or unreachable and we should fail open. +// Simple GET/SET commands should still be fast over REST; anything over 200ms +// means Redis is overloaded or unreachable and callers should fail open. const COMMAND_TIMEOUT_MS = 200; -let client: RedisClient | null = null; -let connectPromise: Promise | null = null; +let client: Redis | null = null; class RedisTimeoutError extends Error { - constructor( - readonly redisTimeoutPhase: RedisTimeoutPhase, - readonly redisTimeoutMs: number - ) { - super(`Redis timeout (${redisTimeoutPhase})`); + constructor(readonly redisTimeoutMs: number) { + super('Redis timeout (command)'); this.name = 'RedisTimeoutError'; } } @@ -31,127 +26,124 @@ function captureRedisOperationException( err: unknown, operation: RedisOperation, key: RedisKey, - c: RedisClient + config: RedisConfig ) { - const timeoutPhase = err instanceof RedisTimeoutError ? err.redisTimeoutPhase : undefined; + const timeoutPhase = err instanceof RedisTimeoutError ? 'command' : undefined; captureException(err, { tags: { service: 'redis', + redis_transport: 'rest', + redis_config_source: config.source, operation, ...(timeoutPhase ? { redis_timeout_phase: timeoutPhase } : {}), }, extra: { key, - client_is_open: c.isOpen, - client_is_ready: c.isReady, redis_timeout_ms: err instanceof RedisTimeoutError ? err.redisTimeoutMs : undefined, }, }); } -function getOrCreateClient(): RedisClient | null { - if (!process.env.REDIS_URL) { - return null; +function getRedisConfig(): RedisConfig | null { + if (process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN) { + return { + url: process.env.UPSTASH_REDIS_REST_URL, + token: process.env.UPSTASH_REDIS_REST_TOKEN, + source: 'upstash', + }; } - if (!client) { - client = createClient({ - url: process.env.REDIS_URL, - socket: { connectTimeout: CONNECT_TIMEOUT_MS }, - }); - client.on('error', err => { - captureException(err, { tags: { service: 'redis' } }); - }); + + if (process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN) { + return { + url: process.env.KV_REST_API_URL, + token: process.env.KV_REST_API_TOKEN, + source: 'vercel-kv', + }; } - return client; + + return null; } -async function ensureConnected(c: RedisClient): Promise { - if (c.isReady) return c; - if (!connectPromise) { - connectPromise = c - .connect() - .catch(err => { - captureException(err, { tags: { service: 'redis', operation: 'connect' } }); - throw err; - }) - .finally(() => { - connectPromise = null; - }); +function getOrCreateClient(): { client: Redis; config: RedisConfig } | null { + const config = getRedisConfig(); + if (!config) { + return null; + } + if (!client) { + client = new Redis({ + url: config.url, + token: config.token, + automaticDeserialization: false, + retry: false, + }); } - await connectPromise; - return c; + return { client, config }; } -function withTimeout( - promise: Promise, - ms: number, - redisTimeoutPhase: RedisTimeoutPhase -): Promise { - let timer: ReturnType; +function withTimeout(promise: Promise, ms: number): Promise { + let timer: ReturnType | undefined; return Promise.race([ - promise.finally(() => clearTimeout(timer)), + promise.finally(() => { + if (timer) clearTimeout(timer); + }), new Promise((_, reject) => { - timer = setTimeout(() => reject(new RedisTimeoutError(redisTimeoutPhase, ms)), ms); + timer = setTimeout(() => reject(new RedisTimeoutError(ms)), ms); }), ]); } export async function redisGet(key: RedisKey): Promise { - const c = getOrCreateClient(); - if (!c) return null; + const redis = getOrCreateClient(); + if (!redis) return null; try { - await withTimeout(ensureConnected(c), CONNECT_TIMEOUT_MS, 'connect'); - return await withTimeout(c.get(key), COMMAND_TIMEOUT_MS, 'command'); + return await withTimeout(redis.client.get(key), COMMAND_TIMEOUT_MS); } catch (err) { - captureRedisOperationException(err, 'get', key, c); + captureRedisOperationException(err, 'get', key, redis.config); throw err; } } export async function redisGetDel(key: RedisKey): Promise { - const c = getOrCreateClient(); - if (!c) return null; + const redis = getOrCreateClient(); + if (!redis) return null; try { - await withTimeout(ensureConnected(c), CONNECT_TIMEOUT_MS, 'connect'); - return await withTimeout(c.getDel(key), COMMAND_TIMEOUT_MS, 'command'); + return await withTimeout(redis.client.getdel(key), COMMAND_TIMEOUT_MS); } catch (err) { - captureRedisOperationException(err, 'getdel', key, c); + captureRedisOperationException(err, 'getdel', key, redis.config); throw err; } } -/** Returns false if Redis is not configured (REDIS_URL unset). */ +/** Returns false if Redis REST env vars are not configured. */ export async function redisSet( key: RedisKey, value: string, ttlSeconds?: number ): Promise { - const c = getOrCreateClient(); - if (!c) return false; + const redis = getOrCreateClient(); + if (!redis) return false; try { - await withTimeout(ensureConnected(c), CONNECT_TIMEOUT_MS, 'connect'); if (ttlSeconds) { - await withTimeout(c.set(key, value, { EX: ttlSeconds }), COMMAND_TIMEOUT_MS, 'command'); + await withTimeout(redis.client.set(key, value, { ex: ttlSeconds }), COMMAND_TIMEOUT_MS); } else { - await withTimeout(c.set(key, value), COMMAND_TIMEOUT_MS, 'command'); + await withTimeout(redis.client.set(key, value), COMMAND_TIMEOUT_MS); } return true; } catch (err) { - captureRedisOperationException(err, 'set', key, c); + captureRedisOperationException(err, 'set', key, redis.config); throw err; } } -/** Returns false if Redis is not configured (REDIS_URL unset). */ +/** Returns false if Redis REST env vars are not configured. */ export async function redisDel(key: RedisKey): Promise { - const c = getOrCreateClient(); - if (!c) return false; + const redis = getOrCreateClient(); + if (!redis) return false; try { - await withTimeout(ensureConnected(c), CONNECT_TIMEOUT_MS, 'connect'); - await withTimeout(c.del(key), COMMAND_TIMEOUT_MS, 'command'); + await withTimeout(redis.client.del(key), COMMAND_TIMEOUT_MS); return true; } catch (err) { - captureRedisOperationException(err, 'del', key, c); + captureRedisOperationException(err, 'del', key, redis.config); throw err; } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1778723bab..8b9f9b3f6d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -709,6 +709,9 @@ importers: '@types/mdx': specifier: 2.0.13 version: 2.0.13 + '@upstash/redis': + specifier: ^1.38.0 + version: 1.38.0 '@vercel/functions': specifier: 3.4.6 version: 3.4.6(@aws-sdk/credential-provider-web-identity@3.972.43) @@ -771,7 +774,7 @@ importers: version: 14.25.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) emoji-mart: specifier: 5.6.0 version: 5.6.0 @@ -985,7 +988,7 @@ importers: version: link:../encryption drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) zod: specifier: 'catalog:' version: 4.4.3 @@ -1013,7 +1016,7 @@ importers: version: link:../kiloclaw-instance-tiers drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) pg: specifier: 8.20.0 version: 8.20.0 @@ -1204,7 +1207,7 @@ importers: version: 8.13.0 drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) stripe: specifier: 'catalog:' version: 19.3.1(@types/node@25.5.2) @@ -1272,7 +1275,7 @@ importers: version: 1.0.20 drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) hono: specifier: 4.12.18 version: 4.12.18 @@ -1300,7 +1303,7 @@ importers: version: link:../../packages/worker-utils drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) hono: specifier: 4.12.18 version: 4.12.18 @@ -1343,7 +1346,7 @@ importers: version: 8.0.3 drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) jsonwebtoken: specifier: 'catalog:' version: 9.0.3 @@ -1460,7 +1463,7 @@ importers: version: 11.17.0(typescript@5.9.3) drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) jsonwebtoken: specifier: 'catalog:' version: 9.0.3 @@ -1533,7 +1536,7 @@ importers: version: 11.17.0(typescript@5.9.3) drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) hono: specifier: 4.12.18 version: 4.12.18 @@ -1812,7 +1815,7 @@ importers: version: link:../../packages/worker-utils drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) hono: specifier: 4.12.18 version: 4.12.18 @@ -1864,7 +1867,7 @@ importers: version: 11.17.0(typescript@5.9.3) drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) hono: specifier: 4.12.18 version: 4.12.18 @@ -1959,7 +1962,7 @@ importers: version: 22.0.1 drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) zod: specifier: 'catalog:' version: 4.4.3 @@ -2076,7 +2079,7 @@ importers: version: 0.5.4 drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) hono: specifier: 4.12.18 version: 4.12.18 @@ -2174,7 +2177,7 @@ importers: version: 0.9.5 drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) hono: specifier: 4.12.18 version: 4.12.18 @@ -2223,7 +2226,7 @@ importers: version: 4.1.0 drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) jose: specifier: 'catalog:' version: 6.2.3 @@ -2257,7 +2260,7 @@ importers: version: link:../../packages/db drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) node-html-markdown: specifier: 2.0.0 version: 2.0.0 @@ -2347,7 +2350,7 @@ importers: version: link:../../packages/worker-utils drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) zod: specifier: 'catalog:' version: 4.4.3 @@ -2384,7 +2387,7 @@ importers: version: link:../../packages/worker-utils drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) expo-server-sdk: specifier: 6.1.0 version: 6.1.0(patch_hash=7850520582b5b394397b35d1ea195192fe78589d8a6a748fe15177b818c4ed0b) @@ -2427,7 +2430,7 @@ importers: version: link:../../packages/worker-utils drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) hono: specifier: 4.12.18 version: 4.12.18 @@ -2467,7 +2470,7 @@ importers: version: link:../../packages/worker-utils drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) workers-tagged-logger: specifier: 'catalog:' version: 1.0.0 @@ -2501,7 +2504,7 @@ importers: version: link:../../packages/worker-utils drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) zod: specifier: 'catalog:' version: 4.4.3 @@ -2538,7 +2541,7 @@ importers: version: 0.0.22 drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) hono: specifier: 4.12.18 version: 4.12.18 @@ -2651,7 +2654,7 @@ importers: version: 10.0.1 drizzle-orm: specifier: 0.45.2 - version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) + version: 0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0) hono: specifier: 4.12.18 version: 4.12.18 @@ -9093,6 +9096,9 @@ packages: cpu: [x64] os: [win32] + '@upstash/redis@1.38.0': + resolution: {integrity: sha512-wu+dZBptlLy0+MCUEoHmzrY/TnmgDey3+c7EbIGwrLqAvkP8yi5MWZHYGIFtAygmL4Bkz2TdFu+eU0vFPncIcg==} + '@vercel/functions@3.4.6': resolution: {integrity: sha512-ljfB7SceggUOFjagEmw3PBLSAGQK1NmBln4FV/Cg9cewfbdfJZS88LgC7LRfZ9GgG1kWimkwS/8SLHVtx6lhLA==} engines: {node: '>= 20'} @@ -15756,6 +15762,9 @@ packages: resolution: {integrity: sha512-X2wH19RAPZE3+ldGicOkoj/SIA83OIxcJ6Cuaw23hf8Xc6fQpvZXY0SftE2JgS0QhYLUG4uwodSI3R53keyh7w==} engines: {node: '>=14'} + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -18136,9 +18145,9 @@ snapshots: '@chromaui/rrweb-snapshot': 2.0.0-alpha.18-noAbsolute '@playwright/test': 1.58.2 '@segment/analytics-node': 2.1.3 - '@storybook/addon-essentials': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/addon-essentials': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) '@storybook/csf': 0.1.13 - '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) '@storybook/server-webpack5': 8.5.8(@swc/core@1.15.18)(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@5.9.3) storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) ts-dedent: 2.2.0 @@ -18164,9 +18173,9 @@ snapshots: '@chromaui/rrweb-snapshot': 2.0.0-alpha.18-noAbsolute '@playwright/test': 1.58.2 '@segment/analytics-node': 2.1.3 - '@storybook/addon-essentials': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/addon-essentials': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) '@storybook/csf': 0.1.13 - '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) '@storybook/server-webpack5': 8.5.8(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(typescript@5.9.3) storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) ts-dedent: 2.2.0 @@ -18259,7 +18268,7 @@ snapshots: cjs-module-lexer: 1.4.3 esbuild: 0.27.4 miniflare: 4.20260508.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@24.12.4)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) wrangler: 4.90.1(@cloudflare/workers-types@4.20260511.1)(bufferutil@4.1.0)(utf-8-validate@6.0.6) zod: 3.25.76 transitivePeerDependencies: @@ -23428,7 +23437,7 @@ snapshots: '@stitches/core@1.2.8': {} - '@storybook/addon-actions@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/addon-actions@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 @@ -23437,26 +23446,26 @@ snapshots: storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) uuid: 9.0.1 - '@storybook/addon-backgrounds@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/addon-backgrounds@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) ts-dedent: 2.2.0 - '@storybook/addon-controls@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/addon-controls@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: '@storybook/global': 5.0.0 dequal: 2.0.3 storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) ts-dedent: 2.2.0 - '@storybook/addon-docs@8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/addon-docs@8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: '@mdx-js/react': 3.1.1(@types/react@19.2.14)(react@19.2.6) - '@storybook/blocks': 8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/csf-plugin': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/react-dom-shim': 8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/blocks': 8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/csf-plugin': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/react-dom-shim': 8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) react: 19.2.6 react-dom: 19.2.4(react@19.2.6) storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) @@ -23477,23 +23486,23 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@storybook/addon-essentials@8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': - dependencies: - '@storybook/addon-actions': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/addon-backgrounds': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/addon-controls': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/addon-docs': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/addon-highlight': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/addon-measure': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/addon-outline': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/addon-toolbars': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/addon-viewport': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/addon-essentials@8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': + dependencies: + '@storybook/addon-actions': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/addon-backgrounds': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/addon-controls': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/addon-docs': 8.5.8(@types/react@19.2.14)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/addon-highlight': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/addon-measure': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/addon-outline': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/addon-toolbars': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/addon-viewport': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-highlight@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/addon-highlight@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: '@storybook/global': 5.0.0 storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) @@ -23505,13 +23514,13 @@ snapshots: optionalDependencies: react: 19.2.6 - '@storybook/addon-measure@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/addon-measure@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: '@storybook/global': 5.0.0 storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) tiny-invariant: 1.3.3 - '@storybook/addon-outline@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/addon-outline@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: '@storybook/global': 5.0.0 storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) @@ -23522,16 +23531,16 @@ snapshots: storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) ts-dedent: 2.2.0 - '@storybook/addon-toolbars@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/addon-toolbars@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) - '@storybook/addon-viewport@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/addon-viewport@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: memoizerific: 1.11.3 storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) - '@storybook/blocks@8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/blocks@8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: '@storybook/csf': 0.1.12 '@storybook/icons': 1.6.0(react-dom@19.2.4(react@19.2.6))(react@19.2.6) @@ -23543,7 +23552,7 @@ snapshots: '@storybook/builder-webpack5@8.5.8(@swc/core@1.15.18)(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@5.9.3)': dependencies: - '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) '@types/semver': 7.7.1 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 @@ -23579,7 +23588,7 @@ snapshots: '@storybook/builder-webpack5@8.5.8(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(typescript@5.9.3)': dependencies: - '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) '@types/semver': 7.7.1 browser-assert: 1.2.1 case-sensitive-paths-webpack-plugin: 2.4.0 @@ -23641,11 +23650,11 @@ snapshots: - uglify-js - webpack-cli - '@storybook/components@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/components@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) - '@storybook/core-webpack@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/core-webpack@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) ts-dedent: 2.2.0 @@ -23655,7 +23664,7 @@ snapshots: storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) ts-dedent: 2.2.0 - '@storybook/csf-plugin@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/csf-plugin@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) unplugin: 1.16.1 @@ -23680,7 +23689,7 @@ snapshots: react: 19.2.6 react-dom: 19.2.4(react@19.2.6) - '@storybook/manager-api@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/manager-api@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) @@ -23768,17 +23777,17 @@ snapshots: - uglify-js - webpack-cli - '@storybook/preset-server-webpack@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/preset-server-webpack@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: - '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/core-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) '@storybook/global': 5.0.0 - '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) safe-identifier: 0.4.2 storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) ts-dedent: 2.2.0 yaml-loader: 0.8.1 - '@storybook/preview-api@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/preview-api@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) @@ -23796,7 +23805,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@storybook/react-dom-shim@8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/react-dom-shim@8.5.8(react-dom@19.2.4(react@19.2.6))(react@19.2.6)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: react: 19.2.6 react-dom: 19.2.4(react@19.2.6) @@ -23827,8 +23836,8 @@ snapshots: '@storybook/server-webpack5@8.5.8(@swc/core@1.15.18)(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@5.9.3)': dependencies: '@storybook/builder-webpack5': 8.5.8(@swc/core@1.15.18)(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))(typescript@5.9.3) - '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) transitivePeerDependencies: - '@rspack/core' @@ -23841,8 +23850,8 @@ snapshots: '@storybook/server-webpack5@8.5.8(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(typescript@5.9.3)': dependencies: '@storybook/builder-webpack5': 8.5.8(esbuild@0.27.4)(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))(typescript@5.9.3) - '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/preset-server-webpack': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/server': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) transitivePeerDependencies: - '@rspack/core' @@ -23853,14 +23862,14 @@ snapshots: - webpack-cli optional: true - '@storybook/server@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/server@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: - '@storybook/components': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/components': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) '@storybook/csf': 0.1.12 '@storybook/global': 5.0.0 - '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/preview-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - '@storybook/theming': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@storybook/manager-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/preview-api': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) + '@storybook/theming': 8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4))) storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) ts-dedent: 2.2.0 yaml: 2.8.4 @@ -23895,7 +23904,7 @@ snapshots: - supports-color - ts-node - '@storybook/theming@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@storybook/theming@8.5.8(storybook@9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)))': dependencies: storybook: 9.1.20(bufferutil@4.1.0)(utf-8-validate@6.0.6)(vite@8.0.10(@types/node@25.5.2)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4)) @@ -24519,6 +24528,10 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true + '@upstash/redis@1.38.0': + dependencies: + uncrypto: 0.1.3 + '@vercel/functions@3.4.6(@aws-sdk/credential-provider-web-identity@3.972.43)': dependencies: '@vercel/oidc': 3.3.1 @@ -24551,7 +24564,7 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@24.12.4)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) '@vitest/expect@3.2.4': dependencies: @@ -24637,7 +24650,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@25.5.2)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) + vitest: 4.1.6(@opentelemetry/api@1.9.1)(@types/node@24.12.4)(@vitest/coverage-v8@4.1.6)(@vitest/ui@4.1.6)(esbuild@0.27.4)(jiti@2.7.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.4) '@vitest/utils@3.2.4': dependencies: @@ -26336,11 +26349,12 @@ snapshots: esbuild: 0.27.4 tsx: 4.21.0 - drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0): + drizzle-orm@0.45.2(@cloudflare/workers-types@4.20260511.1)(@opentelemetry/api@1.9.1)(@types/pg@8.18.0)(@upstash/redis@1.38.0)(bun-types@1.3.14)(kysely@0.29.2)(pg@8.20.0): optionalDependencies: '@cloudflare/workers-types': 4.20260511.1 '@opentelemetry/api': 1.9.1 '@types/pg': 8.18.0 + '@upstash/redis': 1.38.0 bun-types: 1.3.14 kysely: 0.29.2 pg: 8.20.0 @@ -32810,6 +32824,8 @@ snapshots: unbash@2.2.0: {} + uncrypto@0.1.3: {} + undici-types@6.21.0: {} undici-types@7.16.0: {}