feat: use redis for rate limiting & next caching to resolve memory issues (#2078)

Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
Shubham Palriwala
2024-04-02 20:16:43 +05:30
committed by GitHub
parent c8ab6644bf
commit 20eb679842
16 changed files with 493 additions and 127 deletions

View File

@@ -71,6 +71,7 @@ jobs:
S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }}
OPENTELEMETRY_LISTENER_URL: ${{ vars.OPENTELEMETRY_LISTENER_URL }}
RATE_LIMITING_DISABLED: ${{ vars.RATE_LIMITING_DISABLED }}
REDIS_CLIENT_URL: ${{ vars.REDIS_CLIENT_URL }}
KAMAL_REGISTRY_PASSWORD: ${{ secrets.KAMAL_REGISTRY_PASSWORD }}
steps:
@@ -108,7 +109,7 @@ jobs:
run: |
kamal() { command kamal "$@" -c kamal/deploy.yml; }
set +e
DEPLOY_OUTPUT=$(kamal setup 2>&1)
DEPLOY_OUTPUT=$(kamal deploy 2>&1)
DEPLOY_EXIT_CODE=$?
echo "$DEPLOY_OUTPUT"
if [[ "$DEPLOY_OUTPUT" == *"container not unhealthy (healthy)"* ]]; then

View File

@@ -0,0 +1,27 @@
import { createClient } from "redis";
import { REDIS_CLIENT_URL } from "@formbricks/lib/constants";
const client = createClient({
url: REDIS_CLIENT_URL!,
});
client.on("error", (err) => console.error("Redis Client Error", err));
client.connect();
type Options = {
interval: number;
allowedPerInterval: number;
};
export const redisRateLimiter = (options: Options) => {
return async (token: string) => {
const tokenCount = await client.INCR(token);
if (tokenCount === 1) {
await client.EXPIRE(token, options.interval / 1000);
}
if (tokenCount > options.allowedPerInterval) {
throw new Error("Rate limit exceeded for IP: " + token);
}
return;
};
};

View File

@@ -0,0 +1,21 @@
import { redisRateLimiter } from "@/app/api/internal/cache/client";
import { responses } from "@/app/lib/api/response";
import { NextRequest } from "next/server";
export async function GET(request: NextRequest) {
const token = request.nextUrl.searchParams.get("token");
const interval = parseInt(request.nextUrl.searchParams.get("interval") ?? "0");
const allowedPerInterval = parseInt(request.nextUrl.searchParams.get("allowedPerInterval") ?? "0");
if (!token) {
return responses.notAuthenticatedResponse();
}
try {
const rateLimiter = redisRateLimiter({ interval, allowedPerInterval });
await rateLimiter(token);
return responses.successResponse({ rateLimitExceeded: false }, true);
} catch (e) {
return responses.successResponse({ rateLimitExceeded: true }, true);
}
}

View File

@@ -1,28 +1,45 @@
import { LRUCache } from "lru-cache";
import { REDIS_CLIENT_URL, WEBAPP_URL } from "@formbricks/lib/constants";
type Options = {
interval: number;
allowedPerInterval: number;
};
export default function rateLimit(options: Options) {
const tokenCache = new LRUCache({
max: 1000, // Max 1000 unique IP sessions per 15 minutes
const inMemoryRateLimiter = (options: Options) => {
const tokenCache = new LRUCache<string, number>({
max: 1000,
ttl: options.interval,
});
return {
check: (token: string) =>
new Promise<void>((resolve, reject) => {
const tokenCount = (tokenCache.get(token) as number[]) || [0];
if (tokenCount[0] === 0) {
tokenCache.set(token, tokenCount);
}
tokenCount[0] += 1;
const currentUsage = tokenCount[0];
const isRateLimited = currentUsage >= options.allowedPerInterval;
return isRateLimited ? reject() : resolve();
}),
return async (token: string) => {
const currentUsage = tokenCache.get(token) || 0;
if (currentUsage >= options.allowedPerInterval) {
throw new Error("Rate limit exceeded");
}
tokenCache.set(token, currentUsage + 1);
};
};
const redisRateLimiter = (options: Options) => {
return async (token: string) => {
const tokenCountResponse = await fetch(
`${WEBAPP_URL}/api/internal/cache?token=${token}&interval=${options.interval}&allowedPerInterval=${options.allowedPerInterval}`
);
const {
data: { rateLimitExceeded },
} = await tokenCountResponse.json();
if (!tokenCountResponse.ok || rateLimitExceeded) {
throw new Error("Rate limit exceeded for IP: " + token);
}
};
};
export default function rateLimit(options: Options) {
if (REDIS_CLIENT_URL) {
return redisRateLimiter(options);
} else {
return inMemoryRateLimiter(options);
}
}

View File

@@ -0,0 +1,27 @@
import { CacheHandler } from "@neshca/cache-handler";
import createLruHandler from "@neshca/cache-handler/local-lru";
import createRedisHandler from "@neshca/cache-handler/redis-strings";
import { createClient } from "redis";
CacheHandler.onCreation(async () => {
let redisHandler;
if (process.env.REDIS_CLIENT_URL) {
const client = createClient({
url: process.env.REDIS_CLIENT_URL,
});
client.on("error", () => {});
await client.connect();
redisHandler = createRedisHandler({
client,
timeoutMs: 5000,
});
}
const localHandler = createLruHandler();
return {
handlers: [redisHandler, localHandler],
};
});
export default CacheHandler;

View File

@@ -35,7 +35,6 @@ export async function middleware(request: NextRequest) {
return NextResponse.next();
}
const res = NextResponse.next();
let ip = request.ip ?? request.headers.get("x-real-ip");
const forwardedFor = request.headers.get("x-forwarded-for");
if (!ip && forwardedFor) {
@@ -45,28 +44,27 @@ export async function middleware(request: NextRequest) {
if (ip) {
try {
if (loginRoute(request.nextUrl.pathname)) {
await loginLimiter.check(ip);
await loginLimiter(`login-${ip}`);
} else if (signupRoute(request.nextUrl.pathname)) {
await signUpLimiter.check(ip);
await signUpLimiter(`signup-${ip}`);
} else if (clientSideApiRoute(request.nextUrl.pathname)) {
await clientSideApiEndpointsLimiter.check(ip);
await clientSideApiEndpointsLimiter(`client-side-api-${ip}`);
const envIdAndUserId = isSyncWithUserIdentificationEndpoint(request.nextUrl.pathname);
if (envIdAndUserId) {
const { environmentId, userId } = envIdAndUserId;
await syncUserIdentificationLimiter.check(`${environmentId}-${userId}`);
await syncUserIdentificationLimiter(`sync-${environmentId}-${userId}`);
}
} else if (shareUrlRoute(request.nextUrl.pathname)) {
await shareUrlLimiter.check(ip);
await shareUrlLimiter(`share-${ip}`);
}
return res;
} catch (_e) {
return NextResponse.next();
} catch (e) {
console.log(`Rate Limiting IP: ${ip}`);
return NextResponse.json({ error: "Too many requests, Please try after a while!" }, { status: 429 });
}
}
return res;
return NextResponse.next();
}
export const config = {

View File

@@ -1,9 +1,11 @@
import { createId } from "@paralleldrive/cuid2";
import { withSentryConfig } from "@sentry/nextjs";
import createJiti from "jiti";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
const jiti = createJiti(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
jiti("@formbricks/lib/env");
@@ -24,6 +26,7 @@ const nextConfig = {
"app/api/packages": ["../../packages/js-core/dist/*", "../../packages/surveys/dist/*"],
},
},
cacheHandler: process.env.VERCEL !== "1" ? require.resolve("./cache-handler.mjs") : undefined,
transpilePackages: ["@formbricks/database", "@formbricks/ee", "@formbricks/ui", "@formbricks/lib"],
images: {
remotePatterns: [

View File

@@ -59,6 +59,7 @@
"react-hook-form": "^7.51.0",
"react-hot-toast": "^2.4.1",
"react-icons": "^5.0.1",
"redis": "^4.6.13",
"sharp": "^0.33.2",
"ua-parser-js": "^1.0.37",
"webpack": "^5.90.3",
@@ -66,6 +67,7 @@
},
"devDependencies": {
"@formbricks/tsconfig": "workspace:*",
"@neshca/cache-handler": "^1.0.7",
"@types/bcryptjs": "^2.4.6",
"@types/lodash": "^4.17.0",
"@types/markdown-it": "^13.0.7",

View File

@@ -6,6 +6,8 @@ x-webapp-url: &webapp_url http://localhost:3000
# PostgreSQL DB for Formbricks to connect to
x-database-url: &database_url postgresql://postgres:postgres@postgres:5432/formbricks?schema=public
x-redis-url: &redis_url
# NextJS Auth
# @see: https://next-auth.js.org/configuration/options#nextauth_secret
# You can use: `openssl rand -hex 32` to generate one
@@ -113,6 +115,7 @@ services:
GOOGLE_CLIENT_ID: *google_client_id
GOOGLE_CLIENT_SECRET: *google_client_secret
CRON_SECRET: *cron_secret
REDIS_CLIENT_URL: *redis_url
volumes:
- uploads:/home/nextjs/apps/web/uploads/

View File

@@ -79,6 +79,9 @@ x-environment: &environment
# Uncomment and set to 1 to skip onboarding for new users
# ONBOARDING_DISABLED: 1
# The below is used for Rate Limiting & Next Caching (uses In-Memory Next Cache if not provided)
# REDIS_CLIENT_URL:
services:
postgres:
restart: always

View File

@@ -86,6 +86,7 @@ env:
- S3_REGION
- S3_BUCKET_NAME
- RATE_LIMITING_DISABLED
- REDIS_CLIENT_URL
# Use a different ssh user than root
ssh:
@@ -138,27 +139,27 @@ traefik:
- CLOUDFLARE_EMAIL
# Use accessory services (secrets come from .env).
# accessories:
# db:
# image: mysql:8.0
# host: 192.168.0.2
# port: 3306
# env:
# clear:
# MYSQL_ROOT_HOST: '%'
# secret:
# - MYSQL_ROOT_PASSWORD
# files:
# - config/mysql/production.cnf:/etc/mysql/my.cnf
# - db/production.sql:/docker-entrypoint-initdb.d/setup.sql
# directories:
# - data:/var/lib/mysql
# redis:
# image: redis:7.0
# host: 192.168.0.2
# port: 6379
# directories:
# - data:/data
accessories:
# db:
# image: mysql:8.0
# host: 192.168.0.2
# port: 3306
# env:
# clear:
# MYSQL_ROOT_HOST: '%'
# secret:
# - MYSQL_ROOT_PASSWORD
# files:
# - config/mysql/production.cnf:/etc/mysql/my.cnf
# - db/production.sql:/docker-entrypoint-initdb.d/setup.sql
# directories:
# - data:/var/lib/mysql
redis:
image: redis:7.0
host: 18.196.187.144
port: 6379
directories:
- data:/data
# Configure custom arguments for Traefik
# traefik:

View File

@@ -172,6 +172,7 @@ export const DEBUG = env.DEBUG === "1";
// Enterprise License constant
export const ENTERPRISE_LICENSE_KEY = env.ENTERPRISE_LICENSE_KEY;
export const REDIS_CLIENT_URL = env.REDIS_CLIENT_URL;
export const RATE_LIMITING_DISABLED = env.RATE_LIMITING_DISABLED === "1";
export const CUSTOMER_IO_SITE_ID = env.CUSTOMER_IO_SITE_ID;

View File

@@ -52,6 +52,7 @@ export const env = createEnv({
OIDC_SIGNING_ALGORITHM: z.string().optional(),
OPENTELEMETRY_LISTENER_URL: z.string().optional(),
ONBOARDING_DISABLED: z.enum(["1", "0"]).optional(),
REDIS_CLIENT_URL: z.string().optional(),
PASSWORD_RESET_DISABLED: z.enum(["1", "0"]).optional(),
PRIVACY_URL: z
.string()
@@ -155,6 +156,7 @@ export const env = createEnv({
OIDC_ISSUER: process.env.OIDC_ISSUER,
OIDC_SIGNING_ALGORITHM: process.env.OIDC_SIGNING_ALGORITHM,
ONBOARDING_DISABLED: process.env.ONBOARDING_DISABLED,
REDIS_CLIENT_URL: process.env.REDIS_CLIENT_URL,
PASSWORD_RESET_DISABLED: process.env.PASSWORD_RESET_DISABLED,
PRIVACY_URL: process.env.PRIVACY_URL,
RATE_LIMITING_DISABLED: process.env.RATE_LIMITING_DISABLED,

View File

@@ -6,13 +6,13 @@
"downlevelIteration": true,
"baseUrl": ".",
"paths": {
"@prisma/client/*": ["@formbricks/database/client/*"],
"@prisma/client/*": ["@formbricks/database/client/*"]
},
"plugins": [
{
"name": "next",
},
"name": "next"
}
],
"strictNullChecks": true,
},
"strictNullChecks": true
}
}

407
pnpm-lock.yaml generated
View File

@@ -344,10 +344,10 @@ importers:
version: 2.2.2
'@radix-ui/react-collapsible':
specifier: ^1.0.3
version: 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
version: 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@react-email/components':
specifier: ^0.0.15
version: 0.0.15(@types/react@18.2.48)(react-email@2.1.0)(react@18.2.0)
version: 0.0.15(react-email@2.1.0)(react@18.2.0)
'@sentry/nextjs':
specifier: ^7.106.1
version: 7.106.1(encoding@0.1.13)(next@14.1.3)(react@18.2.0)(webpack@5.90.3)
@@ -429,6 +429,9 @@ importers:
react-icons:
specifier: ^5.0.1
version: 5.0.1(react@18.2.0)
redis:
specifier: ^4.6.13
version: 4.6.13
sharp:
specifier: ^0.33.2
version: 0.33.2
@@ -445,6 +448,9 @@ importers:
'@formbricks/tsconfig':
specifier: workspace:*
version: link:../../packages/tsconfig
'@neshca/cache-handler':
specifier: ^1.0.7
version: 1.0.7(next@14.1.3)(redis@4.6.13)
'@types/bcryptjs':
specifier: ^2.4.6
version: 2.4.6
@@ -647,7 +653,7 @@ importers:
version: 3.533.0
'@formbricks/api':
specifier: '*'
version: link:../api
version: 1.7.0
'@formbricks/database':
specifier: '*'
version: link:../database
@@ -3944,6 +3950,10 @@ packages:
resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==}
dev: false
/@formbricks/api@1.7.0:
resolution: {integrity: sha512-NpsxIMK2B6yUMH6Y7IoDavJoKEO72g3sBZzNVh66g5L+969Fw4gpRB9f8xDb6ei51cirSJzgyMXCAAWUAlxoNQ==}
dev: false
/@grpc/grpc-js@1.10.2:
resolution: {integrity: sha512-lSbgu8iayAod8O0YcoXK3+bMFGThY2svtN35Zlm9VepsB3jfyIcoupKknEht7Kh9Q8ITjsp0J4KpYo9l4+FhNg==}
engines: {node: '>=12.10.0'}
@@ -4723,6 +4733,17 @@ packages:
resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==}
dev: true
/@neshca/cache-handler@1.0.7(next@14.1.3)(redis@4.6.13):
resolution: {integrity: sha512-fn0jpHQ04cchhQf2DBHfvzPLTyKRRETk+BeiwDAbS0lPFX8EP3+iX2zFBw/SR3kMRAfvTrOTRCgdnSGJaJ/fHQ==}
peerDependencies:
next: '>=13.5.1'
redis: '>=4.6'
dependencies:
lru-cache: 10.2.0
next: 14.1.3(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
redis: 4.6.13
dev: true
/@next/env@13.5.6:
resolution: {integrity: sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw==}
dev: false
@@ -4733,7 +4754,6 @@ packages:
/@next/env@14.1.3:
resolution: {integrity: sha512-VhgXTvrgeBRxNPjyfBsDIMvgsKDxjlpw4IAUsHCX8Gjl1vtHUYRT3+xfQ/wwvLPDd/6kqfLqk9Pt4+7gysuCKQ==}
dev: false
/@next/eslint-plugin-next@14.1.1:
resolution: {integrity: sha512-NP1WoGFnFLpqqCWgGFjnn/sTwUExdPyjeFKRdQP1X/bL/tjAQ/TXDmYqw6vzGaP5NaZ2u6xzg+N/0nd7fOPOGQ==}
@@ -4772,7 +4792,6 @@ packages:
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-darwin-x64@14.1.0:
@@ -4790,7 +4809,6 @@ packages:
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-gnu@14.1.0:
@@ -4808,7 +4826,6 @@ packages:
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-arm64-musl@14.1.0:
@@ -4826,7 +4843,6 @@ packages:
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-gnu@14.1.0:
@@ -4844,7 +4860,6 @@ packages:
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-linux-x64-musl@14.1.0:
@@ -4862,7 +4877,6 @@ packages:
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-arm64-msvc@14.1.0:
@@ -4880,7 +4894,6 @@ packages:
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-ia32-msvc@14.1.0:
@@ -4898,7 +4911,6 @@ packages:
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@next/swc-win32-x64-msvc@14.1.0:
@@ -4916,7 +4928,6 @@ packages:
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@noble/hashes@1.3.3:
@@ -6246,13 +6257,13 @@ packages:
dependencies:
'@babel/runtime': 7.23.6
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collapsible': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-collapsible': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-collection': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -6279,6 +6290,25 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-arrow@1.0.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-checkbox@1.0.4(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==}
peerDependencies:
@@ -6294,10 +6324,10 @@ packages:
dependencies:
'@babel/runtime': 7.23.6
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-previous': 1.0.1(react@18.2.0)
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.48)(react@18.2.0)
@@ -6333,6 +6363,32 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-collapsible@1.0.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.6
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
peerDependencies:
@@ -6357,6 +6413,28 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-collection@1.0.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.48)(react@18.2.0):
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
peerDependencies:
@@ -6383,7 +6461,6 @@ packages:
'@babel/runtime': 7.23.8
'@types/react': 18.2.61
react: 18.2.0
dev: true
/@radix-ui/react-context@1.0.1(@types/react@18.2.48)(react@18.2.0):
resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==}
@@ -6414,16 +6491,16 @@ packages:
dependencies:
'@babel/runtime': 7.23.6
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.0.4(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
aria-hidden: 1.2.3
react: 18.2.0
@@ -6485,8 +6562,8 @@ packages:
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.48)(react@18.2.0)
react: 18.2.0
@@ -6508,11 +6585,11 @@ packages:
dependencies:
'@babel/runtime': 7.23.6
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-menu': 2.0.6(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -6569,8 +6646,8 @@ packages:
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -6605,7 +6682,7 @@ packages:
optional: true
dependencies:
'@babel/runtime': 7.23.6
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
@@ -6625,8 +6702,8 @@ packages:
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-collection': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(react-dom@18.2.0)(react@18.2.0)
@@ -6635,10 +6712,10 @@ packages:
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-roving-focus': 1.0.4(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
aria-hidden: 1.2.3
react: 18.2.0
@@ -6696,7 +6773,7 @@ packages:
dependencies:
'@babel/runtime': 7.23.6
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.48)(react@18.2.0)
@@ -6704,9 +6781,9 @@ packages:
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
aria-hidden: 1.2.3
react: 18.2.0
@@ -6759,10 +6836,10 @@ packages:
dependencies:
'@babel/runtime': 7.23.8
'@floating-ui/react-dom': 2.0.4(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-arrow': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
@@ -6807,7 +6884,7 @@ packages:
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
@@ -6834,6 +6911,26 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-presence@1.0.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
peerDependencies:
@@ -6855,6 +6952,25 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-primitive@1.0.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-radio-group@1.1.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-x+yELayyefNeKeTx4fjK6j99Fs6c4qKm3aY38G3swQVTN6xMpsrbigC0uHs2L//g8q4qR7qOcww8430jJmi2ag==}
peerDependencies:
@@ -6870,12 +6986,12 @@ packages:
dependencies:
'@babel/runtime': 7.23.6
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-roving-focus': 1.0.4(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-previous': 1.0.1(react@18.2.0)
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.48)(react@18.2.0)
@@ -6912,6 +7028,33 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-roving-focus@1.0.4(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-select@2.0.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==}
peerDependencies:
@@ -6928,8 +7071,8 @@ packages:
'@babel/runtime': 7.23.6
'@radix-ui/number': 1.0.1
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-collection': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(react-dom@18.2.0)(react@18.2.0)
@@ -6938,13 +7081,13 @@ packages:
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-previous': 1.0.1(react@18.2.0)
'@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-visually-hidden': 1.0.3(react-dom@18.2.0)(react@18.2.0)
aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
@@ -6967,11 +7110,11 @@ packages:
'@babel/runtime': 7.23.6
'@radix-ui/number': 1.0.1
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-collection': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-previous': 1.0.1(react@18.2.0)
@@ -7008,7 +7151,6 @@ packages:
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@types/react': 18.2.61
react: 18.2.0
dev: true
/@radix-ui/react-switch@1.0.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==}
@@ -7025,9 +7167,9 @@ packages:
dependencies:
'@babel/runtime': 7.23.6
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-use-previous': 1.0.1(react@18.2.0)
'@radix-ui/react-use-size': 1.0.1(@types/react@18.2.48)(react@18.2.0)
@@ -7132,17 +7274,17 @@ packages:
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/primitive': 1.0.1
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.0.5(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-popper': 1.1.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-portal': 1.0.4(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-presence': 1.0.1(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
'@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-visually-hidden': 1.0.3(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
@@ -7269,6 +7411,25 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/react-visually-hidden@1.0.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
dependencies:
'@babel/runtime': 7.23.8
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@radix-ui/rect@1.0.1:
resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
dependencies:
@@ -7352,6 +7513,38 @@ packages:
- react-email
dev: false
/@react-email/components@0.0.15(react-email@2.1.0)(react@18.2.0):
resolution: {integrity: sha512-jXfKiuyi94JBYfPVptEUwF57nRCvhEZIfyl2LqbL53fKsMrGlcjlN921iNnx1z41GAJOqZ8LPogeix3Iid23zw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
'@react-email/body': 0.0.7(react@18.2.0)
'@react-email/button': 0.0.14(react@18.2.0)
'@react-email/code-block': 0.0.3(react@18.2.0)
'@react-email/code-inline': 0.0.1(react@18.2.0)
'@react-email/column': 0.0.9(react@18.2.0)
'@react-email/container': 0.0.11(react@18.2.0)
'@react-email/font': 0.0.5(react@18.2.0)
'@react-email/head': 0.0.7(react@18.2.0)
'@react-email/heading': 0.0.11(react@18.2.0)
'@react-email/hr': 0.0.7(react@18.2.0)
'@react-email/html': 0.0.7(react@18.2.0)
'@react-email/img': 0.0.7(react@18.2.0)
'@react-email/link': 0.0.7(react@18.2.0)
'@react-email/markdown': 0.0.8(react-email@2.1.0)(react@18.2.0)
'@react-email/preview': 0.0.8(react@18.2.0)
'@react-email/render': 0.0.12
'@react-email/row': 0.0.7(react@18.2.0)
'@react-email/section': 0.0.11(react@18.2.0)
'@react-email/tailwind': 0.0.14(react@18.2.0)
'@react-email/text': 0.0.7(react@18.2.0)
react: 18.2.0
transitivePeerDependencies:
- '@types/react'
- react-email
dev: false
/@react-email/container@0.0.11(react@18.2.0):
resolution: {integrity: sha512-jzl/EHs0ClXIRFamfH+NR/cqv4GsJJscqRhdYtnWYuRAsWpKBM1muycrrPqIVhWvWi6sFHInWTt07jX+bDc3SQ==}
engines: {node: '>=18.0.0'}
@@ -7390,6 +7583,18 @@ packages:
- '@types/react'
dev: false
/@react-email/heading@0.0.11(react@18.2.0):
resolution: {integrity: sha512-EF5ZtRCxhHPw3m+8iibKKg0RAvAeHj1AP68sjU7s6+J+kvRgllr/E972Wi5Y8UvcIGossCvpX1WrSMDzeB4puA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: 18.2.0
dependencies:
'@radix-ui/react-slot': 1.0.2(@types/react@18.2.61)(react@18.2.0)
react: 18.2.0
transitivePeerDependencies:
- '@types/react'
dev: false
/@react-email/hr@0.0.7(react@18.2.0):
resolution: {integrity: sha512-8suK0M/deXHt0DBSeKhSC4bnCBCBm37xk6KJh9M0/FIKlvdltQBem52YUiuqVl1XLB87Y6v6tvspn3SZ9fuxEA==}
engines: {node: '>=18.0.0'}
@@ -7493,6 +7698,49 @@ packages:
react: 18.2.0
dev: false
/@redis/bloom@1.2.0(@redis/client@1.5.14):
resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.14
/@redis/client@1.5.14:
resolution: {integrity: sha512-YGn0GqsRBFUQxklhY7v562VMOP0DcmlrHHs3IV1mFE3cbxe31IITUkqhBcIhVSI/2JqtWAJXg5mjV4aU+zD0HA==}
engines: {node: '>=14'}
dependencies:
cluster-key-slot: 1.1.2
generic-pool: 3.9.0
yallist: 4.0.0
/@redis/graph@1.1.1(@redis/client@1.5.14):
resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.14
/@redis/json@1.0.6(@redis/client@1.5.14):
resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.14
/@redis/search@1.1.6(@redis/client@1.5.14):
resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.14
/@redis/time-series@1.0.5(@redis/client@1.5.14):
resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==}
peerDependencies:
'@redis/client': ^1.0.0
dependencies:
'@redis/client': 1.5.14
/@resvg/resvg-wasm@2.4.0:
resolution: {integrity: sha512-C7c51Nn4yTxXFKvgh2txJFNweaVcfUPQxwEUFw4aWsCmfiBDJsTSwviIF8EcwjQ6k8bPyMWCl1vw4BdxE569Cg==}
engines: {node: '>= 10'}
@@ -9034,7 +9282,6 @@ packages:
resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==}
dependencies:
tslib: 2.6.2
dev: false
/@swc/types@0.1.5:
resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==}
@@ -11035,7 +11282,6 @@ packages:
engines: {node: '>=10.16.0'}
dependencies:
streamsearch: 1.1.0
dev: false
/bytes@3.0.0:
resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
@@ -11312,7 +11558,6 @@ packages:
/client-only@0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
dev: false
/clipboardy@3.0.0:
resolution: {integrity: sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==}
@@ -11358,6 +11603,10 @@ packages:
resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==}
engines: {node: '>=6'}
/cluster-key-slot@1.1.2:
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
engines: {node: '>=0.10.0'}
/cmdk@1.0.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==}
peerDependencies:
@@ -11365,7 +11614,7 @@ packages:
react-dom: ^18.0.0
dependencies:
'@radix-ui/react-dialog': 1.0.5(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-primitive': 1.0.3(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
transitivePeerDependencies:
@@ -13265,6 +13514,10 @@ packages:
- supports-color
dev: false
/generic-pool@3.9.0:
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
engines: {node: '>= 4'}
/gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
@@ -15980,7 +16233,6 @@ packages:
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
dev: false
/node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
@@ -16667,7 +16919,6 @@ packages:
nanoid: 3.3.7
picocolors: 1.0.0
source-map-js: 1.0.2
dev: false
/postcss@8.4.35:
resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
@@ -17616,6 +17867,16 @@ packages:
strip-indent: 3.0.0
dev: false
/redis@4.6.13:
resolution: {integrity: sha512-MHgkS4B+sPjCXpf+HfdetBwbRz6vCtsceTmw1pHNYJAsYxrfpOP6dz+piJWGos8wqG7qb3vj/Rrc5qOlmInUuA==}
dependencies:
'@redis/bloom': 1.2.0(@redis/client@1.5.14)
'@redis/client': 1.5.14
'@redis/graph': 1.1.1(@redis/client@1.5.14)
'@redis/json': 1.0.6(@redis/client@1.5.14)
'@redis/search': 1.1.6(@redis/client@1.5.14)
'@redis/time-series': 1.0.5(@redis/client@1.5.14)
/redux@4.2.1:
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
dependencies:
@@ -18551,7 +18812,6 @@ packages:
/streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
dev: false
/string-argv@0.3.2:
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
@@ -18740,7 +19000,6 @@ packages:
dependencies:
client-only: 0.0.1
react: 18.2.0
dev: false
/stylis@4.3.0:
resolution: {integrity: sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==}

View File

@@ -118,6 +118,7 @@
"PLAYWRIGHT_CI",
"PRIVACY_URL",
"RATE_LIMITING_DISABLED",
"REDIS_CLIENT_URL",
"S3_ACCESS_KEY",
"S3_SECRET_KEY",
"S3_REGION",