diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml index 8903418c61..1104dbbd0f 100644 --- a/.github/workflows/sonarqube.yml +++ b/.github/workflows/sonarqube.yml @@ -1,11 +1,11 @@ name: SonarQube on: workflow_dispatch: -# push: -# branches: -# - main -# pull_request: -# types: [opened, synchronize, reopened] + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened] permissions: contents: read jobs: @@ -16,6 +16,35 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + + - name: Setup Node.js 20.x + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af + with: + node-version: 20.x + + - name: Install pnpm + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 + + - name: Install dependencies + run: pnpm install --config.platform=linux --config.architecture=x64 + + - name: create .env + run: cp .env.example .env + + - name: Generate Random ENCRYPTION_KEY, CRON_SECRET & NEXTAUTH_SECRET and fill in .env + run: | + RANDOM_KEY=$(openssl rand -hex 32) + sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${RANDOM_KEY}/" .env + sed -i "s/CRON_SECRET=.*/CRON_SECRET=${RANDOM_KEY}/" .env + sed -i "s/NEXTAUTH_SECRET=.*/NEXTAUTH_SECRET=${RANDOM_KEY}/" .env + + - name: Run tests with coverage + run: | + cd apps/web + pnpm test:coverage + cd ../../ + # The Vitest coverage config is in your vite.config.mts + - name: SonarQube Scan uses: SonarSource/sonarqube-scan-action@bfd4e558cda28cda6b5defafb9232d191be8c203 env: diff --git a/apps/demo-react-native/.env.example b/apps/demo-react-native/.env.example index 3a2d97bdc4..340aecb341 100644 --- a/apps/demo-react-native/.env.example +++ b/apps/demo-react-native/.env.example @@ -1,2 +1,2 @@ -EXPO_PUBLIC_API_HOST=http://192.168.178.20:3000 -EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=clzr04nkd000bcdl110j0ijyq +EXPO_PUBLIC_APP_URL=http://192.168.0.197:3000 +EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=cm5p0cs7r000819182b32j0a1 \ No newline at end of file diff --git a/apps/demo-react-native/app.json b/apps/demo-react-native/app.json index 66cd17cbb8..31d6cb2a53 100644 --- a/apps/demo-react-native/app.json +++ b/apps/demo-react-native/app.json @@ -18,6 +18,7 @@ }, "jsEngine": "hermes", "name": "react-native-demo", + "newArchEnabled": true, "orientation": "portrait", "slug": "react-native-demo", "splash": { diff --git a/apps/demo-react-native/package.json b/apps/demo-react-native/package.json index 4dc5955136..acd06c3451 100644 --- a/apps/demo-react-native/package.json +++ b/apps/demo-react-native/package.json @@ -13,16 +13,17 @@ "dependencies": { "@formbricks/js": "workspace:*", "@formbricks/react-native": "workspace:*", - "expo": "52.0.18", - "expo-status-bar": "2.0.0", + "@react-native-async-storage/async-storage": "2.1.0", + "expo": "52.0.28", + "expo-status-bar": "2.0.1", "react": "18.3.1", "react-dom": "18.3.1", - "react-native": "0.76.5", + "react-native": "0.76.6", "react-native-webview": "13.12.5" }, "devDependencies": { "@babel/core": "7.26.0", - "@types/react": "19.0.1", + "@types/react": "18.3.18", "typescript": "5.7.2" }, "private": true diff --git a/apps/demo-react-native/src/app.tsx b/apps/demo-react-native/src/app.tsx index 28e0f50552..a4816481e3 100644 --- a/apps/demo-react-native/src/app.tsx +++ b/apps/demo-react-native/src/app.tsx @@ -1,7 +1,14 @@ import { StatusBar } from "expo-status-bar"; import React, { type JSX } from "react"; import { Button, LogBox, StyleSheet, Text, View } from "react-native"; -import Formbricks, { track } from "@formbricks/react-native"; +import Formbricks, { + logout, + setAttribute, + setAttributes, + setLanguage, + setUserId, + track, +} from "@formbricks/react-native"; LogBox.ignoreAllLogs(); @@ -10,35 +17,92 @@ export default function App(): JSX.Element { throw new Error("EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID is required"); } - if (!process.env.EXPO_PUBLIC_API_HOST) { - throw new Error("EXPO_PUBLIC_API_HOST is required"); + if (!process.env.EXPO_PUBLIC_APP_URL) { + throw new Error("EXPO_PUBLIC_APP_URL is required"); } - const config = { - environmentId: process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID as string, - apiHost: process.env.EXPO_PUBLIC_API_HOST as string, - userId: "random-user-id", - attributes: { - language: "en", - testAttr: "attr-test", - }, - }; - return ( Formbricks React Native SDK Demo - - - - ); - } else if (user?.email?.toLowerCase() !== email?.toLowerCase()) { - return ( - - - - ); - } else { - after(async () => { - await createMembershipAction(); - }); - return ( - - - - ); - } - } catch (e) { - console.error(e); - return ( - - ); - } -}; - -export default Page; +export default InvitePage; diff --git a/apps/web/app/api/v1/client/[environmentId]/environment/lib/environmentState.ts b/apps/web/app/api/v1/client/[environmentId]/environment/lib/environmentState.ts index 76c771f610..51d2dd0d4c 100644 --- a/apps/web/app/api/v1/client/[environmentId]/environment/lib/environmentState.ts +++ b/apps/web/app/api/v1/client/[environmentId]/environment/lib/environmentState.ts @@ -29,7 +29,7 @@ import { getSurveysForEnvironmentState } from "./survey"; */ export const getEnvironmentState = async ( environmentId: string -): Promise<{ state: TJsEnvironmentState["data"]; revalidateEnvironment?: boolean }> => +): Promise<{ data: TJsEnvironmentState["data"]; revalidateEnvironment?: boolean }> => cache( async () => { let revalidateEnvironment = false; @@ -102,14 +102,14 @@ export const getEnvironmentState = async ( (survey) => survey.type === "app" && survey.status === "inProgress" ); - const state: TJsEnvironmentState["data"] = { + const data: TJsEnvironmentState["data"] = { surveys: !isMonthlyResponsesLimitReached ? filteredSurveys : [], actionClasses, project: project, }; return { - state, + data, revalidateEnvironment, }; }, diff --git a/apps/web/app/api/v1/client/[environmentId]/environment/route.ts b/apps/web/app/api/v1/client/[environmentId]/environment/route.ts index c24b3f1f5d..a57d36109e 100644 --- a/apps/web/app/api/v1/client/[environmentId]/environment/route.ts +++ b/apps/web/app/api/v1/client/[environmentId]/environment/route.ts @@ -36,16 +36,20 @@ export const GET = async ( try { const environmentState = await getEnvironmentState(params.environmentId); + const { data, revalidateEnvironment } = environmentState; - if (environmentState.revalidateEnvironment) { + if (revalidateEnvironment) { environmentCache.revalidate({ id: inputValidation.data.environmentId, - projectId: environmentState.state.project.id, + projectId: data.project.id, }); } return responses.successResponse( - environmentState.state, + { + data, + expiresAt: new Date(Date.now() + 1000 * 60 * 30), // 30 minutes + }, true, "public, s-maxage=600, max-age=840, stale-while-revalidate=600, stale-if-error=600" ); diff --git a/apps/web/app/api/v1/client/[environmentId]/user/route.ts b/apps/web/app/api/v1/client/[environmentId]/user/route.ts new file mode 100644 index 0000000000..0198ac1f99 --- /dev/null +++ b/apps/web/app/api/v1/client/[environmentId]/user/route.ts @@ -0,0 +1,3 @@ +import { OPTIONS, POST } from "@/modules/ee/contacts/api/client/[environmentId]/user/route"; + +export { POST, OPTIONS }; diff --git a/apps/web/app/setup/(fresh-instance)/intro/page.tsx b/apps/web/app/setup/(fresh-instance)/intro/page.tsx index 70f3ddbb83..a1d0c89566 100644 --- a/apps/web/app/setup/(fresh-instance)/intro/page.tsx +++ b/apps/web/app/setup/(fresh-instance)/intro/page.tsx @@ -1,35 +1,4 @@ -import { Button } from "@/modules/ui/components/button"; -import { Metadata } from "next"; -import { getTranslations } from "next-intl/server"; -import Link from "next/link"; +import { IntroPage, metadata } from "@/modules/setup/(fresh-instance)/intro/page"; -export const metadata: Metadata = { - title: "Intro", - description: "Open-source Experience Management. Free & open source.", -}; - -const renderRichText = async (text: string) => { - const t = await getTranslations(); - return

{t.rich(text, { b: (chunks) => {chunks} })}

; -}; - -const Page = async () => { - const t = await getTranslations(); - return ( -
-

{t("setup.intro.welcome_to_formbricks")}

-
- {renderRichText("setup.intro.paragraph_1")} - {renderRichText("setup.intro.paragraph_2")} - {renderRichText("setup.intro.paragraph_3")} -
- - -

{t("setup.intro.made_with_love_in_kiel")}

-
- ); -}; - -export default Page; +export { metadata }; +export default IntroPage; diff --git a/apps/web/app/setup/(fresh-instance)/layout.tsx b/apps/web/app/setup/(fresh-instance)/layout.tsx index f0ae3ea0b2..d8fd828ba5 100644 --- a/apps/web/app/setup/(fresh-instance)/layout.tsx +++ b/apps/web/app/setup/(fresh-instance)/layout.tsx @@ -1,16 +1,3 @@ -import { authOptions } from "@/modules/auth/lib/authOptions"; -import { getServerSession } from "next-auth"; -import { notFound } from "next/navigation"; -import { getIsFreshInstance } from "@formbricks/lib/instance/service"; - -const FreshInstanceLayout = async ({ children }: { children: React.ReactNode }) => { - const session = await getServerSession(authOptions); - const isFreshInstance = await getIsFreshInstance(); - - if (session ?? !isFreshInstance) { - return notFound(); - } - return <>{children}; -}; +import { FreshInstanceLayout } from "@/modules/setup/(fresh-instance)/layout"; export default FreshInstanceLayout; diff --git a/apps/web/app/setup/(fresh-instance)/signup/page.tsx b/apps/web/app/setup/(fresh-instance)/signup/page.tsx index 86b2356fce..9eae7c8b3b 100644 --- a/apps/web/app/setup/(fresh-instance)/signup/page.tsx +++ b/apps/web/app/setup/(fresh-instance)/signup/page.tsx @@ -1,57 +1,4 @@ -import { SignupForm } from "@/modules/auth/signup/components/signup-form"; -import { getIsSSOEnabled } from "@/modules/ee/license-check/lib/utils"; -import { Metadata } from "next"; -import { getTranslations } from "next-intl/server"; -import { - AZURE_OAUTH_ENABLED, - DEFAULT_ORGANIZATION_ID, - DEFAULT_ORGANIZATION_ROLE, - EMAIL_AUTH_ENABLED, - EMAIL_VERIFICATION_DISABLED, - GITHUB_OAUTH_ENABLED, - GOOGLE_OAUTH_ENABLED, - IS_TURNSTILE_CONFIGURED, - OIDC_DISPLAY_NAME, - OIDC_OAUTH_ENABLED, - PRIVACY_URL, - TERMS_URL, - WEBAPP_URL, -} from "@formbricks/lib/constants"; -import { findMatchingLocale } from "@formbricks/lib/utils/locale"; +import { SignupPage, metadata } from "@/modules/setup/(fresh-instance)/signup/page"; -export const metadata: Metadata = { - title: "Sign up", - description: "Open-source Experience Management. Free & open source.", -}; - -const Page = async () => { - const locale = await findMatchingLocale(); - const isSSOEnabled = await getIsSSOEnabled(); - const t = await getTranslations(); - return ( -
-

{t("setup.signup.create_administrator")}

-

{t("setup.signup.this_user_has_all_the_power")}

-
- -
- ); -}; - -export default Page; +export { metadata }; +export default SignupPage; diff --git a/apps/web/app/setup/layout.tsx b/apps/web/app/setup/layout.tsx index fdea39aa02..5a4138afb0 100644 --- a/apps/web/app/setup/layout.tsx +++ b/apps/web/app/setup/layout.tsx @@ -1,22 +1,3 @@ -import { FormbricksLogo } from "@/modules/ui/components/formbricks-logo"; -import { Toaster } from "react-hot-toast"; - -const SetupLayout = ({ children }: { children: React.ReactNode }) => { - return ( - <> - -
-
-
- -
- {children} -
-
- - ); -}; +import { SetupLayout } from "@/modules/setup/layout"; export default SetupLayout; diff --git a/apps/web/app/setup/organization/[organizationId]/invite/page.tsx b/apps/web/app/setup/organization/[organizationId]/invite/page.tsx index 44cb556384..62aec844aa 100644 --- a/apps/web/app/setup/organization/[organizationId]/invite/page.tsx +++ b/apps/web/app/setup/organization/[organizationId]/invite/page.tsx @@ -1,40 +1,4 @@ -import { InviteMembers } from "@/app/setup/organization/[organizationId]/invite/components/invite-members"; -import { authOptions } from "@/modules/auth/lib/authOptions"; -import { Metadata } from "next"; -import { getServerSession } from "next-auth"; -import { getTranslations } from "next-intl/server"; -import { notFound } from "next/navigation"; -import { SMTP_HOST, SMTP_PASSWORD, SMTP_PORT, SMTP_USER } from "@formbricks/lib/constants"; -import { verifyUserRoleAccess } from "@formbricks/lib/organization/auth"; -import { AuthenticationError } from "@formbricks/types/errors"; +import { InvitePage, metadata } from "@/modules/setup/organization/[organizationId]/invite/page"; -type Params = Promise<{ - organizationId: string; -}>; -export const metadata: Metadata = { - title: "Invite", - description: "Open-source Experience Management. Free & open source.", -}; - -interface InvitePageProps { - params: Params; -} - -const Page = async (props: InvitePageProps) => { - const params = await props.params; - const t = await getTranslations(); - const IS_SMTP_CONFIGURED = Boolean(SMTP_HOST && SMTP_PORT && SMTP_USER && SMTP_PASSWORD); - const session = await getServerSession(authOptions); - if (!session) throw new AuthenticationError(t("common.session_not_found")); - - const { hasCreateOrUpdateMembersAccess } = await verifyUserRoleAccess( - params.organizationId, - session.user.id - ); - - if (!hasCreateOrUpdateMembersAccess) return notFound(); - - return ; -}; - -export default Page; +export { metadata }; +export default InvitePage; diff --git a/apps/web/app/setup/organization/create/page.tsx b/apps/web/app/setup/organization/create/page.tsx index 030ea5a128..7fdaf27827 100644 --- a/apps/web/app/setup/organization/create/page.tsx +++ b/apps/web/app/setup/organization/create/page.tsx @@ -1,47 +1,4 @@ -import { RemovedFromOrganization } from "@/app/setup/organization/create/components/removed-from-organization"; -import { authOptions } from "@/modules/auth/lib/authOptions"; -import { getIsMultiOrgEnabled } from "@/modules/ee/license-check/lib/utils"; -import { ClientLogout } from "@/modules/ui/components/client-logout"; -import { Metadata } from "next"; -import { getServerSession } from "next-auth"; -import { getTranslations } from "next-intl/server"; -import { notFound } from "next/navigation"; -import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants"; -import { gethasNoOrganizations } from "@formbricks/lib/instance/service"; -import { getOrganizationsByUserId } from "@formbricks/lib/organization/service"; -import { getUser } from "@formbricks/lib/user/service"; -import { AuthenticationError } from "@formbricks/types/errors"; -import { CreateOrganization } from "./components/create-organization"; +import { CreateOrganizationPage, metadata } from "@/modules/setup/organization/create/page"; -export const metadata: Metadata = { - title: "Create Organization", - description: "Open-source Experience Management. Free & open source.", -}; - -const Page = async () => { - const t = await getTranslations(); - const session = await getServerSession(authOptions); - - if (!session) throw new AuthenticationError(t("common.session_not_found")); - - const user = await getUser(session.user.id); - if (!user) { - return ; - } - - const hasNoOrganizations = await gethasNoOrganizations(); - const isMultiOrgEnabled = await getIsMultiOrgEnabled(); - const userOrganizations = await getOrganizationsByUserId(session.user.id); - - if (hasNoOrganizations || isMultiOrgEnabled) { - return ; - } - - if (userOrganizations.length === 0) { - return ; - } - - return notFound(); -}; - -export default Page; +export { metadata }; +export default CreateOrganizationPage; diff --git a/packages/lib/invite/cache.ts b/apps/web/lib/cache/invite.ts similarity index 100% rename from packages/lib/invite/cache.ts rename to apps/web/lib/cache/invite.ts diff --git a/apps/web/lib/utils/services.ts b/apps/web/lib/utils/services.ts index 6829f1bd90..8f80fdb7c1 100644 --- a/apps/web/lib/utils/services.ts +++ b/apps/web/lib/utils/services.ts @@ -2,6 +2,7 @@ import { apiKeyCache } from "@/lib/cache/api-key"; import { contactCache } from "@/lib/cache/contact"; +import { inviteCache } from "@/lib/cache/invite"; import { teamCache } from "@/lib/cache/team"; import { webhookCache } from "@/lib/cache/webhook"; import { Prisma } from "@prisma/client"; @@ -12,7 +13,6 @@ import { cache } from "@formbricks/lib/cache"; import { segmentCache } from "@formbricks/lib/cache/segment"; import { environmentCache } from "@formbricks/lib/environment/cache"; import { integrationCache } from "@formbricks/lib/integration/cache"; -import { inviteCache } from "@formbricks/lib/invite/cache"; import { projectCache } from "@formbricks/lib/project/cache"; import { responseCache } from "@formbricks/lib/response/cache"; import { responseNoteCache } from "@formbricks/lib/responseNote/cache"; diff --git a/apps/web/app/(auth)/invite/components/ContentLayout.tsx b/apps/web/modules/auth/invite/components/content-layout.tsx similarity index 100% rename from apps/web/app/(auth)/invite/components/ContentLayout.tsx rename to apps/web/modules/auth/invite/components/content-layout.tsx diff --git a/apps/web/modules/auth/invite/lib/invite.ts b/apps/web/modules/auth/invite/lib/invite.ts new file mode 100644 index 0000000000..ae007c2081 --- /dev/null +++ b/apps/web/modules/auth/invite/lib/invite.ts @@ -0,0 +1,78 @@ +import { inviteCache } from "@/lib/cache/invite"; +import { type InviteWithCreator } from "@/modules/auth/invite/types/invites"; +import { Prisma } from "@prisma/client"; +import { cache as reactCache } from "react"; +import { prisma } from "@formbricks/database"; +import { cache } from "@formbricks/lib/cache"; +import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors"; + +export const deleteInvite = async (inviteId: string): Promise => { + try { + const invite = await prisma.invite.delete({ + where: { + id: inviteId, + }, + select: { + id: true, + organizationId: true, + }, + }); + + if (!invite) { + throw new ResourceNotFoundError("Invite", inviteId); + } + + inviteCache.revalidate({ + id: invite.id, + organizationId: invite.organizationId, + }); + + return true; + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } +}; + +export const getInvite = reactCache( + async (inviteId: string): Promise => + cache( + async () => { + try { + const invite = await prisma.invite.findUnique({ + where: { + id: inviteId, + }, + select: { + id: true, + expiresAt: true, + organizationId: true, + role: true, + teamIds: true, + creator: { + select: { + name: true, + email: true, + }, + }, + }, + }); + + return invite; + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } + }, + [`invite-getInvite-${inviteId}`], + { + tags: [inviteCache.tag.byId(inviteId)], + } + )() +); diff --git a/apps/web/modules/invite/lib/team.ts b/apps/web/modules/auth/invite/lib/team.ts similarity index 84% rename from apps/web/modules/invite/lib/team.ts rename to apps/web/modules/auth/invite/lib/team.ts index e923716587..00ddc6dab6 100644 --- a/apps/web/modules/invite/lib/team.ts +++ b/apps/web/modules/auth/invite/lib/team.ts @@ -1,17 +1,13 @@ import "server-only"; import { teamCache } from "@/lib/cache/team"; +import { CreateMembershipInvite } from "@/modules/auth/invite/types/invites"; import { Prisma } from "@prisma/client"; import { prisma } from "@formbricks/database"; import { getAccessFlags } from "@formbricks/lib/membership/utils"; import { projectCache } from "@formbricks/lib/project/cache"; -import { validateInputs } from "@formbricks/lib/utils/validate"; -import { ZString } from "@formbricks/types/common"; import { DatabaseError } from "@formbricks/types/errors"; -import { TInvite, ZInvite } from "@formbricks/types/invites"; - -export const createTeamMembership = async (invite: TInvite, userId: string): Promise => { - validateInputs([invite, ZInvite], [userId, ZString]); +export const createTeamMembership = async (invite: CreateMembershipInvite, userId: string): Promise => { const teamIds = invite.teamIds || []; const userMembershipRole = invite.role; const { isOwner, isManager } = getAccessFlags(userMembershipRole); diff --git a/apps/web/modules/auth/invite/page.tsx b/apps/web/modules/auth/invite/page.tsx new file mode 100644 index 0000000000..4f02c2f9e2 --- /dev/null +++ b/apps/web/modules/auth/invite/page.tsx @@ -0,0 +1,147 @@ +import { deleteInvite, getInvite } from "@/modules/auth/invite/lib/invite"; +import { createTeamMembership } from "@/modules/auth/invite/lib/team"; +import { authOptions } from "@/modules/auth/lib/authOptions"; +import { sendInviteAcceptedEmail } from "@/modules/email"; +import { Button } from "@/modules/ui/components/button"; +import { getServerSession } from "next-auth"; +import { getTranslations } from "next-intl/server"; +import Link from "next/link"; +import { after } from "next/server"; +import { DEFAULT_LOCALE, WEBAPP_URL } from "@formbricks/lib/constants"; +import { verifyInviteToken } from "@formbricks/lib/jwt"; +import { createMembership } from "@formbricks/lib/membership/service"; +import { getUser, updateUser } from "@formbricks/lib/user/service"; +import { ContentLayout } from "./components/content-layout"; + +interface InvitePageProps { + searchParams: Promise<{ token: string }>; +} + +export const InvitePage = async (props: InvitePageProps) => { + const searchParams = await props.searchParams; + const t = await getTranslations(); + const session = await getServerSession(authOptions); + const user = session?.user.id ? await getUser(session.user.id) : null; + + try { + const { inviteId, email } = verifyInviteToken(searchParams.token); + + const invite = await getInvite(inviteId); + + if (!invite) { + return ( + + ); + } + + const isInviteExpired = new Date(invite.expiresAt) < new Date(); + + if (isInviteExpired) { + return ( + + ); + } + + if (!session) { + const redirectUrl = WEBAPP_URL + "/invite?token=" + searchParams.token; + const encodedEmail = encodeURIComponent(email); + return ( + + + + + ); + } + + if (user?.email?.toLowerCase() !== email?.toLowerCase()) { + return ( + + + + ); + } + + const createMembershipAction = async () => { + "use server"; + + if (!session || !user) return; + + await createMembership(invite.organizationId, session.user.id, { + accepted: true, + role: invite.role, + }); + if (invite.teamIds) { + await createTeamMembership( + { + organizationId: invite.organizationId, + role: invite.role, + teamIds: invite.teamIds, + }, + user.id + ); + } + await deleteInvite(inviteId); + await sendInviteAcceptedEmail( + invite.creator.name ?? "", + user?.name ?? "", + invite.creator.email, + user?.locale ?? DEFAULT_LOCALE + ); + await updateUser(session.user.id, { + notificationSettings: { + ...user.notificationSettings, + alert: user.notificationSettings.alert ?? {}, + weeklySummary: user.notificationSettings.weeklySummary ?? {}, + unsubscribedOrganizationIds: Array.from( + new Set([ + ...(user.notificationSettings?.unsubscribedOrganizationIds || []), + invite.organizationId, + ]) + ), + }, + }); + }; + + after(async () => { + await createMembershipAction(); + }); + + return ( + + + + ); + } catch (e) { + console.error(e); + return ( + + ); + } +}; diff --git a/apps/web/modules/auth/invite/types/invites.ts b/apps/web/modules/auth/invite/types/invites.ts new file mode 100644 index 0000000000..224799d4df --- /dev/null +++ b/apps/web/modules/auth/invite/types/invites.ts @@ -0,0 +1,11 @@ +import { Invite } from "@prisma/client"; + +export interface InviteWithCreator + extends Pick { + creator: { + name: string | null; + email: string; + }; +} + +export interface CreateMembershipInvite extends Pick {} diff --git a/apps/web/modules/auth/signup/actions.ts b/apps/web/modules/auth/signup/actions.ts index 1bfd22dab2..51129503b9 100644 --- a/apps/web/modules/auth/signup/actions.ts +++ b/apps/web/modules/auth/signup/actions.ts @@ -1,17 +1,15 @@ "use server"; import { actionClient } from "@/lib/utils/action-client"; -import { createUser } from "@/modules/auth/lib/user"; -import { updateUser } from "@/modules/auth/lib/user"; +import { createUser, updateUser } from "@/modules/auth/lib/user"; +import { deleteInvite, getInvite } from "@/modules/auth/signup/lib/invite"; +import { createTeamMembership } from "@/modules/auth/signup/lib/team"; import { captureFailedSignup, verifyTurnstileToken } from "@/modules/auth/signup/lib/utils"; import { getIsMultiOrgEnabled } from "@/modules/ee/license-check/lib/utils"; import { sendInviteAcceptedEmail, sendVerificationEmail } from "@/modules/email"; -import { createTeamMembership } from "@/modules/invite/lib/team"; import { z } from "zod"; import { hashPassword } from "@formbricks/lib/auth"; import { IS_TURNSTILE_CONFIGURED, TURNSTILE_SECRET_KEY } from "@formbricks/lib/constants"; -import { getInvite } from "@formbricks/lib/invite/service"; -import { deleteInvite } from "@formbricks/lib/invite/service"; import { verifyInviteToken } from "@formbricks/lib/jwt"; import { createMembership } from "@formbricks/lib/membership/service"; import { createOrganization, getOrganization } from "@formbricks/lib/organization/service"; @@ -74,7 +72,14 @@ export const createUserAction = actionClient.schema(ZCreateUserAction).action(as }); if (invite.teamIds) { - await createTeamMembership(invite, user.id); + await createTeamMembership( + { + organizationId: invite.organizationId, + role: invite.role, + teamIds: invite.teamIds, + }, + user.id + ); } await updateUser(user.id, { @@ -85,7 +90,12 @@ export const createUserAction = actionClient.schema(ZCreateUserAction).action(as }, }); - await sendInviteAcceptedEmail(invite.creator.name ?? "", user.name, invite.creator.email, user.locale); + await sendInviteAcceptedEmail( + invite.creator.name ?? "", + user.name, + invite.creator.email, + invite.creator.locale + ); await deleteInvite(invite.id); } // Handle organization assignment diff --git a/apps/web/modules/auth/signup/lib/invite.ts b/apps/web/modules/auth/signup/lib/invite.ts new file mode 100644 index 0000000000..6a2cd7be03 --- /dev/null +++ b/apps/web/modules/auth/signup/lib/invite.ts @@ -0,0 +1,78 @@ +import { inviteCache } from "@/lib/cache/invite"; +import { InviteWithCreator } from "@/modules/auth/signup/types/invites"; +import { Prisma } from "@prisma/client"; +import { cache as reactCache } from "react"; +import { prisma } from "@formbricks/database"; +import { cache } from "@formbricks/lib/cache"; +import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors"; + +export const deleteInvite = async (inviteId: string): Promise => { + try { + const invite = await prisma.invite.delete({ + where: { + id: inviteId, + }, + select: { + id: true, + organizationId: true, + }, + }); + + if (!invite) { + throw new ResourceNotFoundError("Invite", inviteId); + } + + inviteCache.revalidate({ + id: invite.id, + organizationId: invite.organizationId, + }); + + return true; + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } +}; + +export const getInvite = reactCache( + async (inviteId: string): Promise => + cache( + async () => { + try { + const invite = await prisma.invite.findUnique({ + where: { + id: inviteId, + }, + select: { + id: true, + organizationId: true, + role: true, + teamIds: true, + creator: { + select: { + name: true, + email: true, + locale: true, + }, + }, + }, + }); + + return invite; + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } + }, + [`signup-getInvite-${inviteId}`], + { + tags: [inviteCache.tag.byId(inviteId)], + } + )() +); diff --git a/apps/web/modules/auth/signup/lib/team.ts b/apps/web/modules/auth/signup/lib/team.ts new file mode 100644 index 0000000000..d3564a1512 --- /dev/null +++ b/apps/web/modules/auth/signup/lib/team.ts @@ -0,0 +1,65 @@ +import "server-only"; +import { teamCache } from "@/lib/cache/team"; +import { CreateMembershipInvite } from "@/modules/auth/signup/types/invites"; +import { Prisma } from "@prisma/client"; +import { prisma } from "@formbricks/database"; +import { getAccessFlags } from "@formbricks/lib/membership/utils"; +import { projectCache } from "@formbricks/lib/project/cache"; +import { DatabaseError } from "@formbricks/types/errors"; + +export const createTeamMembership = async (invite: CreateMembershipInvite, userId: string): Promise => { + const teamIds = invite.teamIds || []; + const userMembershipRole = invite.role; + const { isOwner, isManager } = getAccessFlags(userMembershipRole); + + const validTeamIds: string[] = []; + const validProjectIds: string[] = []; + + const isOwnerOrManager = isOwner || isManager; + try { + for (const teamId of teamIds) { + const team = await prisma.team.findUnique({ + where: { + id: teamId, + }, + select: { + projectTeams: { + select: { + projectId: true, + }, + }, + }, + }); + + if (team) { + await prisma.teamUser.create({ + data: { + teamId, + userId, + role: isOwnerOrManager ? "admin" : "contributor", + }, + }); + + validTeamIds.push(teamId); + validProjectIds.push(...team.projectTeams.map((pt) => pt.projectId)); + } + } + + for (const projectId of validProjectIds) { + teamCache.revalidate({ id: projectId }); + } + + for (const teamId of validTeamIds) { + teamCache.revalidate({ id: teamId }); + } + + teamCache.revalidate({ userId, organizationId: invite.organizationId }); + projectCache.revalidate({ userId, organizationId: invite.organizationId }); + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } +}; diff --git a/apps/web/modules/auth/signup/types/invites.ts b/apps/web/modules/auth/signup/types/invites.ts new file mode 100644 index 0000000000..957e2c5ff2 --- /dev/null +++ b/apps/web/modules/auth/signup/types/invites.ts @@ -0,0 +1,7 @@ +import { Invite, User } from "@prisma/client"; + +export interface InviteWithCreator extends Pick { + creator: Pick; +} + +export interface CreateMembershipInvite extends Pick {} diff --git a/apps/web/modules/auth/verification-requested/page.tsx b/apps/web/modules/auth/verification-requested/page.tsx index bd1c5eef1b..fd3dcf6114 100644 --- a/apps/web/modules/auth/verification-requested/page.tsx +++ b/apps/web/modules/auth/verification-requested/page.tsx @@ -6,8 +6,9 @@ import { ZUserEmail } from "@formbricks/types/user"; export const VerificationRequestedPage = async ({ searchParams }) => { const t = await getTranslations(); + const { token } = await searchParams; try { - const email = getEmailFromEmailToken(searchParams.token); + const email = getEmailFromEmailToken(token); const parsedEmail = ZUserEmail.safeParse(email); if (parsedEmail.success) { return ( diff --git a/apps/web/modules/auth/verify/page.tsx b/apps/web/modules/auth/verify/page.tsx index 19319d2bf8..2975507889 100644 --- a/apps/web/modules/auth/verify/page.tsx +++ b/apps/web/modules/auth/verify/page.tsx @@ -4,10 +4,12 @@ import { getTranslations } from "next-intl/server"; export const VerifyPage = async ({ searchParams }) => { const t = await getTranslations(); - return searchParams && searchParams.token ? ( + const { token } = await searchParams; + + return token ? (

{t("auth.verify.verifying")}

- +
) : (

{t("auth.verify.no_token_provided")}

diff --git a/apps/web/modules/ee/billing/components/pricing-table.tsx b/apps/web/modules/ee/billing/components/pricing-table.tsx index 748a4cfb52..3e2eac2b6a 100644 --- a/apps/web/modules/ee/billing/components/pricing-table.tsx +++ b/apps/web/modules/ee/billing/components/pricing-table.tsx @@ -102,7 +102,6 @@ export const PricingTable = ({ throw new Error(t("common.something_went_wrong_please_try_again")); } } catch (err) { - console.log({ err }); toast.error(t("environments.settings.billing.unable_to_upgrade_plan")); } }; diff --git a/apps/web/modules/ee/contacts/actions.ts b/apps/web/modules/ee/contacts/actions.ts index d6e7322aad..2ff6d85d0c 100644 --- a/apps/web/modules/ee/contacts/actions.ts +++ b/apps/web/modules/ee/contacts/actions.ts @@ -45,12 +45,12 @@ export const getContactsAction = authenticatedActionClient return getContacts(parsedInput.environmentId, parsedInput.offset, parsedInput.searchValue); }); -const ZPersonDeleteAction = z.object({ +const ZContactDeleteAction = z.object({ contactId: ZId, }); export const deleteContactAction = authenticatedActionClient - .schema(ZPersonDeleteAction) + .schema(ZContactDeleteAction) .action(async ({ ctx, parsedInput }) => { const organizationId = await getOrganizationIdFromContactId(parsedInput.contactId); const projectId = await getProjectIdFromContactId(parsedInput.contactId); diff --git a/apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/lib/contact.ts b/apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/lib/contact.ts index 70a9c46384..324a73701b 100644 --- a/apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/lib/contact.ts +++ b/apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/lib/contact.ts @@ -35,7 +35,7 @@ export const getContactByUserIdWithAttributes = reactCache( return contact; }, - [`getContactByUserId-${environmentId}-${userId}-${JSON.stringify(updatedAttributes)}`], + [`getContactByUserIdWithAttributes-${environmentId}-${userId}-${JSON.stringify(updatedAttributes)}`], { tags: [ contactCache.tag.byEnvironmentIdAndUserId(environmentId, userId), diff --git a/apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/route.ts b/apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/route.ts index b526dfd89b..a71891c8d3 100644 --- a/apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/route.ts +++ b/apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/route.ts @@ -1,10 +1,10 @@ import { responses } from "@/app/lib/api/response"; import { transformErrorToDetails } from "@/app/lib/api/validator"; +import { updateAttributes } from "@/modules/ee/contacts/lib/attributes"; import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils"; import { NextRequest } from "next/server"; import { ResourceNotFoundError } from "@formbricks/types/errors"; import { ZJsContactsUpdateAttributeInput } from "@formbricks/types/js"; -import { updateAttributes } from "./lib/attributes"; import { getContactByUserIdWithAttributes } from "./lib/contact"; export const OPTIONS = async () => { @@ -74,36 +74,15 @@ export const PUT = async ( ); } - const { details: updateAttrDetails } = await updateAttributes( - contact.id, - userId, - environmentId, - updatedAttributes - ); - - // if userIdAttr or idAttr was in the payload, we need to inform the user that it was ignored - const details: Record = {}; - if (userIdAttr) { - details.userId = "updating userId is ignored as it is a reserved field and cannot be updated."; - } - - if (idAttr) { - details.id = "updating id is ignored as it is a reserved field and cannot be updated."; - } - - if (updateAttrDetails && Object.keys(updateAttrDetails).length > 0) { - Object.entries(updateAttrDetails).forEach(([key, value]) => { - details[key] = value; - }); - } + const { messages } = await updateAttributes(contact.id, userId, environmentId, updatedAttributes); return responses.successResponse( { changed: true, message: "The person was successfully updated.", - ...(Object.keys(details).length > 0 + ...(messages && messages.length > 0 ? { - details, + messages, } : {}), }, diff --git a/apps/web/modules/ee/contacts/api/client/[environmentId]/identify/contacts/[userId]/route.ts b/apps/web/modules/ee/contacts/api/client/[environmentId]/identify/contacts/[userId]/route.ts index e3f935a1f4..ea0bfaf3e2 100644 --- a/apps/web/modules/ee/contacts/api/client/[environmentId]/identify/contacts/[userId]/route.ts +++ b/apps/web/modules/ee/contacts/api/client/[environmentId]/identify/contacts/[userId]/route.ts @@ -4,7 +4,7 @@ import { contactCache } from "@/lib/cache/contact"; import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils"; import { NextRequest, userAgent } from "next/server"; import { ResourceNotFoundError } from "@formbricks/types/errors"; -import { ZJsPersonIdentifyInput } from "@formbricks/types/js"; +import { ZJsUserIdentifyInput } from "@formbricks/types/js"; import { getPersonState } from "./lib/personState"; export const OPTIONS = async (): Promise => { @@ -21,7 +21,7 @@ export const GET = async ( const { environmentId, userId } = params; // Validate input - const syncInputValidation = ZJsPersonIdentifyInput.safeParse({ + const syncInputValidation = ZJsUserIdentifyInput.safeParse({ environmentId, userId, }); diff --git a/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/contact.ts b/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/contact.ts new file mode 100644 index 0000000000..45d8af47c6 --- /dev/null +++ b/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/contact.ts @@ -0,0 +1,39 @@ +import { contactCache } from "@/lib/cache/contact"; +import { contactAttributeCache } from "@/lib/cache/contact-attribute"; +import { contactAttributeKeyCache } from "@/lib/cache/contact-attribute-key"; +import { cache as reactCache } from "react"; +import { prisma } from "@formbricks/database"; +import { cache } from "@formbricks/lib/cache"; + +export const getContactByUserIdWithAttributes = reactCache((environmentId: string, userId: string) => + cache( + async () => { + const contact = await prisma.contact.findFirst({ + where: { + environmentId, + attributes: { some: { attributeKey: { key: "userId", environmentId }, value: userId } }, + }, + select: { + id: true, + attributes: { + select: { attributeKey: { select: { key: true } }, value: true }, + }, + }, + }); + + if (!contact) { + return null; + } + + return contact; + }, + [`getContactByUserIdWithAttributes-${environmentId}-${userId}`], + { + tags: [ + contactCache.tag.byEnvironmentIdAndUserId(environmentId, userId), + contactAttributeCache.tag.byEnvironmentIdAndUserId(environmentId, userId), + contactAttributeKeyCache.tag.byEnvironmentId(environmentId), + ], + } + )() +); diff --git a/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/segments.ts b/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/segments.ts new file mode 100644 index 0000000000..7405244066 --- /dev/null +++ b/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/segments.ts @@ -0,0 +1,82 @@ +import { contactAttributeCache } from "@/lib/cache/contact-attribute"; +import { evaluateSegment } from "@/modules/ee/contacts/segments/lib/segments"; +import { Prisma } from "@prisma/client"; +import { cache as reactCache } from "react"; +import { prisma } from "@formbricks/database"; +import { cache } from "@formbricks/lib/cache"; +import { segmentCache } from "@formbricks/lib/cache/segment"; +import { validateInputs } from "@formbricks/lib/utils/validate"; +import { ZId, ZString } from "@formbricks/types/common"; +import { DatabaseError } from "@formbricks/types/errors"; +import { TBaseFilter } from "@formbricks/types/segment"; + +const getSegments = reactCache((environmentId: string) => + cache( + async () => { + try { + return prisma.segment.findMany({ + where: { environmentId }, + select: { id: true, filters: true }, + }); + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } + }, + [`getSegments-environmentId-${environmentId}`], + { + tags: [segmentCache.tag.byEnvironmentId(environmentId)], + } + )() +); + +export const getPersonSegmentIds = ( + environmentId: string, + contactId: string, + contactUserId: string, + attributes: Record, + deviceType: "phone" | "desktop" +): Promise => + cache( + async () => { + validateInputs([environmentId, ZId], [contactId, ZId], [contactUserId, ZString]); + + const segments = await getSegments(environmentId); + + // fast path; if there are no segments, return an empty array + if (!segments) { + return []; + } + + const personSegments: { id: string; filters: TBaseFilter[] }[] = []; + + for (const segment of segments) { + const isIncluded = await evaluateSegment( + { + attributes, + deviceType, + environmentId, + contactId: contactId, + userId: contactUserId, + }, + segment.filters + ); + + if (isIncluded) { + personSegments.push(segment); + } + } + + return personSegments.map((segment) => segment.id); + }, + [`getPersonSegmentIds-${environmentId}-${contactId}-${deviceType}`], + { + tags: [ + segmentCache.tag.byEnvironmentId(environmentId), + contactAttributeCache.tag.byContactId(contactId), + ], + } + )(); diff --git a/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/update-user.ts b/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/update-user.ts new file mode 100644 index 0000000000..13345b92f1 --- /dev/null +++ b/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/update-user.ts @@ -0,0 +1,129 @@ +import { contactCache } from "@/lib/cache/contact"; +import { updateAttributes } from "@/modules/ee/contacts/lib/attributes"; +import { prisma } from "@formbricks/database"; +import { getEnvironment } from "@formbricks/lib/environment/service"; +import { ResourceNotFoundError } from "@formbricks/types/errors"; +import { TJsPersonState } from "@formbricks/types/js"; +import { getContactByUserIdWithAttributes } from "./contact"; +import { getUserState } from "./user-state"; + +export const updateUser = async ( + environmentId: string, + userId: string, + device: "phone" | "desktop", + attributes?: Record +): Promise<{ state: TJsPersonState; messages?: string[] }> => { + const environment = await getEnvironment(environmentId); + + if (!environment) { + throw new ResourceNotFoundError(`environment`, environmentId); + } + + let contact = await getContactByUserIdWithAttributes(environmentId, userId); + + if (!contact) { + contact = await prisma.contact.create({ + data: { + environment: { + connect: { + id: environmentId, + }, + }, + attributes: { + create: [ + { + attributeKey: { + connect: { key_environmentId: { key: "userId", environmentId } }, + }, + value: userId, + }, + ], + }, + }, + select: { + id: true, + attributes: { + select: { attributeKey: { select: { key: true } }, value: true }, + }, + }, + }); + + contactCache.revalidate({ + environmentId, + userId, + id: contact.id, + }); + } + + let contactAttributes = contact.attributes.reduce( + (acc, ctx) => { + acc[ctx.attributeKey.key] = ctx.value; + return acc; + }, + {} as Record + ); + + // update the contact attributes if needed: + let messages: string[] = []; + let language = contactAttributes.language; + + if (attributes && Object.keys(attributes).length > 0) { + let shouldUpdate = false; + const oldAttributes = contact.attributes.reduce( + (acc, ctx) => { + acc[ctx.attributeKey.key] = ctx.value; + return acc; + }, + {} as Record + ); + + for (const [key, value] of Object.entries(attributes)) { + if (value !== oldAttributes[key]) { + shouldUpdate = true; + break; + } + } + + if (shouldUpdate) { + const { success, messages: updateAttrMessages } = await updateAttributes( + contact.id, + userId, + environmentId, + attributes + ); + + messages = updateAttrMessages ?? []; + + // If the attributes update was successful and the language attribute was provided, set the language + if (success) { + contactAttributes = { + ...contactAttributes, + ...attributes, + }; + + if (attributes.language) { + language = attributes.language; + } + } + } + } + + const userState = await getUserState({ + environmentId, + userId, + contactId: contact.id, + attributes: contactAttributes, + device, + }); + + return { + state: { + data: { + ...userState, + language, + }, + expiresAt: new Date(Date.now() + 1000 * 60 * 30), // 30 minutes + }, + messages, + }; +}; diff --git a/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/user-state.ts b/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/user-state.ts new file mode 100644 index 0000000000..df7b5e8c5c --- /dev/null +++ b/apps/web/modules/ee/contacts/api/client/[environmentId]/user/lib/user-state.ts @@ -0,0 +1,89 @@ +import { contactCache } from "@/lib/cache/contact"; +import { contactAttributeCache } from "@/lib/cache/contact-attribute"; +import { prisma } from "@formbricks/database"; +import { cache } from "@formbricks/lib/cache"; +import { segmentCache } from "@formbricks/lib/cache/segment"; +import { displayCache } from "@formbricks/lib/display/cache"; +import { environmentCache } from "@formbricks/lib/environment/cache"; +import { organizationCache } from "@formbricks/lib/organization/cache"; +import { responseCache } from "@formbricks/lib/response/cache"; +import { TJsPersonState } from "@formbricks/types/js"; +import { getPersonSegmentIds } from "./segments"; + +/** + * + * @param environmentId - The environment id + * @param userId - The user id + * @param device - The device type + * @param attributes - The contact attributes + * @returns The person state + * @throws {ValidationError} - If the input is invalid + * @throws {ResourceNotFoundError} - If the environment or organization is not found + */ +export const getUserState = async ({ + environmentId, + userId, + contactId, + device, + attributes, +}: { + environmentId: string; + userId: string; + contactId: string; + device: "phone" | "desktop"; + attributes: Record; +}): Promise => + cache( + async () => { + const contactResponses = await prisma.response.findMany({ + where: { + contactId, + }, + select: { + surveyId: true, + }, + }); + + const contactDisplays = await prisma.display.findMany({ + where: { + contactId, + }, + select: { + surveyId: true, + createdAt: true, + }, + }); + + const segments = await getPersonSegmentIds(environmentId, contactId, userId, attributes, device); + + // If the person exists, return the persons's state + const userState: TJsPersonState["data"] = { + userId, + segments, + displays: + contactDisplays?.map((display) => ({ + surveyId: display.surveyId, + createdAt: display.createdAt, + })) ?? [], + responses: contactResponses?.map((response) => response.surveyId) ?? [], + lastDisplayAt: + contactDisplays.length > 0 + ? contactDisplays.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())[0].createdAt + : null, + }; + + return userState; + }, + [`personState-${environmentId}-${userId}-${device}`], + { + tags: [ + environmentCache.tag.byId(environmentId), + organizationCache.tag.byEnvironmentId(environmentId), + contactCache.tag.byEnvironmentIdAndUserId(environmentId, userId), + contactAttributeCache.tag.byEnvironmentIdAndUserId(environmentId, userId), + displayCache.tag.byEnvironmentIdAndUserId(environmentId, userId), + responseCache.tag.byEnvironmentIdAndUserId(environmentId, userId), + segmentCache.tag.byEnvironmentId(environmentId), + ], + } + )(); diff --git a/apps/web/modules/ee/contacts/api/client/[environmentId]/user/route.ts b/apps/web/modules/ee/contacts/api/client/[environmentId]/user/route.ts new file mode 100644 index 0000000000..f1023d61e3 --- /dev/null +++ b/apps/web/modules/ee/contacts/api/client/[environmentId]/user/route.ts @@ -0,0 +1,92 @@ +import { responses } from "@/app/lib/api/response"; +import { transformErrorToDetails } from "@/app/lib/api/validator"; +import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils"; +import { NextRequest, userAgent } from "next/server"; +import { TContactAttributes } from "@formbricks/types/contact-attribute"; +import { ResourceNotFoundError } from "@formbricks/types/errors"; +import { TJsPersonState, ZJsUserIdentifyInput, ZJsUserUpdateInput } from "@formbricks/types/js"; +import { updateUser } from "./lib/update-user"; + +export const OPTIONS = async (): Promise => { + return responses.successResponse({}, true); +}; + +export const POST = async ( + request: NextRequest, + props: { params: Promise<{ environmentId: string }> } +): Promise => { + const params = await props.params; + + try { + const { environmentId } = params; + const jsonInput = await request.json(); + + // Validate input + const syncInputValidation = ZJsUserIdentifyInput.pick({ environmentId: true }).safeParse({ + environmentId, + }); + + if (!syncInputValidation.success) { + return responses.badRequestResponse( + "Fields are missing or incorrectly formatted", + transformErrorToDetails(syncInputValidation.error), + true + ); + } + + const parsedInput = ZJsUserUpdateInput.safeParse(jsonInput); + if (!parsedInput.success) { + return responses.badRequestResponse( + "Fields are missing or incorrectly formatted", + transformErrorToDetails(parsedInput.error), + true + ); + } + + const { userId, attributes } = parsedInput.data; + + const isContactsEnabled = await getIsContactsEnabled(); + if (!isContactsEnabled) { + return responses.forbiddenResponse("User identification is only available for enterprise users.", true); + } + + let attributeUpdatesToSend: TContactAttributes | null = null; + if (attributes) { + // remove userId and id from attributes + const { userId: userIdAttr, id: idAttr, ...updatedAttributes } = attributes; + attributeUpdatesToSend = updatedAttributes; + } + + const { device } = userAgent(request); + const deviceType = device ? "phone" : "desktop"; + + try { + const { state: userState, messages } = await updateUser( + environmentId, + userId, + deviceType, + attributeUpdatesToSend ?? undefined + ); + + let responseJson: { state: TJsPersonState; messages?: string[] } = { + state: userState, + }; + + if (messages && messages.length > 0) { + responseJson.messages = messages; + } + + return responses.successResponse(responseJson, true); + } catch (err) { + if (err instanceof ResourceNotFoundError) { + return responses.notFoundResponse(err.resourceType, err.resourceId); + } + + console.error(err); + return responses.internalServerErrorResponse(err.message ?? "Unable to fetch person state", true); + } + } catch (error) { + console.error(error); + return responses.internalServerErrorResponse(`Unable to complete response: ${error.message}`, true); + } +}; diff --git a/apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/lib/attributes.ts b/apps/web/modules/ee/contacts/lib/attributes.ts similarity index 75% rename from apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/lib/attributes.ts rename to apps/web/modules/ee/contacts/lib/attributes.ts index 677a126fde..1e110e8056 100644 --- a/apps/web/modules/ee/contacts/api/client/[environmentId]/contacts/[userId]/attributes/lib/attributes.ts +++ b/apps/web/modules/ee/contacts/lib/attributes.ts @@ -1,44 +1,18 @@ -import "server-only"; import { contactAttributeCache } from "@/lib/cache/contact-attribute"; import { contactAttributeKeyCache } from "@/lib/cache/contact-attribute-key"; -import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; -import { cache } from "@formbricks/lib/cache"; import { MAX_ATTRIBUTE_CLASSES_PER_ENVIRONMENT } from "@formbricks/lib/constants"; import { validateInputs } from "@formbricks/lib/utils/validate"; import { ZId, ZString } from "@formbricks/types/common"; import { TContactAttributes, ZContactAttributes } from "@formbricks/types/contact-attribute"; - -export const getContactAttributeKeys = reactCache((environmentId: string) => - cache( - async () => { - validateInputs([environmentId, ZId]); - - const contactAttributes = await prisma.contactAttributeKey.findMany({ - where: { - environmentId, - }, - select: { - id: true, - key: true, - }, - }); - - return contactAttributes; - }, - [`getContactAttributeKeys-attributes-api-${environmentId}`], - { - tags: [contactAttributeKeyCache.tag.byEnvironmentId(environmentId)], - } - )() -); +import { getContactAttributeKeys } from "./contacts"; export const updateAttributes = async ( contactId: string, userId: string, environmentId: string, contactAttributesParam: TContactAttributes -): Promise<{ success: boolean; details?: Record }> => { +): Promise<{ success: boolean; messages?: string[] }> => { validateInputs( [contactId, ZId], [userId, ZString], @@ -96,9 +70,9 @@ export const updateAttributes = async ( } ); - let details: Record = emailExists - ? { email: "The email already exists for this environment and was not updated." } - : {}; + let messages: string[] = emailExists + ? ["The email already exists for this environment and was not updated."] + : []; // First, update all existing attributes if (existingAttributes.length > 0) { @@ -122,9 +96,9 @@ export const updateAttributes = async ( ); // Revalidate cache for existing attributes - existingAttributes.map(({ key }) => - contactAttributeCache.revalidate({ environmentId, contactId, userId, key }) - ); + for (const attribute of existingAttributes) { + contactAttributeCache.revalidate({ environmentId, contactId, userId, key: attribute.key }); + } } // Then, try to create new attributes if any exist @@ -133,10 +107,9 @@ export const updateAttributes = async ( if (totalAttributeClassesLength > MAX_ATTRIBUTE_CLASSES_PER_ENVIRONMENT) { // Add warning to details about skipped attributes - details = { - ...details, - newAttributes: `Could not create ${newAttributes.length} new attribute(s) as it would exceed the maximum limit of ${MAX_ATTRIBUTE_CLASSES_PER_ENVIRONMENT} attribute classes. Existing attributes were updated successfully.`, - }; + messages.push( + `Could not create ${newAttributes.length} new attribute(s) as it would exceed the maximum limit of ${MAX_ATTRIBUTE_CLASSES_PER_ENVIRONMENT} attribute classes. Existing attributes were updated successfully.` + ); } else { // Create new attributes since we're under the limit await prisma.$transaction( @@ -155,16 +128,17 @@ export const updateAttributes = async ( ); // Batch revalidate caches for new attributes - newAttributes.forEach(({ key }) => { - contactAttributeKeyCache.revalidate({ environmentId, key }); - contactAttributeCache.revalidate({ environmentId, contactId, userId, key }); - }); + for (const attribute of newAttributes) { + contactAttributeKeyCache.revalidate({ environmentId, key: attribute.key }); + contactAttributeCache.revalidate({ environmentId, contactId, userId, key: attribute.key }); + } + contactAttributeKeyCache.revalidate({ environmentId }); } } return { success: true, - ...(Object.keys(details).length > 0 ? { details } : {}), + messages, }; }; diff --git a/apps/web/modules/ee/contacts/lib/contacts.ts b/apps/web/modules/ee/contacts/lib/contacts.ts index 6ddfbe8be6..391cad0bcc 100644 --- a/apps/web/modules/ee/contacts/lib/contacts.ts +++ b/apps/web/modules/ee/contacts/lib/contacts.ts @@ -148,9 +148,12 @@ export const deleteContact = async (contactId: string): Promise select: selectContact, }); + const contactUserId = contact.attributes.find((attr) => attr.attributeKey.key === "userId")?.value; + contactCache.revalidate({ id: contact.id, environmentId: contact.environmentId, + userId: contactUserId, }); return contact; diff --git a/apps/web/modules/ee/role-management/actions.ts b/apps/web/modules/ee/role-management/actions.ts index 92a2f71cc7..8142652064 100644 --- a/apps/web/modules/ee/role-management/actions.ts +++ b/apps/web/modules/ee/role-management/actions.ts @@ -3,14 +3,14 @@ import { authenticatedActionClient } from "@/lib/utils/action-client"; import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware"; import { getRoleManagementPermission } from "@/modules/ee/license-check/lib/utils"; +import { updateInvite } from "@/modules/ee/role-management/lib/invite"; import { updateMembership } from "@/modules/ee/role-management/lib/membership"; +import { ZInviteUpdateInput } from "@/modules/ee/role-management/types/invites"; import { z } from "zod"; import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants"; -import { updateInvite } from "@formbricks/lib/invite/service"; import { getOrganization } from "@formbricks/lib/organization/service"; import { ZId, ZUuid } from "@formbricks/types/common"; import { OperationNotAllowedError, ValidationError } from "@formbricks/types/errors"; -import { ZInviteUpdateInput } from "@formbricks/types/invites"; import { ZMembershipUpdateInput } from "@formbricks/types/memberships"; export const checkRoleManagementPermission = async (organizationId: string) => { diff --git a/apps/web/modules/ee/role-management/lib/invite.ts b/apps/web/modules/ee/role-management/lib/invite.ts new file mode 100644 index 0000000000..4b5f0124ef --- /dev/null +++ b/apps/web/modules/ee/role-management/lib/invite.ts @@ -0,0 +1,31 @@ +import { inviteCache } from "@/lib/cache/invite"; +import { type TInviteUpdateInput } from "@/modules/ee/role-management/types/invites"; +import { Prisma } from "@prisma/client"; +import { prisma } from "@formbricks/database"; +import { ResourceNotFoundError } from "@formbricks/types/errors"; + +export const updateInvite = async (inviteId: string, data: TInviteUpdateInput): Promise => { + try { + const invite = await prisma.invite.update({ + where: { id: inviteId }, + data, + }); + + if (invite === null) { + throw new ResourceNotFoundError("Invite", inviteId); + } + + inviteCache.revalidate({ + id: invite.id, + organizationId: invite.organizationId, + }); + + return true; + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") { + throw new ResourceNotFoundError("Invite", inviteId); + } else { + throw error; // Re-throw any other errors + } + } +}; diff --git a/apps/web/modules/ee/role-management/types/invites.ts b/apps/web/modules/ee/role-management/types/invites.ts new file mode 100644 index 0000000000..92c2772dbe --- /dev/null +++ b/apps/web/modules/ee/role-management/types/invites.ts @@ -0,0 +1,8 @@ +import { z } from "zod"; +import { ZInvite } from "@formbricks/database/zod/invites"; + +export const ZInviteUpdateInput = ZInvite.pick({ + role: true, +}); + +export type TInviteUpdateInput = z.infer; diff --git a/apps/web/modules/ee/whitelabel/email-customization/actions.ts b/apps/web/modules/ee/whitelabel/email-customization/actions.ts index 3d6d5eb95b..89e5c7bca7 100644 --- a/apps/web/modules/ee/whitelabel/email-customization/actions.ts +++ b/apps/web/modules/ee/whitelabel/email-customization/actions.ts @@ -90,7 +90,6 @@ export const sendTestEmailAction = authenticatedActionClient await sendEmailCustomizationPreviewEmail( ctx.user.email, - "Formbricks Email Customization Preview", ctx.user.name, ctx.user.locale, organization?.whitelabel?.logoUrl || "" diff --git a/apps/web/modules/email/components/email-footer.tsx b/apps/web/modules/email/components/email-footer.tsx index 27f93026be..400c661c0a 100644 --- a/apps/web/modules/email/components/email-footer.tsx +++ b/apps/web/modules/email/components/email-footer.tsx @@ -1,10 +1,15 @@ +import { translateEmailText } from "@/modules/email/lib/utils"; import { Text } from "@react-email/components"; -export function EmailFooter(): React.JSX.Element { +interface EmailFooterProps { + locale: string; +} + +export function EmailFooter({ locale }: EmailFooterProps): React.JSX.Element { return ( - Have a great day! -
The Formbricks Team + {translateEmailText("email_footer_text_1", locale)} +
{translateEmailText("email_footer_text_2", locale)}
); } diff --git a/apps/web/modules/email/components/email-template.tsx b/apps/web/modules/email/components/email-template.tsx index c32b0aedf0..26d3aca15f 100644 --- a/apps/web/modules/email/components/email-template.tsx +++ b/apps/web/modules/email/components/email-template.tsx @@ -1,3 +1,4 @@ +import { translateEmailText } from "@/modules/email/lib/utils"; import { Body, Container, Html, Img, Link, Section, Tailwind, Text } from "@react-email/components"; import { IMPRINT_ADDRESS, IMPRINT_URL, PRIVACY_URL } from "@formbricks/lib/constants"; @@ -8,9 +9,10 @@ const logoLink = "https://formbricks.com?utm_source=email_header&utm_medium=emai interface EmailTemplateProps { children: React.ReactNode; logoUrl?: string; + locale: string; } -export function EmailTemplate({ children, logoUrl }: EmailTemplateProps): React.JSX.Element { +export function EmailTemplate({ children, logoUrl, locale }: EmailTemplateProps): React.JSX.Element { const isDefaultLogo = !logoUrl || logoUrl === fbLogoUrl; return ( @@ -35,21 +37,22 @@ export function EmailTemplate({ children, logoUrl }: EmailTemplateProps): React.
- This email was sent via Formbricks. + + {translateEmailText("email_template_text_1", locale)} + {IMPRINT_ADDRESS && ( {IMPRINT_ADDRESS} )} {IMPRINT_URL && ( - Imprint{" "} + {translateEmailText("imprint", locale)} )} {IMPRINT_URL && PRIVACY_URL && "•"} {PRIVACY_URL && ( - {" "} - Privacy Policy + {translateEmailText("privacy_policy", locale)} )} diff --git a/apps/web/modules/email/components/preview-email-template.tsx b/apps/web/modules/email/components/preview-email-template.tsx index 3d749f4933..851efea501 100644 --- a/apps/web/modules/email/components/preview-email-template.tsx +++ b/apps/web/modules/email/components/preview-email-template.tsx @@ -18,28 +18,34 @@ import { getLocalizedValue } from "@formbricks/lib/i18n/utils"; import { COLOR_DEFAULTS } from "@formbricks/lib/styling/constants"; import { isLight, mixColor } from "@formbricks/lib/utils/colors"; import { type TSurvey, TSurveyQuestionTypeEnum, type TSurveyStyling } from "@formbricks/types/surveys/types"; -import { getNPSOptionColor, getRatingNumberOptionColor } from "../lib/utils"; +import { getNPSOptionColor, getRatingNumberOptionColor, translateEmailText } from "../lib/utils"; interface PreviewEmailTemplateProps { survey: TSurvey; surveyUrl: string; styling: TSurveyStyling; + locale: string; } export const getPreviewEmailTemplateHtml = async ( survey: TSurvey, surveyUrl: string, - styling: TSurveyStyling + styling: TSurveyStyling, + locale: string ): Promise => { - return render(, { - pretty: true, - }); + return render( + , + { + pretty: true, + } + ); }; export function PreviewEmailTemplate({ survey, surveyUrl, styling, + locale, }: PreviewEmailTemplateProps): React.JSX.Element { const url = `${surveyUrl}?preview=true`; const urlWithPrefilling = `${surveyUrl}?preview=true&skipPrefilled=true&`; @@ -87,7 +93,7 @@ export function PreviewEmailTemplate({ - Reject + {translateEmailText("reject", locale)} )} - Accept + {translateEmailText("accept", locale)} @@ -365,17 +371,17 @@ export function PreviewEmailTemplate({ - {getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)} + {getLocalizedValue(firstQuestion.headline, defaultLanguageCode)} - You have been invited to schedule a meet via cal.com. + {getLocalizedValue(firstQuestion.subheader, defaultLanguageCode)} - Schedule your meeting + {translateEmailText("schedule_your_meeting", defaultLanguageCode)} @@ -392,7 +398,9 @@ export function PreviewEmailTemplate({
- Select a date + + {translateEmailText("select_a_date", defaultLanguageCode)} +
@@ -478,7 +486,9 @@ export function PreviewEmailTemplate({
- Click or drag to upload files. + + {translateEmailText("click_or_drag_to_upload_files", defaultLanguageCode)} +
diff --git a/apps/web/modules/email/emails/auth/forgot-password-email.tsx b/apps/web/modules/email/emails/auth/forgot-password-email.tsx index 37c845fd16..80263c62d9 100644 --- a/apps/web/modules/email/emails/auth/forgot-password-email.tsx +++ b/apps/web/modules/email/emails/auth/forgot-password-email.tsx @@ -12,7 +12,7 @@ interface ForgotPasswordEmailProps { export function ForgotPasswordEmail({ verifyLink, locale }: ForgotPasswordEmailProps): React.JSX.Element { return ( - + {translateEmailText("forgot_password_email_heading", locale)} {translateEmailText("forgot_password_email_text", locale)} @@ -24,7 +24,7 @@ export function ForgotPasswordEmail({ verifyLink, locale }: ForgotPasswordEmailP {translateEmailText("forgot_password_email_link_valid_for_24_hours", locale)} {translateEmailText("forgot_password_email_did_not_request", locale)} - + ); diff --git a/apps/web/modules/email/emails/auth/password-reset-notify-email.tsx b/apps/web/modules/email/emails/auth/password-reset-notify-email.tsx index ec0eeb720a..51f2840738 100644 --- a/apps/web/modules/email/emails/auth/password-reset-notify-email.tsx +++ b/apps/web/modules/email/emails/auth/password-reset-notify-email.tsx @@ -10,11 +10,11 @@ interface PasswordResetNotifyEmailProps { export function PasswordResetNotifyEmail({ locale }: PasswordResetNotifyEmailProps): React.JSX.Element { return ( - + {translateEmailText("password_changed_email_heading", locale)} {translateEmailText("password_changed_email_text", locale)} - + ); diff --git a/apps/web/modules/email/emails/auth/verification-email.tsx b/apps/web/modules/email/emails/auth/verification-email.tsx index 0da20d8c62..2967e4238d 100644 --- a/apps/web/modules/email/emails/auth/verification-email.tsx +++ b/apps/web/modules/email/emails/auth/verification-email.tsx @@ -17,7 +17,7 @@ export function VerificationEmail({ locale, }: VerificationEmailProps): React.JSX.Element { return ( - + {translateEmailText("verification_email_heading", locale)} {translateEmailText("verification_email_text", locale)} @@ -38,7 +38,7 @@ export function VerificationEmail({ {translateEmailText("verification_email_request_new_verification", locale)} - + ); diff --git a/apps/web/modules/email/emails/general/email-customization-preview-email.tsx b/apps/web/modules/email/emails/general/email-customization-preview-email.tsx index 9084604019..f230dae8d6 100644 --- a/apps/web/modules/email/emails/general/email-customization-preview-email.tsx +++ b/apps/web/modules/email/emails/general/email-customization-preview-email.tsx @@ -15,7 +15,7 @@ export function EmailCustomizationPreviewEmail({ logoUrl, }: EmailCustomizationPreviewEmailProps): React.JSX.Element { return ( - + {translateEmailText("email_customization_preview_email_heading", locale, { diff --git a/apps/web/modules/email/emails/invite/invite-accepted-email.tsx b/apps/web/modules/email/emails/invite/invite-accepted-email.tsx index fa6828fd7e..e8505d5f13 100644 --- a/apps/web/modules/email/emails/invite/invite-accepted-email.tsx +++ b/apps/web/modules/email/emails/invite/invite-accepted-email.tsx @@ -16,7 +16,7 @@ export function InviteAcceptedEmail({ locale, }: InviteAcceptedEmailProps): React.JSX.Element { return ( - + {translateEmailText("invite_accepted_email_heading", locale)} {inviterName}, @@ -25,7 +25,7 @@ export function InviteAcceptedEmail({ {translateEmailText("invite_accepted_email_text_par1", locale)} {inviteeName}{" "} {translateEmailText("invite_accepted_email_text_par2", locale)} - + ); diff --git a/apps/web/modules/email/emails/invite/invite-email.tsx b/apps/web/modules/email/emails/invite/invite-email.tsx index 8015a78bec..cc66066a65 100644 --- a/apps/web/modules/email/emails/invite/invite-email.tsx +++ b/apps/web/modules/email/emails/invite/invite-email.tsx @@ -19,7 +19,7 @@ export function InviteEmail({ locale, }: InviteEmailProps): React.JSX.Element { return ( - + {translateEmailText("invite_email_heading", locale)} {inviteeName}, @@ -28,8 +28,8 @@ export function InviteEmail({ {translateEmailText("invite_email_text_par1", locale)} {inviterName}{" "} {translateEmailText("invite_email_text_par2", locale)} - - + + ); diff --git a/apps/web/modules/email/emails/invite/onboarding-invite-email.tsx b/apps/web/modules/email/emails/invite/onboarding-invite-email.tsx index fc8b7a6b7c..0948958140 100644 --- a/apps/web/modules/email/emails/invite/onboarding-invite-email.tsx +++ b/apps/web/modules/email/emails/invite/onboarding-invite-email.tsx @@ -20,7 +20,7 @@ export function OnboardingInviteEmail({ inviteeName, }: OnboardingInviteEmailProps): React.JSX.Element { return ( - + {translateEmailText("onboarding_invite_email_heading", locale)} {inviteeName} 👋 @@ -34,8 +34,11 @@ export function OnboardingInviteEmail({
  • {translateEmailText("onboarding_invite_email_connect_formbricks", locale)}
  • {translateEmailText("onboarding_invite_email_done", locale)} ✅
  • - - + +
    ); diff --git a/apps/web/modules/email/emails/survey/embed-survey-preview-email.tsx b/apps/web/modules/email/emails/survey/embed-survey-preview-email.tsx index 15b60922c2..f572900f08 100644 --- a/apps/web/modules/email/emails/survey/embed-survey-preview-email.tsx +++ b/apps/web/modules/email/emails/survey/embed-survey-preview-email.tsx @@ -17,7 +17,7 @@ export function EmbedSurveyPreviewEmail({ logoUrl, }: EmbedSurveyPreviewEmailProps): React.JSX.Element { return ( - + {translateEmailText("embed_survey_preview_email_heading", locale)} {translateEmailText("embed_survey_preview_email_text", locale)} diff --git a/apps/web/modules/email/emails/survey/link-survey-email.tsx b/apps/web/modules/email/emails/survey/link-survey-email.tsx index 37e0238692..43d65f977d 100644 --- a/apps/web/modules/email/emails/survey/link-survey-email.tsx +++ b/apps/web/modules/email/emails/survey/link-survey-email.tsx @@ -19,7 +19,7 @@ export function LinkSurveyEmail({ logoUrl, }: LinkSurveyEmailProps): React.JSX.Element { return ( - + {translateEmailText("verification_email_hey", locale)} {translateEmailText("verification_email_thanks", locale)} @@ -28,7 +28,7 @@ export function LinkSurveyEmail({ {translateEmailText("verification_email_survey_name", locale)}: {surveyName} - + ); diff --git a/apps/web/modules/email/emails/survey/response-finished-email.tsx b/apps/web/modules/email/emails/survey/response-finished-email.tsx index 46bd9392ad..2d48e394fc 100644 --- a/apps/web/modules/email/emails/survey/response-finished-email.tsx +++ b/apps/web/modules/email/emails/survey/response-finished-email.tsx @@ -93,7 +93,7 @@ export function ResponseFinishedEmail({ const questions = getQuestionResponseMapping(survey, response); return ( - + diff --git a/apps/web/modules/email/emails/weekly-summary/weekly-summary-notification-email.tsx b/apps/web/modules/email/emails/weekly-summary/weekly-summary-notification-email.tsx index c7987c8a30..85069bf732 100644 --- a/apps/web/modules/email/emails/weekly-summary/weekly-summary-notification-email.tsx +++ b/apps/web/modules/email/emails/weekly-summary/weekly-summary-notification-email.tsx @@ -24,7 +24,7 @@ export function WeeklySummaryNotificationEmail({ locale, }: WeeklySummaryNotificationEmailProps): React.JSX.Element { return ( - + { - return `${projectName} User Insights - Last Week by Formbricks`; +const getEmailSubject = (projectName: string, locale: string): string => { + return translateEmailText("weekly_summary_email_subject", locale, { + projectName, + }); }; export const sendEmail = async (emailData: SendEmailDataProps): Promise => { @@ -117,7 +119,7 @@ export const sendForgotPasswordEmail = async (user: { const html = await render(ForgotPasswordEmail({ verifyLink, locale: user.locale })); return await sendEmail({ to: user.email, - subject: "Reset your Formbricks password", + subject: translateEmailText("forgot_password_email_subject", user.locale), html, }); }; @@ -129,7 +131,7 @@ export const sendPasswordResetNotifyEmail = async (user: { const html = await render(PasswordResetNotifyEmail({ locale: user.locale })); return await sendEmail({ to: user.email, - subject: "Your Formbricks password has been changed", + subject: translateEmailText("password_reset_notify_email_subject", user.locale), html, }); }; @@ -155,14 +157,16 @@ export const sendInviteMemberEmail = async ( ); return await sendEmail({ to: email, - subject: `${inviterName} needs a hand setting up Formbricks. Can you help out?`, + subject: translateEmailText("onboarding_invite_email_subject", locale, { + inviterName, + }), html, }); } else { const html = await render(InviteEmail({ inviteeName, inviterName, verifyLink, locale })); return await sendEmail({ to: email, - subject: `You're invited to collaborate on Formbricks!`, + subject: translateEmailText("invite_member_email_subject", locale), html, }); } @@ -177,7 +181,7 @@ export const sendInviteAcceptedEmail = async ( const html = await render(InviteAcceptedEmail({ inviteeName, inviterName, locale })); await sendEmail({ to: email, - subject: `You've got a new organization member!`, + subject: translateEmailText("invite_accepted_email_subject", locale), html, }); }; @@ -212,8 +216,13 @@ export const sendResponseFinishedEmail = async ( await sendEmail({ to: email, subject: personEmail - ? `${personEmail} just completed your ${survey.name} survey ✅` - : `A response for ${survey.name} was completed ✅`, + ? translateEmailText("response_finished_email_subject_with_email", locale, { + personEmail, + surveyName: survey.name, + }) + : translateEmailText("response_finished_email_subject", locale, { + surveyName: survey.name, + }), replyTo: personEmail?.toString() ?? MAIL_FROM, html, }); @@ -221,7 +230,6 @@ export const sendResponseFinishedEmail = async ( export const sendEmbedSurveyPreviewEmail = async ( to: string, - subject: string, innerHtml: string, environmentId: string, locale: string, @@ -230,14 +238,13 @@ export const sendEmbedSurveyPreviewEmail = async ( const html = await render(EmbedSurveyPreviewEmail({ html: innerHtml, environmentId, locale, logoUrl })); return await sendEmail({ to, - subject, + subject: translateEmailText("embed_survey_preview_email_subject", locale), html, }); }; export const sendEmailCustomizationPreviewEmail = async ( to: string, - subject: string, userName: string, locale: string, logoUrl?: string @@ -246,7 +253,7 @@ export const sendEmailCustomizationPreviewEmail = async ( return await sendEmail({ to, - subject, + subject: translateEmailText("email_customization_preview_email_subject", locale), html: emailHtmlBody, }); }; @@ -270,7 +277,7 @@ export const sendLinkSurveyToVerifiedEmail = async (data: TLinkSurveyEmailData): const html = await render(LinkSurveyEmail({ surveyName, surveyLink, locale, logoUrl })); return await sendEmail({ to: data.email, - subject: "Your survey is ready to be filled out.", + subject: translateEmailText("verified_link_survey_email_subject", locale), html, }); }; @@ -302,7 +309,7 @@ export const sendWeeklySummaryNotificationEmail = async ( ); await sendEmail({ to: email, - subject: getEmailSubject(notificationData.projectName), + subject: getEmailSubject(notificationData.projectName, locale), html, }); }; @@ -334,7 +341,7 @@ export const sendNoLiveSurveyNotificationEmail = async ( ); await sendEmail({ to: email, - subject: getEmailSubject(notificationData.projectName), + subject: getEmailSubject(notificationData.projectName, locale), html, }); }; diff --git a/apps/web/modules/organization/settings/teams/actions.ts b/apps/web/modules/organization/settings/teams/actions.ts index e09ecee678..e0ad1e7368 100644 --- a/apps/web/modules/organization/settings/teams/actions.ts +++ b/apps/web/modules/organization/settings/teams/actions.ts @@ -14,13 +14,13 @@ import { import { OrganizationRole } from "@prisma/client"; import { z } from "zod"; import { INVITE_DISABLED, IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants"; -import { deleteInvite, getInvite, inviteUser, resendInvite } from "@formbricks/lib/invite/service"; import { createInviteToken } from "@formbricks/lib/jwt"; import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service"; import { getAccessFlags } from "@formbricks/lib/membership/utils"; import { ZId, ZUuid } from "@formbricks/types/common"; import { AuthenticationError, OperationNotAllowedError, ValidationError } from "@formbricks/types/errors"; import { ZOrganizationRole } from "@formbricks/types/memberships"; +import { deleteInvite, getInvite, inviteUser, resendInvite } from "./lib/invite"; const ZDeleteInviteAction = z.object({ inviteId: ZUuid, @@ -206,7 +206,7 @@ export const inviteUserAction = authenticatedActionClient await checkRoleManagementPermission(parsedInput.organizationId); } - const invite = await inviteUser({ + const inviteId = await inviteUser({ organizationId: parsedInput.organizationId, invitee: { email: parsedInput.email, @@ -217,9 +217,9 @@ export const inviteUserAction = authenticatedActionClient currentUserId: ctx.user.id, }); - if (invite) { + if (inviteId) { await sendInviteMemberEmail( - invite.id, + inviteId, parsedInput.email, ctx.user.name ?? "", parsedInput.name ?? "", @@ -229,7 +229,7 @@ export const inviteUserAction = authenticatedActionClient ); } - return invite; + return inviteId; }); const ZLeaveOrganizationAction = z.object({ diff --git a/apps/web/modules/organization/settings/teams/components/edit-memberships/edit-memberships.tsx b/apps/web/modules/organization/settings/teams/components/edit-memberships/edit-memberships.tsx index 19abec9a44..e6bf66ded3 100644 --- a/apps/web/modules/organization/settings/teams/components/edit-memberships/edit-memberships.tsx +++ b/apps/web/modules/organization/settings/teams/components/edit-memberships/edit-memberships.tsx @@ -1,8 +1,8 @@ import { MembersInfo } from "@/modules/organization/settings/teams/components/edit-memberships/members-info"; +import { getInvitesByOrganizationId } from "@/modules/organization/settings/teams/lib/invite"; import { getMembershipByOrganizationId } from "@/modules/organization/settings/teams/lib/membership"; import { getTranslations } from "next-intl/server"; import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants"; -import { getInvitesByOrganizationId } from "@formbricks/lib/invite/service"; import { TOrganizationRole } from "@formbricks/types/memberships"; import { TOrganization } from "@formbricks/types/organizations"; diff --git a/apps/web/modules/organization/settings/teams/components/edit-memberships/member-actions.tsx b/apps/web/modules/organization/settings/teams/components/edit-memberships/member-actions.tsx index 316b8e8749..4e5858a4b5 100644 --- a/apps/web/modules/organization/settings/teams/components/edit-memberships/member-actions.tsx +++ b/apps/web/modules/organization/settings/teams/components/edit-memberships/member-actions.tsx @@ -8,6 +8,7 @@ import { resendInviteAction, } from "@/modules/organization/settings/teams/actions"; import { ShareInviteModal } from "@/modules/organization/settings/teams/components/invite-member/share-invite-modal"; +import { TInvite } from "@/modules/organization/settings/teams/types/invites"; import { Button } from "@/modules/ui/components/button"; import { DeleteDialog } from "@/modules/ui/components/delete-dialog"; import { TooltipRenderer } from "@/modules/ui/components/tooltip"; @@ -16,7 +17,6 @@ import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import React, { useMemo, useState } from "react"; import toast from "react-hot-toast"; -import { TInvite } from "@formbricks/types/invites"; import { TMember } from "@formbricks/types/memberships"; import { TOrganization } from "@formbricks/types/organizations"; @@ -56,7 +56,6 @@ export const MemberActions = ({ organization, member, invite, showDeleteButton } setIsDeleting(false); router.refresh(); } catch (err) { - console.log({ err }); setIsDeleting(false); toast.error(t("common.something_went_wrong_please_try_again")); } diff --git a/apps/web/modules/organization/settings/teams/components/edit-memberships/members-info.tsx b/apps/web/modules/organization/settings/teams/components/edit-memberships/members-info.tsx index 6d686168fe..dd9bad16a9 100644 --- a/apps/web/modules/organization/settings/teams/components/edit-memberships/members-info.tsx +++ b/apps/web/modules/organization/settings/teams/components/edit-memberships/members-info.tsx @@ -1,12 +1,12 @@ -import { isInviteExpired } from "@/app/lib/utils"; import { EditMembershipRole } from "@/modules/ee/role-management/components/edit-membership-role"; import { MemberActions } from "@/modules/organization/settings/teams/components/edit-memberships/member-actions"; +import { isInviteExpired } from "@/modules/organization/settings/teams/lib/utilts"; +import { TInvite } from "@/modules/organization/settings/teams/types/invites"; import { Badge } from "@/modules/ui/components/badge"; import { TooltipRenderer } from "@/modules/ui/components/tooltip"; import { useTranslations } from "next-intl"; import { getAccessFlags } from "@formbricks/lib/membership/utils"; import { getFormattedDateTimeString } from "@formbricks/lib/utils/datetime"; -import { TInvite } from "@formbricks/types/invites"; import { TMember, TOrganizationRole } from "@formbricks/types/memberships"; import { TOrganization } from "@formbricks/types/organizations"; diff --git a/apps/web/modules/organization/settings/teams/components/edit-memberships/organization-actions.tsx b/apps/web/modules/organization/settings/teams/components/edit-memberships/organization-actions.tsx index 48af1bbb76..d771bafef9 100644 --- a/apps/web/modules/organization/settings/teams/components/edit-memberships/organization-actions.tsx +++ b/apps/web/modules/organization/settings/teams/components/edit-memberships/organization-actions.tsx @@ -4,6 +4,7 @@ import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { TOrganizationTeam } from "@/modules/ee/teams/team-list/types/team"; import { inviteUserAction, leaveOrganizationAction } from "@/modules/organization/settings/teams/actions"; import { InviteMemberModal } from "@/modules/organization/settings/teams/components/invite-member/invite-member-modal"; +import { TInvitee } from "@/modules/organization/settings/teams/types/invites"; import { Button } from "@/modules/ui/components/button"; import { CustomDialog } from "@/modules/ui/components/custom-dialog"; import { XIcon } from "lucide-react"; @@ -11,7 +12,7 @@ import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; -import { TInvitee } from "@formbricks/types/invites"; +import { FORMBRICKS_ENVIRONMENT_ID_LS } from "@formbricks/lib/localStorage"; import { TOrganizationRole } from "@formbricks/types/memberships"; import { TOrganization } from "@formbricks/types/organizations"; @@ -53,6 +54,7 @@ export const OrganizationActions = ({ toast.success(t("environments.settings.general.member_deleted_successfully")); router.refresh(); setLoading(false); + localStorage.removeItem(FORMBRICKS_ENVIRONMENT_ID_LS); router.push("/"); } catch (err) { toast.error(`Error: ${err.message}`); @@ -84,7 +86,7 @@ export const OrganizationActions = ({ email: email.toLowerCase(), name, role, - teamIds: teamIds, + teamIds, }); return { email, diff --git a/apps/web/modules/organization/settings/teams/components/invite-member/bulk-invite-tab.tsx b/apps/web/modules/organization/settings/teams/components/invite-member/bulk-invite-tab.tsx index c359c64c47..94dfdc088c 100644 --- a/apps/web/modules/organization/settings/teams/components/invite-member/bulk-invite-tab.tsx +++ b/apps/web/modules/organization/settings/teams/components/invite-member/bulk-invite-tab.tsx @@ -1,5 +1,6 @@ "use client"; +import { ZInvitees } from "@/modules/organization/settings/teams/types/invites"; import { Alert, AlertDescription } from "@/modules/ui/components/alert"; import { Button } from "@/modules/ui/components/button"; import { Uploader } from "@/modules/ui/components/file-input/components/uploader"; @@ -9,7 +10,6 @@ import Link from "next/link"; import Papa, { type ParseResult } from "papaparse"; import { useState } from "react"; import toast from "react-hot-toast"; -import { ZInvitees } from "@formbricks/types/invites"; import { TOrganizationRole } from "@formbricks/types/memberships"; interface BulkInviteTabProps { diff --git a/packages/lib/invite/service.ts b/apps/web/modules/organization/settings/teams/lib/invite.ts similarity index 67% rename from packages/lib/invite/service.ts rename to apps/web/modules/organization/settings/teams/lib/invite.ts index 163e137dca..b3443b7f8b 100644 --- a/packages/lib/invite/service.ts +++ b/apps/web/modules/organization/settings/teams/lib/invite.ts @@ -1,173 +1,21 @@ -import "server-only"; -import { Prisma } from "@prisma/client"; +import { inviteCache } from "@/lib/cache/invite"; +import { Invite, Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; +import { z } from "zod"; import { prisma } from "@formbricks/database"; -import { ZOptionalNumber, ZString } from "@formbricks/types/common"; +import { cache } from "@formbricks/lib/cache"; +import { ITEMS_PER_PAGE } from "@formbricks/lib/constants"; +import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service"; +import { validateInputs } from "@formbricks/lib/utils/validate"; import { DatabaseError, InvalidInputError, ResourceNotFoundError, ValidationError, } from "@formbricks/types/errors"; -import { - TInvite, - TInviteUpdateInput, - TInvitee, - ZInviteUpdateInput, - ZInvitee, -} from "@formbricks/types/invites"; -import { cache } from "../cache"; -import { ITEMS_PER_PAGE } from "../constants"; -import { getMembershipByUserIdOrganizationId } from "../membership/service"; -import { validateInputs } from "../utils/validate"; -import { inviteCache } from "./cache"; - -const inviteSelect: Prisma.InviteSelect = { - id: true, - email: true, - name: true, - organizationId: true, - creatorId: true, - acceptorId: true, - createdAt: true, - expiresAt: true, - role: true, - teamIds: true, -}; -interface InviteWithCreator extends TInvite { - creator: { - name: string | null; - email: string; - }; -} -export const getInvitesByOrganizationId = reactCache( - async (organizationId: string, page?: number): Promise => - cache( - async () => { - validateInputs([organizationId, ZString], [page, ZOptionalNumber]); - - try { - const invites = await prisma.invite.findMany({ - where: { organizationId }, - select: inviteSelect, - take: page ? ITEMS_PER_PAGE : undefined, - skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined, - }); - - return invites; - } catch (error) { - if (error instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseError(error.message); - } - - throw error; - } - }, - [`getInvitesByOrganizationId-${organizationId}-${page}`], - { - tags: [inviteCache.tag.byOrganizationId(organizationId)], - } - )() -); - -export const updateInvite = async (inviteId: string, data: TInviteUpdateInput): Promise => { - validateInputs([inviteId, ZString], [data, ZInviteUpdateInput]); - - try { - const invite = await prisma.invite.update({ - where: { id: inviteId }, - data, - select: inviteSelect, - }); - - if (invite === null) { - throw new ResourceNotFoundError("Invite", inviteId); - } - - inviteCache.revalidate({ - id: invite.id, - organizationId: invite.organizationId, - }); - - return invite; - } catch (error) { - if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") { - throw new ResourceNotFoundError("Invite", inviteId); - } else { - throw error; // Re-throw any other errors - } - } -}; - -export const deleteInvite = async (inviteId: string): Promise => { - validateInputs([inviteId, ZString]); - - try { - const invite = await prisma.invite.delete({ - where: { - id: inviteId, - }, - }); - - if (invite === null) { - throw new ResourceNotFoundError("Invite", inviteId); - } - - inviteCache.revalidate({ - id: invite.id, - organizationId: invite.organizationId, - }); - - return invite; - } catch (error) { - if (error instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseError(error.message); - } - - throw error; - } -}; - -export const getInvite = reactCache( - async (inviteId: string): Promise => - cache( - async () => { - validateInputs([inviteId, ZString]); - - try { - const invite = await prisma.invite.findUnique({ - where: { - id: inviteId, - }, - include: { - creator: { - select: { - name: true, - email: true, - }, - }, - }, - }); - - return invite; - } catch (error) { - if (error instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseError(error.message); - } - - throw error; - } - }, - [`getInvite-${inviteId}`], - { - tags: [inviteCache.tag.byId(inviteId)], - } - )() -); - -export const resendInvite = async (inviteId: string): Promise => { - validateInputs([inviteId, ZString]); +import { type InviteWithCreator, type TInvite, type TInvitee } from "../types/invites"; +export const resendInvite = async (inviteId: string): Promise> => { try { const invite = await prisma.invite.findUnique({ where: { @@ -191,6 +39,12 @@ export const resendInvite = async (inviteId: string): Promise => { data: { expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7), }, + select: { + id: true, + email: true, + name: true, + organizationId: true, + }, }); inviteCache.revalidate({ @@ -198,7 +52,10 @@ export const resendInvite = async (inviteId: string): Promise => { organizationId: updatedInvite.organizationId, }); - return updatedInvite; + return { + email: updatedInvite.email, + name: updatedInvite.name, + }; } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseError(error.message); @@ -208,6 +65,43 @@ export const resendInvite = async (inviteId: string): Promise => { } }; +export const getInvitesByOrganizationId = reactCache( + async (organizationId: string, page?: number): Promise => + cache( + async () => { + validateInputs([organizationId, z.string()], [page, z.number().optional()]); + + try { + const invites = await prisma.invite.findMany({ + where: { organizationId }, + select: { + expiresAt: true, + role: true, + email: true, + name: true, + id: true, + createdAt: true, + }, + take: page ? ITEMS_PER_PAGE : undefined, + skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined, + }); + + return invites; + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } + }, + [`getInvitesByOrganizationId-${organizationId}-${page}`], + { + tags: [inviteCache.tag.byOrganizationId(organizationId)], + } + )() +); + export const inviteUser = async ({ invitee, organizationId, @@ -216,9 +110,7 @@ export const inviteUser = async ({ organizationId: string; invitee: TInvitee; currentUserId: string; -}): Promise => { - validateInputs([organizationId, ZString], [invitee, ZInvitee]); - +}): Promise => { try { const { name, email, role, teamIds } = invitee; @@ -276,7 +168,7 @@ export const inviteUser = async ({ organizationId: invite.organizationId, }); - return invite; + return invite.id; } catch (error) { if (error instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseError(error.message); @@ -285,3 +177,69 @@ export const inviteUser = async ({ throw error; } }; + +export const deleteInvite = async (inviteId: string): Promise => { + try { + const invite = await prisma.invite.delete({ + where: { + id: inviteId, + }, + select: { + id: true, + organizationId: true, + }, + }); + + if (!invite) { + throw new ResourceNotFoundError("Invite", inviteId); + } + + inviteCache.revalidate({ + id: invite.id, + organizationId: invite.organizationId, + }); + + return true; + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } +}; + +export const getInvite = reactCache( + async (inviteId: string): Promise => + cache( + async () => { + try { + const invite = await prisma.invite.findUnique({ + where: { + id: inviteId, + }, + select: { + email: true, + creator: { + select: { + name: true, + }, + }, + }, + }); + + return invite; + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } + }, + [`teams-getInvite-${inviteId}`], + { + tags: [inviteCache.tag.byId(inviteId)], + } + )() +); diff --git a/apps/web/app/lib/utils.ts b/apps/web/modules/organization/settings/teams/lib/utilts.ts similarity index 66% rename from apps/web/app/lib/utils.ts rename to apps/web/modules/organization/settings/teams/lib/utilts.ts index a64262d1b0..6f370d0705 100644 --- a/apps/web/app/lib/utils.ts +++ b/apps/web/modules/organization/settings/teams/lib/utilts.ts @@ -1,4 +1,4 @@ -import { TInvite } from "@formbricks/types/invites"; +import { TInvite } from "@/modules/organization/settings/teams/types/invites"; export const isInviteExpired = (invite: TInvite) => { const now = new Date(); diff --git a/apps/web/modules/organization/settings/teams/types/invites.ts b/apps/web/modules/organization/settings/teams/types/invites.ts new file mode 100644 index 0000000000..3434a2c06c --- /dev/null +++ b/apps/web/modules/organization/settings/teams/types/invites.ts @@ -0,0 +1,25 @@ +import { Invite } from "@prisma/client"; +import { z } from "zod"; +import { ZInvite } from "@formbricks/database/zod/invites"; +import { ZUserName } from "@formbricks/types/user"; + +export interface TInvite + extends Omit {} + +export interface InviteWithCreator extends Pick { + creator: { + name: string; + }; +} + +export const ZInvitee = ZInvite.pick({ + email: true, + role: true, + teamIds: true, +}).extend({ + name: ZUserName, +}); + +export type TInvitee = z.infer; + +export const ZInvitees = z.array(ZInvitee); diff --git a/apps/web/modules/projects/settings/general/components/delete-project-render.tsx b/apps/web/modules/projects/settings/general/components/delete-project-render.tsx index 7444a74269..1255a0d57a 100644 --- a/apps/web/modules/projects/settings/general/components/delete-project-render.tsx +++ b/apps/web/modules/projects/settings/general/components/delete-project-render.tsx @@ -16,13 +16,15 @@ import { TProject } from "@formbricks/types/project"; interface DeleteProjectRenderProps { isDeleteDisabled: boolean; isOwnerOrManager: boolean; - project: TProject; + currentProject: TProject; + organizationProjects: TProject[]; } export const DeleteProjectRender = ({ isDeleteDisabled, isOwnerOrManager, - project, + currentProject, + organizationProjects, }: DeleteProjectRenderProps) => { const t = useTranslations(); const router = useRouter(); @@ -30,9 +32,20 @@ export const DeleteProjectRender = ({ const [isDeleting, setIsDeleting] = useState(false); const handleDeleteProject = async () => { setIsDeleting(true); - const deleteProjectResponse = await deleteProjectAction({ projectId: project.id }); + const deleteProjectResponse = await deleteProjectAction({ projectId: currentProject.id }); if (deleteProjectResponse?.data) { - localStorage.removeItem(FORMBRICKS_ENVIRONMENT_ID_LS); + if (organizationProjects.length === 1) { + localStorage.removeItem(FORMBRICKS_ENVIRONMENT_ID_LS); + } else if (organizationProjects.length > 1) { + // prevents changing of organization when deleting project + const remainingProjects = organizationProjects.filter((project) => project.id !== currentProject.id); + const productionEnvironment = remainingProjects[0].environments.find( + (environment) => environment.type === "production" + ); + if (productionEnvironment) { + localStorage.setItem(FORMBRICKS_ENVIRONMENT_ID_LS, productionEnvironment.id); + } + } toast.success(t("environments.project.general.project_deleted_successfully")); router.push("/"); } else { @@ -51,7 +64,7 @@ export const DeleteProjectRender = ({ {t( "environments.project.general.delete_project_name_includes_surveys_responses_people_and_more", { - projectName: truncate(project.name, 30), + projectName: truncate(currentProject.name, 30), } )}{" "} {t("environments.project.general.this_action_cannot_be_undone")} @@ -81,7 +94,7 @@ export const DeleteProjectRender = ({ setOpen={setIsDeleteDialogOpen} onDelete={handleDeleteProject} text={t("environments.project.general.delete_project_confirmation", { - projectName: truncate(project.name, 30), + projectName: truncate(currentProject.name, 30), })} isDeleting={isDeleting} /> diff --git a/apps/web/modules/projects/settings/general/components/delete-project.tsx b/apps/web/modules/projects/settings/general/components/delete-project.tsx index 406e5ef16a..0658993ea2 100644 --- a/apps/web/modules/projects/settings/general/components/delete-project.tsx +++ b/apps/web/modules/projects/settings/general/components/delete-project.tsx @@ -8,11 +8,17 @@ import { TProject } from "@formbricks/types/project"; interface DeleteProjectProps { environmentId: string; - project: TProject; + currentProject: TProject; + organizationProjects: TProject[]; isOwnerOrManager: boolean; } -export const DeleteProject = async ({ environmentId, project, isOwnerOrManager }: DeleteProjectProps) => { +export const DeleteProject = async ({ + environmentId, + currentProject, + organizationProjects, + isOwnerOrManager, +}: DeleteProjectProps) => { const t = await getTranslations(); const session = await getServerSession(authOptions); if (!session) { @@ -31,7 +37,8 @@ export const DeleteProject = async ({ environmentId, project, isOwnerOrManager } ); }; diff --git a/apps/web/modules/projects/settings/general/page.tsx b/apps/web/modules/projects/settings/general/page.tsx index 2b8b236dc4..8d9404a304 100644 --- a/apps/web/modules/projects/settings/general/page.tsx +++ b/apps/web/modules/projects/settings/general/page.tsx @@ -17,7 +17,7 @@ import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants"; import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service"; import { getAccessFlags } from "@formbricks/lib/membership/utils"; import { getOrganizationByEnvironmentId } from "@formbricks/lib/organization/service"; -import { getProjectByEnvironmentId } from "@formbricks/lib/project/service"; +import { getProjectByEnvironmentId, getProjects } from "@formbricks/lib/project/service"; import { DeleteProject } from "./components/delete-project"; import { EditProjectNameForm } from "./components/edit-project-name-form"; import { EditWaitingTimeForm } from "./components/edit-waiting-time-form"; @@ -41,6 +41,8 @@ export const GeneralSettingsPage = async (props: { params: Promise<{ environment throw new Error(t("common.organization_not_found")); } + const organizationProjects = await getProjects(organization.id); + const currentUserMembership = await getMembershipByUserIdOrganizationId(session?.user.id, organization.id); const projectPermission = await getProjectPermissionByUserId(session.user.id, project.id); @@ -79,7 +81,8 @@ export const GeneralSettingsPage = async (props: { params: Promise<{ environment description={t("environments.project.general.delete_project_settings_description")}> diff --git a/apps/web/modules/setup/(fresh-instance)/intro/page.tsx b/apps/web/modules/setup/(fresh-instance)/intro/page.tsx new file mode 100644 index 0000000000..0cde887aec --- /dev/null +++ b/apps/web/modules/setup/(fresh-instance)/intro/page.tsx @@ -0,0 +1,33 @@ +import { Button } from "@/modules/ui/components/button"; +import { Metadata } from "next"; +import { getTranslations } from "next-intl/server"; +import Link from "next/link"; + +export const metadata: Metadata = { + title: "Intro", + description: "Open-source Experience Management. Free & open source.", +}; + +const renderRichText = async (text: string) => { + const t = await getTranslations(); + return

    {t.rich(text, { b: (chunks) => {chunks} })}

    ; +}; + +export const IntroPage = async () => { + const t = await getTranslations(); + return ( +
    +

    {t("setup.intro.welcome_to_formbricks")}

    +
    + {renderRichText("setup.intro.paragraph_1")} + {renderRichText("setup.intro.paragraph_2")} + {renderRichText("setup.intro.paragraph_3")} +
    + + +

    {t("setup.intro.made_with_love_in_kiel")}

    +
    + ); +}; diff --git a/apps/web/modules/setup/(fresh-instance)/layout.tsx b/apps/web/modules/setup/(fresh-instance)/layout.tsx new file mode 100644 index 0000000000..fa16495a7a --- /dev/null +++ b/apps/web/modules/setup/(fresh-instance)/layout.tsx @@ -0,0 +1,14 @@ +import { authOptions } from "@/modules/auth/lib/authOptions"; +import { getServerSession } from "next-auth"; +import { notFound } from "next/navigation"; +import { getIsFreshInstance } from "@formbricks/lib/instance/service"; + +export const FreshInstanceLayout = async ({ children }: { children: React.ReactNode }) => { + const session = await getServerSession(authOptions); + const isFreshInstance = await getIsFreshInstance(); + + if (session || !isFreshInstance) { + return notFound(); + } + return <>{children}; +}; diff --git a/apps/web/modules/setup/(fresh-instance)/signup/page.tsx b/apps/web/modules/setup/(fresh-instance)/signup/page.tsx new file mode 100644 index 0000000000..41117f1e3a --- /dev/null +++ b/apps/web/modules/setup/(fresh-instance)/signup/page.tsx @@ -0,0 +1,55 @@ +import { SignupForm } from "@/modules/auth/signup/components/signup-form"; +import { getIsSSOEnabled } from "@/modules/ee/license-check/lib/utils"; +import { Metadata } from "next"; +import { getTranslations } from "next-intl/server"; +import { + AZURE_OAUTH_ENABLED, + DEFAULT_ORGANIZATION_ID, + DEFAULT_ORGANIZATION_ROLE, + EMAIL_AUTH_ENABLED, + EMAIL_VERIFICATION_DISABLED, + GITHUB_OAUTH_ENABLED, + GOOGLE_OAUTH_ENABLED, + IS_TURNSTILE_CONFIGURED, + OIDC_DISPLAY_NAME, + OIDC_OAUTH_ENABLED, + PRIVACY_URL, + TERMS_URL, + WEBAPP_URL, +} from "@formbricks/lib/constants"; +import { findMatchingLocale } from "@formbricks/lib/utils/locale"; + +export const metadata: Metadata = { + title: "Sign up", + description: "Open-source Experience Management. Free & open source.", +}; + +export const SignupPage = async () => { + const locale = await findMatchingLocale(); + const isSSOEnabled = await getIsSSOEnabled(); + const t = await getTranslations(); + return ( +
    +

    {t("setup.signup.create_administrator")}

    +

    {t("setup.signup.this_user_has_all_the_power")}

    +
    + +
    + ); +}; diff --git a/apps/web/modules/setup/layout.tsx b/apps/web/modules/setup/layout.tsx new file mode 100644 index 0000000000..2e4722ec5f --- /dev/null +++ b/apps/web/modules/setup/layout.tsx @@ -0,0 +1,20 @@ +import { FormbricksLogo } from "@/modules/ui/components/formbricks-logo"; +import { Toaster } from "react-hot-toast"; + +export const SetupLayout = ({ children }: { children: React.ReactNode }) => { + return ( + <> + +
    +
    +
    + +
    + {children} +
    +
    + + ); +}; diff --git a/apps/web/app/setup/organization/[organizationId]/invite/actions.ts b/apps/web/modules/setup/organization/[organizationId]/invite/actions.ts similarity index 88% rename from apps/web/app/setup/organization/[organizationId]/invite/actions.ts rename to apps/web/modules/setup/organization/[organizationId]/invite/actions.ts index e9cd07a0bb..d1e5e7e429 100644 --- a/apps/web/app/setup/organization/[organizationId]/invite/actions.ts +++ b/apps/web/modules/setup/organization/[organizationId]/invite/actions.ts @@ -3,9 +3,9 @@ import { authenticatedActionClient } from "@/lib/utils/action-client"; import { checkAuthorizationUpdated } from "@/lib/utils/action-client-middleware"; import { sendInviteMemberEmail } from "@/modules/email"; +import { inviteUser } from "@/modules/setup/organization/[organizationId]/invite/lib/invite"; import { z } from "zod"; import { INVITE_DISABLED } from "@formbricks/lib/constants"; -import { inviteUser } from "@formbricks/lib/invite/service"; import { ZId } from "@formbricks/types/common"; import { AuthenticationError } from "@formbricks/types/errors"; import { ZUserEmail, ZUserName } from "@formbricks/types/user"; @@ -34,19 +34,17 @@ export const inviteOrganizationMemberAction = authenticatedActionClient ], }); - const invite = await inviteUser({ + const invitedUserId = await inviteUser({ organizationId: parsedInput.organizationId, invitee: { email: parsedInput.email, name: parsedInput.name, - role: "owner", - teamIds: [], }, currentUserId: ctx.user.id, }); await sendInviteMemberEmail( - invite.id, + invitedUserId, parsedInput.email, ctx.user.name, "", @@ -55,5 +53,5 @@ export const inviteOrganizationMemberAction = authenticatedActionClient ctx.user.locale ); - return invite; + return invitedUserId; }); diff --git a/apps/web/app/setup/organization/[organizationId]/invite/components/invite-members.tsx b/apps/web/modules/setup/organization/[organizationId]/invite/components/invite-members.tsx similarity index 95% rename from apps/web/app/setup/organization/[organizationId]/invite/components/invite-members.tsx rename to apps/web/modules/setup/organization/[organizationId]/invite/components/invite-members.tsx index ccef3ddf30..ff91a79f92 100644 --- a/apps/web/app/setup/organization/[organizationId]/invite/components/invite-members.tsx +++ b/apps/web/modules/setup/organization/[organizationId]/invite/components/invite-members.tsx @@ -1,7 +1,11 @@ "use client"; -import { inviteOrganizationMemberAction } from "@/app/setup/organization/[organizationId]/invite/actions"; import { getFormattedErrorMessage } from "@/lib/utils/helper"; +import { inviteOrganizationMemberAction } from "@/modules/setup/organization/[organizationId]/invite/actions"; +import { + type TInviteMembersFormSchema, + ZInviteMembersFormSchema, +} from "@/modules/setup/organization/[organizationId]/invite/types/invites"; import { Alert, AlertDescription, AlertTitle } from "@/modules/ui/components/alert"; import { Button } from "@/modules/ui/components/button"; import { FormControl, FormError, FormField, FormItem, FormProvider } from "@/modules/ui/components/form"; @@ -13,7 +17,6 @@ import { useRouter } from "next/navigation"; import React, { useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "react-hot-toast"; -import { TInviteMembersFormSchema, ZInviteMembersFormSchema } from "@formbricks/types/invites"; interface InviteMembersProps { IS_SMTP_CONFIGURED: boolean; diff --git a/apps/web/modules/setup/organization/[organizationId]/invite/lib/invite.ts b/apps/web/modules/setup/organization/[organizationId]/invite/lib/invite.ts new file mode 100644 index 0000000000..e5bd95c136 --- /dev/null +++ b/apps/web/modules/setup/organization/[organizationId]/invite/lib/invite.ts @@ -0,0 +1,64 @@ +import { inviteCache } from "@/lib/cache/invite"; +import { TInvitee } from "@/modules/setup/organization/[organizationId]/invite/types/invites"; +import { Prisma } from "@prisma/client"; +import { prisma } from "@formbricks/database"; +import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service"; +import { DatabaseError, InvalidInputError } from "@formbricks/types/errors"; + +export const inviteUser = async ({ + invitee, + organizationId, + currentUserId, +}: { + organizationId: string; + invitee: TInvitee; + currentUserId: string; +}): Promise => { + try { + const { name, email } = invitee; + + const existingInvite = await prisma.invite.findFirst({ where: { email, organizationId } }); + + if (existingInvite) { + throw new InvalidInputError("Invite already exists"); + } + + const user = await prisma.user.findUnique({ where: { email } }); + + if (user) { + const member = await getMembershipByUserIdOrganizationId(user.id, organizationId); + + if (member) { + throw new InvalidInputError("User is already a member of this organization"); + } + } + + const expiresIn = 7 * 24 * 60 * 60 * 1000; // 7 days + const expiresAt = new Date(Date.now() + expiresIn); + + const invite = await prisma.invite.create({ + data: { + email, + name, + organization: { connect: { id: organizationId } }, + creator: { connect: { id: currentUserId } }, + acceptor: user ? { connect: { id: user.id } } : undefined, + role: "owner", + expiresAt, + }, + }); + + inviteCache.revalidate({ + id: invite.id, + organizationId: invite.organizationId, + }); + + return invite.id; + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError) { + throw new DatabaseError(error.message); + } + + throw error; + } +}; diff --git a/apps/web/modules/setup/organization/[organizationId]/invite/page.tsx b/apps/web/modules/setup/organization/[organizationId]/invite/page.tsx new file mode 100644 index 0000000000..bd2a635436 --- /dev/null +++ b/apps/web/modules/setup/organization/[organizationId]/invite/page.tsx @@ -0,0 +1,35 @@ +import { authOptions } from "@/modules/auth/lib/authOptions"; +import { InviteMembers } from "@/modules/setup/organization/[organizationId]/invite/components/invite-members"; +import { Metadata } from "next"; +import { getServerSession } from "next-auth"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; +import { SMTP_HOST, SMTP_PASSWORD, SMTP_PORT, SMTP_USER } from "@formbricks/lib/constants"; +import { verifyUserRoleAccess } from "@formbricks/lib/organization/auth"; +import { AuthenticationError } from "@formbricks/types/errors"; + +export const metadata: Metadata = { + title: "Invite", + description: "Open-source Experience Management. Free & open source.", +}; + +interface InvitePageProps { + params: Promise<{ organizationId: string }>; +} + +export const InvitePage = async (props: InvitePageProps) => { + const params = await props.params; + const t = await getTranslations(); + const IS_SMTP_CONFIGURED = Boolean(SMTP_HOST && SMTP_PORT && SMTP_USER && SMTP_PASSWORD); + const session = await getServerSession(authOptions); + if (!session) throw new AuthenticationError(t("common.session_not_found")); + + const { hasCreateOrUpdateMembersAccess } = await verifyUserRoleAccess( + params.organizationId, + session.user.id + ); + + if (!hasCreateOrUpdateMembersAccess) return notFound(); + + return ; +}; diff --git a/apps/web/modules/setup/organization/[organizationId]/invite/types/invites.ts b/apps/web/modules/setup/organization/[organizationId]/invite/types/invites.ts new file mode 100644 index 0000000000..e28ac58e24 --- /dev/null +++ b/apps/web/modules/setup/organization/[organizationId]/invite/types/invites.ts @@ -0,0 +1,24 @@ +import { z } from "zod"; +import { ZInvite } from "@formbricks/database/zod/invites"; +import { ZUserName } from "@formbricks/types/user"; + +export const ZInvitee = ZInvite.pick({ + name: true, + email: true, +}).extend({ + name: ZUserName, +}); + +export type TInvitee = z.infer; + +export const ZInviteMembersFormSchema = z.record( + ZInvite.pick({ + email: true, + name: true, + }).extend({ + email: z.string().email("Invalid email address"), + name: ZUserName, + }) +); + +export type TInviteMembersFormSchema = z.infer; diff --git a/apps/web/app/setup/organization/create/components/create-organization.tsx b/apps/web/modules/setup/organization/create/components/create-organization.tsx similarity index 100% rename from apps/web/app/setup/organization/create/components/create-organization.tsx rename to apps/web/modules/setup/organization/create/components/create-organization.tsx diff --git a/apps/web/app/setup/organization/create/components/removed-from-organization.tsx b/apps/web/modules/setup/organization/create/components/removed-from-organization.tsx similarity index 100% rename from apps/web/app/setup/organization/create/components/removed-from-organization.tsx rename to apps/web/modules/setup/organization/create/components/removed-from-organization.tsx diff --git a/apps/web/modules/setup/organization/create/page.tsx b/apps/web/modules/setup/organization/create/page.tsx new file mode 100644 index 0000000000..bd8953fd2c --- /dev/null +++ b/apps/web/modules/setup/organization/create/page.tsx @@ -0,0 +1,45 @@ +import { authOptions } from "@/modules/auth/lib/authOptions"; +import { getIsMultiOrgEnabled } from "@/modules/ee/license-check/lib/utils"; +import { RemovedFromOrganization } from "@/modules/setup/organization/create/components/removed-from-organization"; +import { ClientLogout } from "@/modules/ui/components/client-logout"; +import { Metadata } from "next"; +import { getServerSession } from "next-auth"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; +import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants"; +import { gethasNoOrganizations } from "@formbricks/lib/instance/service"; +import { getOrganizationsByUserId } from "@formbricks/lib/organization/service"; +import { getUser } from "@formbricks/lib/user/service"; +import { AuthenticationError } from "@formbricks/types/errors"; +import { CreateOrganization } from "./components/create-organization"; + +export const metadata: Metadata = { + title: "Create Organization", + description: "Open-source Experience Management. Free & open source.", +}; + +export const CreateOrganizationPage = async () => { + const t = await getTranslations(); + const session = await getServerSession(authOptions); + + if (!session) throw new AuthenticationError(t("common.session_not_found")); + + const user = await getUser(session.user.id); + if (!user) { + return ; + } + + const hasNoOrganizations = await gethasNoOrganizations(); + const isMultiOrgEnabled = await getIsMultiOrgEnabled(); + const userOrganizations = await getOrganizationsByUserId(session.user.id); + + if (hasNoOrganizations || isMultiOrgEnabled) { + return ; + } + + if (userOrganizations.length === 0) { + return ; + } + + return notFound(); +}; diff --git a/apps/web/modules/ui/components/file-upload-response/index.tsx b/apps/web/modules/ui/components/file-upload-response/index.tsx index 0b2cefc390..bf71389218 100644 --- a/apps/web/modules/ui/components/file-upload-response/index.tsx +++ b/apps/web/modules/ui/components/file-upload-response/index.tsx @@ -7,23 +7,21 @@ import { getOriginalFileNameFromUrl } from "@formbricks/lib/storage/utils"; interface FileUploadResponseProps { selected: string[]; } - export const FileUploadResponse = ({ selected }: FileUploadResponseProps) => { const t = useTranslations(); if (selected.length === 0) { return
    {t("common.skipped")}
    ; } - return (
    {selected.map((fileUrl, index) => { const fileName = getOriginalFileNameFromUrl(fileUrl); - return (

    {fileName ? fileName : "Download"} diff --git a/apps/web/package.json b/apps/web/package.json index 83d44b170b..d8e3c4e11b 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@formbricks/web", - "version": "3.1.3", + "version": "3.1.4", "private": true, "scripts": { "clean": "rimraf .turbo node_modules .next", @@ -10,10 +10,11 @@ "build:dev": "next build", "start": "next start", "lint": "next lint", - "test": "dotenv -e ../../.env -- vitest run" + "test": "dotenv -e ../../.env -- vitest run", + "test:coverage": "dotenv -e ../../.env -- vitest run --coverage" }, "dependencies": { - "@ai-sdk/azure": "1.0.10", + "@ai-sdk/azure": "1.1.9", "@dnd-kit/core": "6.3.1", "@dnd-kit/modifiers": "9.0.0", "@dnd-kit/sortable": "10.0.0", @@ -58,7 +59,7 @@ "@radix-ui/react-toggle-group": "1.1.1", "@radix-ui/react-tooltip": "1.1.5", "@react-email/components": "0.0.31", - "@sentry/nextjs": "8.45.1", + "@sentry/nextjs": "8.52.0", "@tailwindcss/forms": "0.5.9", "@tailwindcss/typography": "0.5.15", "@tanstack/react-table": "8.20.6", @@ -66,7 +67,7 @@ "@vercel/og": "0.6.4", "@vercel/otel": "1.10.0", "@vercel/speed-insights": "1.1.0", - "ai": "4.0.18", + "ai": "4.1.17", "autoprefixer": "10.4.20", "bcryptjs": "2.4.3", "boring-avatars": "1.11.2", @@ -130,6 +131,7 @@ "@types/nodemailer": "6.4.17", "@types/papaparse": "5.3.15", "@types/qrcode": "1.5.5", + "@vitest/coverage-v8": "2.1.8", "vite": "6.0.9", "vitest": "2.1.8", "vitest-mock-extended": "2.0.2" diff --git a/apps/web/playwright/organization.spec.ts b/apps/web/playwright/organization.spec.ts index a04863a468..e1ef5aaa44 100644 --- a/apps/web/playwright/organization.spec.ts +++ b/apps/web/playwright/organization.spec.ts @@ -138,6 +138,7 @@ test.describe("Create, update and delete team", async () => { await page.getByRole("link", { name: "Organization" }).click(); await page.waitForURL(/\/environments\/[^/]+\/settings\/general/); + await page.waitForTimeout(2000); await page.waitForLoadState("networkidle"); await expect(page.getByText("Teams")).toBeVisible(); await page.getByText("Teams").click(); diff --git a/apps/web/vite.config.mts b/apps/web/vite.config.mts index 2333c6a18f..e829c79c83 100644 --- a/apps/web/vite.config.mts +++ b/apps/web/vite.config.mts @@ -1,10 +1,18 @@ -import tsconfigPaths from "vite-tsconfig-paths"; -import { defineConfig } from "vitest/config"; +// vitest.config.ts +import tsconfigPaths from 'vite-tsconfig-paths'; +import { loadEnv } from 'vite' +import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - exclude: ["playwright/**", "node_modules/**"], - setupFiles: ["../../packages/lib/vitestSetup.ts"], + exclude: ['playwright/**', 'node_modules/**'], + setupFiles: ['../../packages/lib/vitestSetup.ts'], + env: loadEnv('', process.cwd(), ''), + coverage: { + provider: 'v8', // Use V8 as the coverage provider + reporter: ['text', 'html', 'lcov'], // Generate text summary and HTML reports + reportsDirectory: './coverage', // Output coverage reports to the coverage/ directory + }, }, plugins: [tsconfigPaths()], -}); +}); \ No newline at end of file diff --git a/packages/api/src/api/client/attribute.ts b/packages/api/src/api/client/attribute.ts index 0aee4cf625..338878108c 100644 --- a/packages/api/src/api/client/attribute.ts +++ b/packages/api/src/api/client/attribute.ts @@ -14,9 +14,7 @@ export class AttributeAPI { async update( attributeUpdateInput: Omit - ): Promise< - Result<{ changed: boolean; message: string; details?: Record }, ApiErrorResponse> - > { + ): Promise> { // transform all attributes to string if attributes are present into a new attributes copy const attributes: Record = {}; for (const key in attributeUpdateInput.attributes) { diff --git a/packages/api/src/api/client/environment.ts b/packages/api/src/api/client/environment.ts new file mode 100644 index 0000000000..8bc2114f1b --- /dev/null +++ b/packages/api/src/api/client/environment.ts @@ -0,0 +1,18 @@ +import { type Result } from "@formbricks/types/error-handlers"; +import { type ApiErrorResponse } from "@formbricks/types/errors"; +import { type TJsEnvironmentState } from "@formbricks/types/js"; +import { makeRequest } from "../../utils/make-request"; + +export class EnvironmentAPI { + private apiHost: string; + private environmentId: string; + + constructor(apiHost: string, environmentId: string) { + this.apiHost = apiHost; + this.environmentId = environmentId; + } + + async getState(): Promise> { + return makeRequest(this.apiHost, `/api/v1/client/${this.environmentId}/environment/`, "GET"); + } +} diff --git a/packages/api/src/api/client/index.ts b/packages/api/src/api/client/index.ts index e1a5704490..e1932e6b3e 100644 --- a/packages/api/src/api/client/index.ts +++ b/packages/api/src/api/client/index.ts @@ -1,14 +1,18 @@ import { type ApiConfig } from "../../types"; import { AttributeAPI } from "./attribute"; import { DisplayAPI } from "./display"; +import { EnvironmentAPI } from "./environment"; import { ResponseAPI } from "./response"; import { StorageAPI } from "./storage"; +import { UserAPI } from "./user"; export class Client { response: ResponseAPI; display: DisplayAPI; storage: StorageAPI; attribute: AttributeAPI; + user: UserAPI; + environment: EnvironmentAPI; constructor(options: ApiConfig) { const { apiHost, environmentId } = options; @@ -17,5 +21,7 @@ export class Client { this.display = new DisplayAPI(apiHost, environmentId); this.attribute = new AttributeAPI(apiHost, environmentId); this.storage = new StorageAPI(apiHost, environmentId); + this.user = new UserAPI(apiHost, environmentId); + this.environment = new EnvironmentAPI(apiHost, environmentId); } } diff --git a/packages/api/src/api/client/user.ts b/packages/api/src/api/client/user.ts new file mode 100644 index 0000000000..c86378903e --- /dev/null +++ b/packages/api/src/api/client/user.ts @@ -0,0 +1,44 @@ +import { type Result } from "@formbricks/types/error-handlers"; +import { type ApiErrorResponse } from "@formbricks/types/errors"; +import { makeRequest } from "../../utils/make-request"; + +export class UserAPI { + private apiHost: string; + private environmentId: string; + + constructor(apiHost: string, environmentId: string) { + this.apiHost = apiHost; + this.environmentId = environmentId; + } + + async createOrUpdate(userUpdateInput: { userId: string; attributes?: Record }): Promise< + Result< + { + state: { + expiresAt: Date | null; + data: { + userId: string | null; + segments: string[]; + displays: { surveyId: string; createdAt: Date }[]; + responses: string[]; + lastDisplayAt: Date | null; + language?: string; + }; + }; + messages?: string[]; + }, + ApiErrorResponse + > + > { + // transform all attributes to string if attributes are present into a new attributes copy + const attributes: Record = {}; + for (const key in userUpdateInput.attributes) { + attributes[key] = String(userUpdateInput.attributes[key]); + } + + return makeRequest(this.apiHost, `/api/v1/client/${this.environmentId}/user`, "POST", { + userId: userUpdateInput.userId, + attributes, + }); + } +} diff --git a/packages/config-typescript/react-native-library.json b/packages/config-typescript/react-native-library.json index 9da691e72a..96a989c4f6 100644 --- a/packages/config-typescript/react-native-library.json +++ b/packages/config-typescript/react-native-library.json @@ -6,7 +6,8 @@ "lib": ["dom", "dom.iterable", "ES2022"], "noEmit": true, "resolveJsonModule": true, - "target": "ESNext" + "target": "ESNext", + "esModuleInterop": true }, "display": "React Native Library", "extends": "./base.json" diff --git a/packages/database/zod/invites.ts b/packages/database/zod/invites.ts new file mode 100644 index 0000000000..f5adbb5e34 --- /dev/null +++ b/packages/database/zod/invites.ts @@ -0,0 +1,15 @@ +import { type Invite } from "@prisma/client"; +import { z } from "zod"; + +export const ZInvite = z.object({ + id: z.string(), + email: z.string().email(), + name: z.string().nullable(), + organizationId: z.string(), + creatorId: z.string(), + acceptorId: z.string().nullable(), + createdAt: z.date(), + expiresAt: z.date(), + role: z.enum(["owner", "manager", "member", "billing"]), + teamIds: z.array(z.string()), +}) satisfies z.ZodType>; diff --git a/packages/js-core/src/lib/attributes.ts b/packages/js-core/src/lib/attributes.ts index fbd2c27924..f62e3489e6 100644 --- a/packages/js-core/src/lib/attributes.ts +++ b/packages/js-core/src/lib/attributes.ts @@ -18,7 +18,7 @@ export const updateAttribute = async ( { changed: boolean; message: string; - details?: Record; + messages?: string[]; }, ApiErrorResponse > @@ -65,10 +65,12 @@ export const updateAttribute = async ( }); } - if (res.data.details) { - Object.entries(res.data.details).forEach(([detailsKey, detailsValue]) => { - logger.error(`${detailsKey}: ${detailsValue}`); - }); + const responseMessages = res.data.messages; + + if (responseMessages && responseMessages.length > 0) { + for (const message of responseMessages) { + logger.debug(message); + } } if (res.data.changed) { @@ -79,9 +81,7 @@ export const updateAttribute = async ( value: { changed: true, message: "Attribute updated in Formbricks", - ...(res.data.details && { - details: res.data.details, - }), + messages: responseMessages, }, }; } @@ -91,9 +91,7 @@ export const updateAttribute = async ( value: { changed: false, message: "Attribute not updated in Formbricks", - ...(res.data.details && { - details: res.data.details, - }), + messages: responseMessages, }, }; }; @@ -123,10 +121,10 @@ export const updateAttributes = async ( const res = await api.client.attribute.update({ userId, attributes: updatedAttributes }); if (res.ok) { - if (res.data.details) { - Object.entries(res.data.details).forEach(([key, value]) => { - logger.debug(`${key}: ${value}`); - }); + if (res.data.messages) { + for (const message of res.data.messages) { + logger.debug(message); + } } return ok(updatedAttributes); diff --git a/packages/js-core/src/lib/constants.ts b/packages/js-core/src/lib/constants.ts index bf19f961e6..7b57b3e576 100644 --- a/packages/js-core/src/lib/constants.ts +++ b/packages/js-core/src/lib/constants.ts @@ -1,4 +1,3 @@ -export const RN_ASYNC_STORAGE_KEY = "formbricks-react-native"; export const JS_LOCAL_STORAGE_KEY = "formbricks-js"; export const LEGACY_JS_WEBSITE_LOCAL_STORAGE_KEY = "formbricks-js-website"; export const LEGACY_JS_APP_LOCAL_STORAGE_KEY = "formbricks-js-app"; diff --git a/packages/js-core/src/lib/environment-state.ts b/packages/js-core/src/lib/environment-state.ts index bf013b2a4f..c77799b6d9 100644 --- a/packages/js-core/src/lib/environment-state.ts +++ b/packages/js-core/src/lib/environment-state.ts @@ -48,13 +48,10 @@ export const fetchEnvironmentState = async ( throw error.error; } - const data = (await response.json()) as { data: TJsEnvironmentState["data"] }; + const data = (await response.json()) as { data: TJsEnvironmentState }; const { data: state } = data; - return { - data: { ...state }, - expiresAt: new Date(new Date().getTime() + 1000 * 60 * 30), // 30 minutes - }; + return state; }; /** diff --git a/packages/lib/messages/de-DE.json b/packages/lib/messages/de-DE.json index 8285658624..87abee8bbb 100644 --- a/packages/lib/messages/de-DE.json +++ b/packages/lib/messages/de-DE.json @@ -443,24 +443,36 @@ "you_will_be_downgraded_to_the_community_edition_on_date": "Du wirst am {Datum} auf die Community Edition herabgestuft." }, "emails": { + "accept": "Annehmen", + "click_or_drag_to_upload_files": "Klicke oder ziehe, um Dateien hochzuladen.", "email_customization_preview_email_heading": "Hey {userName}", + "email_customization_preview_email_subject": "Formbricks E-Mail-Umfrage Vorschau", "email_customization_preview_email_text": "Dies ist eine E-Mail-Vorschau, um dir zu zeigen, welches Logo in den E-Mails gerendert wird.", + "email_footer_text_1": "Einen schönen Tag noch!", + "email_footer_text_2": "Dein Formbricks Team", + "email_template_text_1": "Diese E-Mail wurde via Formbricks gesendet.", "embed_survey_preview_email_didnt_request": "Kein Interesse?", "embed_survey_preview_email_environment_id": "Umgebungs-ID", "embed_survey_preview_email_fight_spam": "Hilf uns, Spam zu bekämpfen, und leite diese Mail an hola@formbricks.com weiter.", "embed_survey_preview_email_heading": "Vorschau Einbettung in E-Mail", + "embed_survey_preview_email_subject": "Formbricks E-Mail-Umfrage Vorschau", "embed_survey_preview_email_text": "So sieht die Umfrage eingebettet in eine E-Mail aus:", "forgot_password_email_change_password": "Passwort ändern", "forgot_password_email_did_not_request": "Wenn Du sie nicht angefordert hast, ignoriere bitte diese E-Mail.", "forgot_password_email_heading": "Passwort ändern", "forgot_password_email_link_valid_for_24_hours": "Der Link ist 24 Stunden gültig.", + "forgot_password_email_subject": "Setz dein Formbricks-Passwort zurück", "forgot_password_email_text": "Du hast einen Link angefordert, um dein Passwort zu ändern. Du kannst dies tun, indem Du auf den untenstehenden Link klickst:", + "imprint": "Impressum", "invite_accepted_email_heading": "Hey", + "invite_accepted_email_subject": "Du hast einen neuen Organisation-Mitglied!", "invite_accepted_email_text_par1": "Wollte dir nur Bescheid geben, dass", "invite_accepted_email_text_par2": "deine Einladung angenommen hat. Viel Spaß bei der Zusammenarbeit!", + "invite_email_button_label": "Organisation beitreten", "invite_email_heading": "Hey", "invite_email_text_par1": "Dein Kollege", "invite_email_text_par2": "hat Dich eingeladen, Formbricks zu nutzen. Um die Einladung anzunehmen, klicke bitte auf den untenstehenden Link:", + "invite_member_email_subject": "Du wurdest eingeladen, Formbricks zu nutzen!", "live_survey_notification_completed": "Abgeschlossen", "live_survey_notification_draft": "Entwurf", "live_survey_notification_in_progress": "In Bearbeitung", @@ -483,13 +495,22 @@ "notification_insight_displays": "Displays", "notification_insight_responses": "Antworten", "notification_insight_surveys": "Umfragen", + "onboarding_invite_email_button_label": "Tritt {inviterName}s Organisation bei", "onboarding_invite_email_connect_formbricks": "Verbinde Formbricks in nur wenigen Minuten über ein HTML-Snippet oder via NPM mit deiner App oder Website.", "onboarding_invite_email_create_account": "Erstelle ein Konto, um {inviterName}s Organisation beizutreten.", "onboarding_invite_email_done": "Erledigt ✅", "onboarding_invite_email_get_started_in_minutes": "Dauert nur wenige Minuten", "onboarding_invite_email_heading": "Hey ", + "onboarding_invite_email_subject": "{inviterName} braucht Hilfe bei Formbricks. Kannst Du ihm helfen?", "password_changed_email_heading": "Passwort geändert", "password_changed_email_text": "Dein Passwort wurde erfolgreich geändert.", + "password_reset_notify_email_subject": "Dein Formbricks-Passwort wurde geändert", + "privacy_policy": "Datenschutzerklärung", + "reject": "Ablehnen", + "response_finished_email_subject": "Eine Antwort für {surveyName} wurde abgeschlossen ✅", + "response_finished_email_subject_with_email": "{personEmail} hat deine Umfrage {surveyName} abgeschlossen ✅", + "schedule_your_meeting": "Termin planen", + "select_a_date": "Datum auswählen", "survey_response_finished_email_congrats": "Glückwunsch, Du hast eine neue Antwort auf deine Umfrage {surveyName} erhalten!", "survey_response_finished_email_dont_want_notifications": "Möchtest Du diese Benachrichtigungen nicht erhalten?", "survey_response_finished_email_hey": "Hey 👋", @@ -512,12 +533,14 @@ "verification_email_thanks": "Danke, dass Du deine E-Mail bestätigt hast!", "verification_email_to_fill_survey": "Um die Umfrage auszufüllen, klicke bitte auf den untenstehenden Button:", "verification_email_verify_email": "E-Mail bestätigen", + "verified_link_survey_email_subject": "Deine Umfrage ist bereit zum Ausfüllen.", "weekly_summary_create_reminder_notification_body_cal_slot": "Wähle einen 15-minütigen Termin im Kalender unseres Gründers aus.", "weekly_summary_create_reminder_notification_body_dont_let_a_week_pass": "Lass keine Woche vergehen, ohne etwas über deine Nutzer zu lernen:", "weekly_summary_create_reminder_notification_body_need_help": "Brauchst Du Hilfe, die richtige Umfrage für dein Produkt zu finden?", "weekly_summary_create_reminder_notification_body_reply_email": "oder antworte auf diese E-Mail :)", "weekly_summary_create_reminder_notification_body_setup_a_new_survey": "Neue Umfrage einrichten", - "weekly_summary_create_reminder_notification_body_text": "Wir würden dir gerne eine wöchentliche Zusammenfassung schicken, aber momentan laufen keine Umfragen für {projectName}." + "weekly_summary_create_reminder_notification_body_text": "Wir würden dir gerne eine wöchentliche Zusammenfassung schicken, aber momentan laufen keine Umfragen für {projectName}.", + "weekly_summary_email_subject": "{projectName} Nutzer-Insights – Letzte Woche von Formbricks" }, "environments": { "actions": { diff --git a/packages/lib/messages/en-US.json b/packages/lib/messages/en-US.json index c16e36672d..67a95ef285 100644 --- a/packages/lib/messages/en-US.json +++ b/packages/lib/messages/en-US.json @@ -443,24 +443,36 @@ "you_will_be_downgraded_to_the_community_edition_on_date": "You will be downgraded to the Community Edition on {date}." }, "emails": { + "accept": "Accept", + "click_or_drag_to_upload_files": "Click or drag to upload files.", "email_customization_preview_email_heading": "Hey {userName}", + "email_customization_preview_email_subject": "Formbricks Email Customization Preview", "email_customization_preview_email_text": "This is an email preview to show you which logo will be rendered in the emails.", + "email_footer_text_1": "Have a great day!", + "email_footer_text_2": "The Formbricks Team", + "email_template_text_1": "This email was sent via Formbricks.", "embed_survey_preview_email_didnt_request": "Didn't request this?", "embed_survey_preview_email_environment_id": "Environment ID", "embed_survey_preview_email_fight_spam": "Help us fight spam and forward this mail to hola@formbricks.com", "embed_survey_preview_email_heading": "Preview Email Embed", + "embed_survey_preview_email_subject": "Formbricks Email Survey Preview", "embed_survey_preview_email_text": "This is how the code snippet looks embedded into an email:", "forgot_password_email_change_password": "Change password", "forgot_password_email_did_not_request": "If you didn't request this, please ignore this email.", "forgot_password_email_heading": "Change password", "forgot_password_email_link_valid_for_24_hours": "The link is valid for 24 hours.", + "forgot_password_email_subject": "Reset your Formbricks password", "forgot_password_email_text": "You have requested a link to change your password. You can do this by clicking the link below:", + "imprint": "Imprint", "invite_accepted_email_heading": "Hey", + "invite_accepted_email_subject": "You've got a new organization member!", "invite_accepted_email_text_par1": "Just letting you know that", "invite_accepted_email_text_par2": "accepted your invitation. Have fun collaborating!", + "invite_email_button_label": "Join organization", "invite_email_heading": "Hey", "invite_email_text_par1": "Your colleague", "invite_email_text_par2": "invited you to join them at Formbricks. To accept the invitation, please click the link below:", + "invite_member_email_subject": "You're invited to collaborate on Formbricks!", "live_survey_notification_completed": "Completed", "live_survey_notification_draft": "Draft", "live_survey_notification_in_progress": "In Progress", @@ -483,13 +495,22 @@ "notification_insight_displays": "Displays", "notification_insight_responses": "Responses", "notification_insight_surveys": "Surveys", + "onboarding_invite_email_button_label": "Join {inviterName}'s organization", "onboarding_invite_email_connect_formbricks": "Connect Formbricks to your app or website via HTML Snippet or NPM in just a few minutes.", "onboarding_invite_email_create_account": "Create an account to join {inviterName}'s organization.", "onboarding_invite_email_done": "Done ✅", "onboarding_invite_email_get_started_in_minutes": "Get Started in Minutes", "onboarding_invite_email_heading": "Hey ", + "onboarding_invite_email_subject": "{inviterName} needs a hand setting up Formbricks. Can you help out?", "password_changed_email_heading": "Password changed", "password_changed_email_text": "Your password has been changed successfully.", + "password_reset_notify_email_subject": "Your Formbricks password has been changed", + "privacy_policy": "Privacy Policy", + "reject": "Reject", + "response_finished_email_subject": "A response for {surveyName} was completed ✅", + "response_finished_email_subject_with_email": "{personEmail} just completed your {surveyName} survey ✅", + "schedule_your_meeting": "Schedule your meeting", + "select_a_date": "Select a date", "survey_response_finished_email_congrats": "Congrats, you received a new response to your survey! Someone just completed your survey: {surveyName}", "survey_response_finished_email_dont_want_notifications": "Don't want to get these notifications?", "survey_response_finished_email_hey": "Hey 👋", @@ -512,12 +533,14 @@ "verification_email_thanks": "Thanks for validating your email!", "verification_email_to_fill_survey": "To fill out the survey please click on the button below:", "verification_email_verify_email": "Verify email", + "verified_link_survey_email_subject": "Your survey is ready to be filled out.", "weekly_summary_create_reminder_notification_body_cal_slot": "Pick a 15-minute slot in our CEOs calendar", "weekly_summary_create_reminder_notification_body_dont_let_a_week_pass": "Don't let a week pass without learning about your users:", "weekly_summary_create_reminder_notification_body_need_help": "Need help finding the right survey for your product?", "weekly_summary_create_reminder_notification_body_reply_email": "or reply to this email :)", "weekly_summary_create_reminder_notification_body_setup_a_new_survey": "Setup a new survey", - "weekly_summary_create_reminder_notification_body_text": "We'd love to send you a Weekly Summary, but currently there are no surveys running for {projectName}." + "weekly_summary_create_reminder_notification_body_text": "We'd love to send you a Weekly Summary, but currently there are no surveys running for {projectName}.", + "weekly_summary_email_subject": "{projectName} User Insights - Last Week by Formbricks" }, "environments": { "actions": { diff --git a/packages/lib/messages/fr-FR.json b/packages/lib/messages/fr-FR.json index 8bf6938fa5..1bd689ee22 100644 --- a/packages/lib/messages/fr-FR.json +++ b/packages/lib/messages/fr-FR.json @@ -443,24 +443,36 @@ "you_will_be_downgraded_to_the_community_edition_on_date": "Vous serez rétrogradé à l'édition communautaire le {date}." }, "emails": { + "accept": "Accepter", + "click_or_drag_to_upload_files": "Cliquez ou faites glisser pour télécharger des fichiers.", "email_customization_preview_email_heading": "Salut {userName}", + "email_customization_preview_email_subject": "Aperçu de la personnalisation des e-mails Formbricks", "email_customization_preview_email_text": "C'est une prévisualisation d'e-mail pour vous montrer quel logo sera rendu dans les e-mails.", + "email_footer_text_1": "Passe une belle journée !", + "email_footer_text_2": "L'équipe Formbricks", + "email_template_text_1": "Cet e-mail a été envoyé via Formbricks.", "embed_survey_preview_email_didnt_request": "Vous n'avez pas demandé cela ?", "embed_survey_preview_email_environment_id": "ID d'environnement", "embed_survey_preview_email_fight_spam": "Aidez-nous à lutter contre le spam et transférez ce mail à hola@formbricks.com.", "embed_survey_preview_email_heading": "Aperçu de l'email intégré", + "embed_survey_preview_email_subject": "Aperçu du sondage par e-mail Formbricks", "embed_survey_preview_email_text": "C'est ainsi que le code s'affiche intégré dans un e-mail :", "forgot_password_email_change_password": "Changer le mot de passe", "forgot_password_email_did_not_request": "Si vous n'avez pas demandé cela, veuillez ignorer cet e-mail.", "forgot_password_email_heading": "Changer le mot de passe", "forgot_password_email_link_valid_for_24_hours": "Le lien est valable pendant 24 heures.", + "forgot_password_email_subject": "Réinitialise ton mot de passe Formbricks", "forgot_password_email_text": "Vous avez demandé un lien pour changer votre mot de passe. Vous pouvez le faire en cliquant sur le lien ci-dessous :", + "imprint": "Impressum", "invite_accepted_email_heading": "Salut", + "invite_accepted_email_subject": "Vous avez un nouveau membre dans votre organisation !", "invite_accepted_email_text_par1": "Je te fais savoir que", "invite_accepted_email_text_par2": "accepté votre invitation. Amusez-vous bien à collaborer !", + "invite_email_button_label": "Rejoindre l'organisation", "invite_email_heading": "Salut", "invite_email_text_par1": "Votre collègue", "invite_email_text_par2": "vous a invité à les rejoindre sur Formbricks. Pour accepter l'invitation, veuillez cliquer sur le lien ci-dessous :", + "invite_member_email_subject": "Vous avez été invité à collaborer sur Formbricks !", "live_survey_notification_completed": "Terminé", "live_survey_notification_draft": "Brouillon", "live_survey_notification_in_progress": "En cours", @@ -483,13 +495,22 @@ "notification_insight_displays": "Affichages", "notification_insight_responses": "Réponses", "notification_insight_surveys": "Enquêtes", + "onboarding_invite_email_button_label": "Rejoins l'organisation de {inviterName}", "onboarding_invite_email_connect_formbricks": "Connectez Formbricks à votre application ou site web via un extrait HTML ou NPM en quelques minutes seulement.", "onboarding_invite_email_create_account": "Créez un compte pour rejoindre l'organisation de {inviterName}.", "onboarding_invite_email_done": "Fait ✅", "onboarding_invite_email_get_started_in_minutes": "Commencez en quelques minutes", "onboarding_invite_email_heading": "Salut ", + "onboarding_invite_email_subject": "{inviterName} a besoin d'aide pour configurer Formbricks. Peux-tu l'aider ?", "password_changed_email_heading": "Mot de passe changé", "password_changed_email_text": "Votre mot de passe a été changé avec succès.", + "password_reset_notify_email_subject": "Ton mot de passe Formbricks a été changé", + "privacy_policy": "Politique de confidentialité", + "reject": "Rejeter", + "response_finished_email_subject": "Une réponse pour {surveyName} a été complétée ✅", + "response_finished_email_subject_with_email": "{personEmail} vient de compléter votre enquête {surveyName} ✅", + "schedule_your_meeting": "Planifier votre rendez-vous", + "select_a_date": "Sélectionner une date", "survey_response_finished_email_congrats": "Félicitations, vous avez reçu une nouvelle réponse à votre enquête ! Quelqu'un vient de compléter votre enquête : {surveyName}", "survey_response_finished_email_dont_want_notifications": "Vous ne voulez pas recevoir ces notifications ?", "survey_response_finished_email_hey": "Salut 👋", @@ -512,12 +533,14 @@ "verification_email_thanks": "Merci de valider votre email !", "verification_email_to_fill_survey": "Pour remplir le questionnaire, veuillez cliquer sur le bouton ci-dessous :", "verification_email_verify_email": "Vérifier l'email", + "verified_link_survey_email_subject": "Votre enquête est prête à être remplie.", "weekly_summary_create_reminder_notification_body_cal_slot": "Choisissez un créneau de 15 minutes dans le calendrier de notre PDG.", "weekly_summary_create_reminder_notification_body_dont_let_a_week_pass": "Ne laissez pas une semaine passer sans en apprendre davantage sur vos utilisateurs :", "weekly_summary_create_reminder_notification_body_need_help": "Besoin d'aide pour trouver le bon sondage pour votre produit ?", "weekly_summary_create_reminder_notification_body_reply_email": "ou répondez à cet e-mail :)", "weekly_summary_create_reminder_notification_body_setup_a_new_survey": "Configurer une nouvelle enquête", - "weekly_summary_create_reminder_notification_body_text": "Nous aimerions vous envoyer un résumé hebdomadaire, mais actuellement, il n'y a pas d'enquêtes en cours pour {projectName}." + "weekly_summary_create_reminder_notification_body_text": "Nous aimerions vous envoyer un résumé hebdomadaire, mais actuellement, il n'y a pas d'enquêtes en cours pour {projectName}.", + "weekly_summary_email_subject": "Aperçu des utilisateurs de {projectName} – La semaine dernière par Formbricks" }, "environments": { "actions": { diff --git a/packages/lib/messages/pt-BR.json b/packages/lib/messages/pt-BR.json index 40171fe4c0..2b90124cbf 100644 --- a/packages/lib/messages/pt-BR.json +++ b/packages/lib/messages/pt-BR.json @@ -443,24 +443,36 @@ "you_will_be_downgraded_to_the_community_edition_on_date": "Você será rebaixado para a Edição Comunitária em {data}." }, "emails": { + "accept": "Aceitar", + "click_or_drag_to_upload_files": "Clique ou arraste para fazer o upload de arquivos.", "email_customization_preview_email_heading": "Oi {userName}", + "email_customization_preview_email_subject": "Prévia da personalização de e-mails do Formbricks", "email_customization_preview_email_text": "Esta é uma pré-visualização de e-mail para mostrar qual logo será renderizado nos e-mails.", + "email_footer_text_1": "Tenha um ótimo dia!", + "email_footer_text_2": "O time Formbricks", + "email_template_text_1": "Este e-mail foi enviado através do Formbricks.", "embed_survey_preview_email_didnt_request": "Não pediu isso?", "embed_survey_preview_email_environment_id": "ID do Ambiente", "embed_survey_preview_email_fight_spam": "Ajude a gente a combater spam e encaminhe este e-mail para hola@formbricks.com", "embed_survey_preview_email_heading": "Pré-visualizar Incorporação de Email", + "embed_survey_preview_email_subject": "Prévia da pesquisa por e-mail do Formbricks", "embed_survey_preview_email_text": "É assim que o trecho de código fica embutido em um e-mail:", "forgot_password_email_change_password": "Mudar senha", "forgot_password_email_did_not_request": "Se você não solicitou isso, por favor ignore este e-mail.", "forgot_password_email_heading": "Mudar senha", "forgot_password_email_link_valid_for_24_hours": "O link é válido por 24 horas.", + "forgot_password_email_subject": "Redefinir sua senha Formbricks", "forgot_password_email_text": "Você pediu um link pra trocar sua senha. Você pode fazer isso clicando no link abaixo:", + "imprint": "Impressum", "invite_accepted_email_heading": "E aí", + "invite_accepted_email_subject": "Você tem um novo membro na sua organização!", "invite_accepted_email_text_par1": "Só pra te avisar que", "invite_accepted_email_text_par2": "aceitou seu convite. Divirta-se colaborando!", + "invite_email_button_label": "Entrar na organização", "invite_email_heading": "E aí", "invite_email_text_par1": "Seu colega", "invite_email_text_par2": "te convidou para se juntar a eles na Formbricks. Para aceitar o convite, por favor clique no link abaixo:", + "invite_member_email_subject": "Você foi convidado a colaborar no Formbricks!", "live_survey_notification_completed": "Concluído", "live_survey_notification_draft": "Rascunho", "live_survey_notification_in_progress": "Em andamento", @@ -483,13 +495,22 @@ "notification_insight_displays": "telas", "notification_insight_responses": "Respostas", "notification_insight_surveys": "pesquisas", + "onboarding_invite_email_button_label": "Entre na organização de {inviterName}", "onboarding_invite_email_connect_formbricks": "Conecte o Formbricks ao seu app ou site via HTML Snippet ou NPM em apenas alguns minutos.", "onboarding_invite_email_create_account": "Crie uma conta para entrar na organização de {inviterName}.", "onboarding_invite_email_done": "Feito ✅", "onboarding_invite_email_get_started_in_minutes": "Comece em Minutos", "onboarding_invite_email_heading": "Oi ", + "onboarding_invite_email_subject": "{inviterName} precisa de ajuda para configurar o Formbricks. Você pode ajudar?", "password_changed_email_heading": "Senha alterada", "password_changed_email_text": "Sua senha foi alterada com sucesso.", + "password_reset_notify_email_subject": "Sua senha Formbricks foi alterada", + "privacy_policy": "Política de Privacidade", + "reject": "Rejeitar", + "response_finished_email_subject": "Uma resposta para {surveyName} foi concluída ✅", + "response_finished_email_subject_with_email": "{personEmail} acabou de completar sua pesquisa {surveyName} ✅", + "schedule_your_meeting": "Agendar sua reunião", + "select_a_date": "Selecione uma data", "survey_response_finished_email_congrats": "Parabéns, você recebeu uma nova resposta na sua pesquisa! Alguém acabou de completar sua pesquisa: {surveyName}", "survey_response_finished_email_dont_want_notifications": "Não quer receber essas notificações?", "survey_response_finished_email_hey": "E aí 👋", @@ -512,12 +533,14 @@ "verification_email_thanks": "Valeu por validar seu e-mail!", "verification_email_to_fill_survey": "Para preencher a pesquisa, por favor clique no botão abaixo:", "verification_email_verify_email": "Verificar e-mail", + "verified_link_survey_email_subject": "Sua pesquisa está pronta para ser preenchida.", "weekly_summary_create_reminder_notification_body_cal_slot": "Escolha um horário de 15 minutos na agenda do nosso CEO", "weekly_summary_create_reminder_notification_body_dont_let_a_week_pass": "Não deixe uma semana passar sem aprender sobre seus usuários:", "weekly_summary_create_reminder_notification_body_need_help": "Precisa de ajuda pra encontrar a pesquisa certa pro seu produto?", "weekly_summary_create_reminder_notification_body_reply_email": "ou responde a esse e-mail :)", "weekly_summary_create_reminder_notification_body_setup_a_new_survey": "Configurar uma nova pesquisa", - "weekly_summary_create_reminder_notification_body_text": "Adoraríamos te enviar um Resumo Semanal, mas no momento não há pesquisas em andamento para {projectName}." + "weekly_summary_create_reminder_notification_body_text": "Adoraríamos te enviar um Resumo Semanal, mas no momento não há pesquisas em andamento para {projectName}.", + "weekly_summary_email_subject": "Insights de usuários do {projectName} – Semana passada por Formbricks" }, "environments": { "actions": { diff --git a/packages/lib/package.json b/packages/lib/package.json index 3e3e6a110c..9f23a0abb0 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -15,38 +15,38 @@ "test": "dotenv -e ../../.env -- vitest run" }, "dependencies": { - "@ai-sdk/azure": "1.0.10", - "@aws-sdk/client-s3": "3.712.0", - "@aws-sdk/s3-presigned-post": "3.712.0", - "@aws-sdk/s3-request-presigner": "3.712.0", + "@ai-sdk/azure": "1.1.9", + "@aws-sdk/client-s3": "3.741.0", + "@aws-sdk/s3-presigned-post": "3.741.0", + "@aws-sdk/s3-request-presigner": "3.741.0", "@formbricks/api": "workspace:*", "@formbricks/database": "workspace:*", "@formbricks/types": "workspace:*", "@paralleldrive/cuid2": "2.2.2", - "@t3-oss/env-nextjs": "0.11.1", - "@ungap/structured-clone": "1.2.1", - "aws-crt": "1.24.0", + "@t3-oss/env-nextjs": "0.12.0", + "@ungap/structured-clone": "1.3.0", + "aws-crt": "1.25.3", "date-fns": "4.1.0", "jsonwebtoken": "9.0.2", "markdown-it": "14.1.0", "mime-types": "2.1.35", "nanoid": "5.0.9", "next-auth": "4.24.11", - "posthog-node": "4.3.2", + "posthog-node": "4.4.1", "qrcode": "1.5.4", "server-only": "0.0.1", "superjson": "2.2.2", - "tailwind-merge": "2.5.5" + "tailwind-merge": "3.0.1" }, "devDependencies": { "@formbricks/config-typescript": "workspace:*", "@formbricks/eslint-config": "workspace:*", - "@types/jsonwebtoken": "9.0.7", + "@types/jsonwebtoken": "9.0.8", "@types/mime-types": "2.1.4", "@types/ungap__structured-clone": "1.2.0", "dotenv": "16.4.7", "ts-node": "10.9.2", - "vitest": "2.1.8", + "vitest": "3.0.5", "vitest-mock-extended": "2.0.2" } } diff --git a/packages/react-native/README.md b/packages/react-native/README.md index 5eae587155..81f446fb41 100644 --- a/packages/react-native/README.md +++ b/packages/react-native/README.md @@ -24,20 +24,10 @@ npm install @formbricks/react-native import Formbricks, { track } from "@formbricks/react-native"; export default function App() { - const config = { - environmentId: "your-environment-id", - apiHost: "https://app.formbricks.com", - userId: "hello-user", // optional - attributes: { - // optional - plan: "free", - }, - }; - return ( {/* Your app code */} - + ); } diff --git a/packages/react-native/package.json b/packages/react-native/package.json index b12f772bf2..fbc7639d5c 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -1,6 +1,6 @@ { "name": "@formbricks/react-native", - "version": "1.3.1", + "version": "2.0.0", "license": "MIT", "description": "Formbricks React Native SDK allows you to connect your app to Formbricks, display surveys and trigger events.", "homepage": "https://formbricks.com", @@ -36,24 +36,29 @@ "build:dev": "tsc && vite build --mode dev", "lint": "eslint src --ext .ts,.js,.tsx,.jsx", "dev": "vite build --watch --mode dev", - "clean": "rimraf .turbo node_modules dist .turbo" + "clean": "rimraf .turbo node_modules dist .turbo", + "test": "vitest", + "coverage": "vitest run --coverage" + }, + "dependencies": { + "zod": "3.24.1" }, "devDependencies": { "@formbricks/api": "workspace:*", "@formbricks/config-typescript": "workspace:*", - "@formbricks/lib": "workspace:*", - "@formbricks/types": "workspace:*", - "@react-native-async-storage/async-storage": "2.1.0", "@types/react": "18.3.11", + "@vitest/coverage-v8": "3.0.4", "react": "18.3.1", "react-native": "0.74.5", "terser": "5.37.0", "vite": "6.0.9", - "vite-plugin-dts": "4.3.0" + "vite-plugin-dts": "4.3.0", + "vitest": "3.0.4" }, "peerDependencies": { "react": ">=16.8.0", "react-native": ">=0.60.0", - "react-native-webview": ">=13.0.0" + "react-native-webview": ">=13.0.0", + "@react-native-async-storage/async-storage": ">=2.1.0" } } diff --git a/packages/react-native/src/formbricks.tsx b/packages/react-native/src/components/formbricks.tsx similarity index 60% rename from packages/react-native/src/formbricks.tsx rename to packages/react-native/src/components/formbricks.tsx index a23b7a18bf..ac0ce6054c 100644 --- a/packages/react-native/src/formbricks.tsx +++ b/packages/react-native/src/components/formbricks.tsx @@ -1,26 +1,25 @@ import React, { useCallback, useEffect, useSyncExternalStore } from "react"; -import { type TJsConfigInput } from "@formbricks/types/js"; -import { Logger } from "../../js-core/src/lib/logger"; -import { init } from "./lib"; -import { SurveyStore } from "./lib/survey-store"; -import { SurveyWebView } from "./survey-web-view"; +import { SurveyWebView } from "@/components/survey-web-view"; +import { init } from "@/lib/common/initialize"; +import { Logger } from "@/lib/common/logger"; +import { SurveyStore } from "@/lib/survey/store"; interface FormbricksProps { - initConfig: TJsConfigInput; + appUrl: string; + environmentId: string; } + const surveyStore = SurveyStore.getInstance(); const logger = Logger.getInstance(); -export function Formbricks({ initConfig }: FormbricksProps): React.JSX.Element | null { +export function Formbricks({ appUrl, environmentId }: FormbricksProps): React.JSX.Element | null { // initializes sdk useEffect(() => { const initialize = async (): Promise => { try { await init({ - environmentId: initConfig.environmentId, - apiHost: initConfig.apiHost, - userId: initConfig.userId, - attributes: initConfig.attributes, + environmentId, + appUrl, }); } catch { logger.debug("Initialization failed"); @@ -30,7 +29,7 @@ export function Formbricks({ initConfig }: FormbricksProps): React.JSX.Element | initialize().catch(() => { logger.debug("Initialization error"); }); - }, [initConfig]); + }, [environmentId, appUrl]); const subscribe = useCallback((callback: () => void) => { const unsubscribe = surveyStore.subscribe(callback); diff --git a/packages/react-native/src/survey-web-view.tsx b/packages/react-native/src/components/survey-web-view.tsx similarity index 77% rename from packages/react-native/src/survey-web-view.tsx rename to packages/react-native/src/components/survey-web-view.tsx index 7031afa565..3aaf684169 100644 --- a/packages/react-native/src/survey-web-view.tsx +++ b/packages/react-native/src/components/survey-web-view.tsx @@ -4,19 +4,17 @@ import React, { type JSX, useEffect, useMemo, useRef, useState } from "react"; import { Modal } from "react-native"; import { WebView, type WebViewMessageEvent } from "react-native-webview"; import { FormbricksAPI } from "@formbricks/api"; -import { ResponseQueue } from "@formbricks/lib/responseQueue"; -import { SurveyState } from "@formbricks/lib/surveyState"; -import { getStyling } from "@formbricks/lib/utils/styling"; -import type { SurveyInlineProps } from "@formbricks/types/formbricks-surveys"; -import { ZJsRNWebViewOnMessageData } from "@formbricks/types/js"; -import type { TJsEnvironmentStateSurvey, TJsFileUploadParams, TJsPersonState } from "@formbricks/types/js"; -import type { TResponseUpdate } from "@formbricks/types/responses"; -import type { TUploadFileConfig } from "@formbricks/types/storage"; -import { Logger } from "../../js-core/src/lib/logger"; -import { filterSurveys, getDefaultLanguageCode, getLanguageCode } from "../../js-core/src/lib/utils"; -import { RNConfig } from "./lib/config"; -import { StorageAPI } from "./lib/storage"; -import { SurveyStore } from "./lib/survey-store"; +import { RNConfig } from "@/lib/common/config"; +import { StorageAPI } from "@/lib/common/file-upload"; +import { Logger } from "@/lib/common/logger"; +import { ResponseQueue } from "@/lib/common/response-queue"; +import { filterSurveys, getDefaultLanguageCode, getLanguageCode, getStyling } from "@/lib/common/utils"; +import { SurveyState } from "@/lib/survey/state"; +import { SurveyStore } from "@/lib/survey/store"; +import { type TEnvironmentStateSurvey, type TUserState, ZJsRNWebViewOnMessageData } from "@/types/config"; +import type { TResponseUpdate } from "@/types/response"; +import type { TFileUploadParams, TUploadFileConfig } from "@/types/storage"; +import type { SurveyInlineProps } from "@/types/survey"; const appConfig = RNConfig.getInstance(); const logger = Logger.getInstance(); @@ -25,7 +23,7 @@ logger.configure({ logLevel: "debug" }); const surveyStore = SurveyStore.getInstance(); interface SurveyWebViewProps { - survey: TJsEnvironmentStateSurvey; + survey: TEnvironmentStateSurvey; } export function SurveyWebView({ survey }: SurveyWebViewProps): JSX.Element | undefined { @@ -33,22 +31,23 @@ export function SurveyWebView({ survey }: SurveyWebViewProps): JSX.Element | und const [isSurveyRunning, setIsSurveyRunning] = useState(false); const [showSurvey, setShowSurvey] = useState(false); - const project = appConfig.get().environmentState.data.project; - const attributes = appConfig.get().attributes; + const project = appConfig.get().environment.data.project; + const language = appConfig.get().user.data.language; const styling = getStyling(project, survey); const isBrandingEnabled = project.inAppSurveyBranding; const isMultiLanguageSurvey = survey.languages.length > 1; + const [languageCode, setLanguageCode] = useState("default"); const [surveyState, setSurveyState] = useState( - new SurveyState(survey.id, null, null, appConfig.get().personState.data.userId) + new SurveyState(survey.id, null, null, appConfig.get().user.data.userId) ); const responseQueue = useMemo( () => new ResponseQueue( { - apiHost: appConfig.get().apiHost, + appUrl: appConfig.get().appUrl, environmentId: appConfig.get().environmentId, retryAttempts: 2, setSurveyState, @@ -59,7 +58,30 @@ export function SurveyWebView({ survey }: SurveyWebViewProps): JSX.Element | und ); useEffect(() => { - if (!isSurveyRunning && survey.delay) { + if (isMultiLanguageSurvey) { + const displayLanguage = getLanguageCode(survey, language); + if (!displayLanguage) { + logger.debug(`Survey "${survey.name}" is not available in specified language.`); + setIsSurveyRunning(false); + setShowSurvey(false); + surveyStore.resetSurvey(); + return; + } + setLanguageCode(displayLanguage); + setIsSurveyRunning(true); + } else { + setIsSurveyRunning(true); + } + }, [isMultiLanguageSurvey, language, survey]); + + useEffect(() => { + if (!isSurveyRunning) { + setShowSurvey(false); + return; + } + + if (survey.delay) { + logger.debug(`Delaying survey "${survey.name}" by ${String(survey.delay)} seconds`); const timerId = setTimeout(() => { setShowSurvey(true); }, survey.delay * 1000); @@ -67,26 +89,13 @@ export function SurveyWebView({ survey }: SurveyWebViewProps): JSX.Element | und return () => { clearTimeout(timerId); }; - } else if (!survey.delay) { - setShowSurvey(true); } - }, [survey.delay, isSurveyRunning]); - let languageCode = "default"; - - if (isMultiLanguageSurvey) { - const displayLanguage = getLanguageCode(survey, attributes); - //if survey is not available in selected language, survey wont be shown - if (!displayLanguage) { - logger.debug(`Survey "${survey.name}" is not available in specified language.`); - setIsSurveyRunning(true); - return; - } - languageCode = displayLanguage; - } + setShowSurvey(true); + }, [survey.delay, isSurveyRunning, survey.name]); const addResponseToQueue = (responseUpdate: TResponseUpdate): void => { - const { userId } = appConfig.get().personState.data; + const { userId } = appConfig.get().user.data; if (userId) surveyState.updateUserId(userId); responseQueue.updateSurveyState(surveyState); @@ -101,13 +110,13 @@ export function SurveyWebView({ survey }: SurveyWebViewProps): JSX.Element | und }; const onCloseSurvey = (): void => { - const { environmentState, personState } = appConfig.get(); + const { environment: environmentState, user: personState } = appConfig.get(); const filteredSurveys = filterSurveys(environmentState, personState); appConfig.update({ ...appConfig.get(), - environmentState, - personState, + environment: environmentState, + user: personState, filteredSurveys, }); @@ -116,10 +125,10 @@ export function SurveyWebView({ survey }: SurveyWebViewProps): JSX.Element | und }; const createDisplay = async (surveyId: string): Promise<{ id: string }> => { - const { userId } = appConfig.get().personState.data; + const { userId } = appConfig.get().user.data; const api = new FormbricksAPI({ - apiHost: appConfig.get().apiHost, + apiHost: appConfig.get().appUrl, environmentId: appConfig.get().environmentId, }); @@ -135,21 +144,19 @@ export function SurveyWebView({ survey }: SurveyWebViewProps): JSX.Element | und return res.data; }; - const uploadFile = async ( - file: TJsFileUploadParams["file"], - params?: TUploadFileConfig - ): Promise => { - const storage = new StorageAPI(appConfig.get().apiHost, appConfig.get().environmentId); + const uploadFile = async (file: TFileUploadParams["file"], params?: TUploadFileConfig): Promise => { + const storage = new StorageAPI(appConfig.get().appUrl, appConfig.get().environmentId); return await storage.uploadFile(file, params); }; return ( { setShowSurvey(false); + setIsSurveyRunning(false); }}> & { apiHost?: string }): string => { +const renderHtml = (options: Partial & { appUrl?: string }): string => { return ` @@ -368,7 +375,6 @@ const renderHtml = (options: Partial & { apiHost?: string }): window.ReactNativeWebView.postMessage(JSON.stringify({ onRetry: true })); }; - window.fileUploadPromiseCallbacks = new Map(); function onFileUpload(file, params) { @@ -422,7 +428,7 @@ const renderHtml = (options: Partial & { apiHost?: string }): } const script = document.createElement("script"); - script.src = "${options.apiHost ?? "http://localhost:3000"}/js/surveys.umd.cjs"; + script.src = "${options.appUrl ?? "http://localhost:3000"}/js/surveys.umd.cjs"; script.async = true; script.onload = () => loadSurvey(); script.onerror = (error) => { diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 19f187dadf..888a454cb4 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -1,6 +1,42 @@ -import { Formbricks } from "./formbricks"; -import { track } from "./lib"; +import { Formbricks } from "@/components/formbricks"; +import { CommandQueue } from "@/lib/common/command-queue"; +import { Logger } from "@/lib/common/logger"; +import * as Actions from "@/lib/survey/action"; +import * as Attributes from "@/lib/user/attribute"; +import * as User from "@/lib/user/user"; + +const logger = Logger.getInstance(); +logger.debug("Create command queue"); +const queue = new CommandQueue(); + +export const track = async (name: string): Promise => { + queue.add(Actions.track, true, name); + await queue.wait(); +}; + +export const setUserId = async (userId: string): Promise => { + queue.add(User.setUserId, true, userId); + await queue.wait(); +}; + +export const setAttribute = async (key: string, value: string): Promise => { + queue.add(Attributes.setAttributes, true, { [key]: value }); + await queue.wait(); +}; + +export const setAttributes = async (attributes: Record): Promise => { + queue.add(Attributes.setAttributes, true, attributes); + await queue.wait(); +}; + +export const setLanguage = async (language: string): Promise => { + queue.add(Attributes.setAttributes, true, { language }); + await queue.wait(); +}; + +export const logout = async (): Promise => { + queue.add(User.logout, true); + await queue.wait(); +}; export default Formbricks; - -export { track }; diff --git a/packages/react-native/src/lib/attributes.ts b/packages/react-native/src/lib/attributes.ts deleted file mode 100644 index 2f861a8bf2..0000000000 --- a/packages/react-native/src/lib/attributes.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { FormbricksAPI } from "@formbricks/api"; -import type { TAttributes } from "@formbricks/types/attributes"; -import type { ApiErrorResponse } from "@formbricks/types/errors"; -import { type Result, err, ok } from "../../../js-core/src/lib/errors"; -import { Logger } from "../../../js-core/src/lib/logger"; - -const logger = Logger.getInstance(); - -export const updateAttributes = async ( - apiHost: string, - environmentId: string, - userId: string, - attributes: TAttributes -): Promise> => { - // clean attributes and remove existing attributes if config already exists - const updatedAttributes = { ...attributes }; - - // send to backend if updatedAttributes is not empty - if (Object.keys(updatedAttributes).length === 0) { - logger.debug("No attributes to update. Skipping update."); - return ok(updatedAttributes); - } - - logger.debug(`Updating attributes: ${JSON.stringify(updatedAttributes)}`); - - const api = new FormbricksAPI({ - apiHost, - environmentId, - }); - - const res = await api.client.attribute.update({ userId, attributes: updatedAttributes }); - - if (res.ok) { - if (res.data.details) { - Object.entries(res.data.details).forEach(([key, value]) => { - logger.debug(`${key}: ${value}`); - }); - } - - return ok(updatedAttributes); - } - - const responseError = res.error; - - if (responseError.details?.ignore) { - logger.error(responseError.message); - return ok(updatedAttributes); - } - - return err({ - code: responseError.code, - status: responseError.status, - message: `Error updating person with userId ${userId}`, - url: new URL(`${apiHost}/api/v1/client/${environmentId}/people/${userId}/attributes`), - responseMessage: responseError.responseMessage, - }); -}; diff --git a/packages/react-native/src/lib/command-queue.ts b/packages/react-native/src/lib/common/command-queue.ts similarity index 82% rename from packages/react-native/src/lib/command-queue.ts rename to packages/react-native/src/lib/common/command-queue.ts index 717b1dda2e..eb9ed7dea8 100644 --- a/packages/react-native/src/lib/command-queue.ts +++ b/packages/react-native/src/lib/common/command-queue.ts @@ -1,6 +1,7 @@ -import { wrapThrowsAsync } from "@formbricks/types/error-handlers"; -import { ErrorHandler, type Result } from "../../../js-core/src/lib/errors"; -import { checkInitialized } from "./initialize"; +/* eslint-disable no-console -- we need to log global errors */ +import { checkInitialized } from "@/lib/common/initialize"; +import { wrapThrowsAsync } from "@/lib/common/utils"; +import type { Result } from "@/types/error"; export class CommandQueue { private queue: { @@ -36,7 +37,6 @@ export class CommandQueue { private async run(): Promise { this.running = true; while (this.queue.length > 0) { - const errorHandler = ErrorHandler.getInstance(); const currentItem = this.queue.shift(); if (!currentItem) continue; @@ -47,7 +47,6 @@ export class CommandQueue { const initResult = checkInitialized(); if (!initResult.ok) { - errorHandler.handle(initResult.error); continue; } } @@ -59,9 +58,9 @@ export class CommandQueue { const result = await wrapThrowsAsync(executeCommand)(); if (!result.ok) { - errorHandler.handle(result.error); + console.error("🧱 Formbricks - Global error: ", result.error); } else if (!result.data.ok) { - errorHandler.handle(result.data.error); + console.error("🧱 Formbricks - Global error: ", result.data.error); } } this.running = false; diff --git a/packages/react-native/src/lib/config.ts b/packages/react-native/src/lib/common/config.ts similarity index 73% rename from packages/react-native/src/lib/config.ts rename to packages/react-native/src/lib/common/config.ts index 085dbdb382..6da3f0e827 100644 --- a/packages/react-native/src/lib/config.ts +++ b/packages/react-native/src/lib/common/config.ts @@ -1,13 +1,15 @@ /* eslint-disable no-console -- Required for error logging */ -import AsyncStorage from "@react-native-async-storage/async-storage"; -import { type Result, err, ok, wrapThrowsAsync } from "@formbricks/types/error-handlers"; -import type { TJsConfig, TJsConfigUpdateInput } from "@formbricks/types/js"; -import { RN_ASYNC_STORAGE_KEY } from "../../../js-core/src/lib/constants"; +import { AsyncStorage } from "@/lib/common/storage"; +import { wrapThrowsAsync } from "@/lib/common/utils"; +import type { TConfig, TConfigUpdateInput } from "@/types/config"; +import { type Result, err, ok } from "@/types/error"; + +export const RN_ASYNC_STORAGE_KEY = "formbricks-react-native"; export class RNConfig { private static instance: RNConfig | null = null; - private config: TJsConfig | null = null; + private config: TConfig | null = null; private constructor() { this.loadFromStorage() @@ -29,7 +31,7 @@ export class RNConfig { return RNConfig.instance; } - public update(newConfig: TJsConfigUpdateInput): void { + public update(newConfig: TConfigUpdateInput): void { this.config = { ...this.config, ...newConfig, @@ -42,24 +44,24 @@ export class RNConfig { void this.saveToStorage(); } - public get(): TJsConfig { + public get(): TConfig { if (!this.config) { throw new Error("config is null, maybe the init function was not called?"); } return this.config; } - public async loadFromStorage(): Promise> { + public async loadFromStorage(): Promise> { try { const savedConfig = await AsyncStorage.getItem(RN_ASYNC_STORAGE_KEY); if (savedConfig) { - const parsedConfig = JSON.parse(savedConfig) as TJsConfig; + const parsedConfig = JSON.parse(savedConfig) as TConfig; // check if the config has expired if ( // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- need to check if expiresAt is set - parsedConfig.environmentState.expiresAt && - new Date(parsedConfig.environmentState.expiresAt) <= new Date() + parsedConfig.environment.expiresAt && + new Date(parsedConfig.environment.expiresAt) <= new Date() ) { return err(new Error("Config in local storage has expired")); } diff --git a/packages/react-native/src/lib/event-listeners.ts b/packages/react-native/src/lib/common/event-listeners.ts similarity index 71% rename from packages/react-native/src/lib/event-listeners.ts rename to packages/react-native/src/lib/common/event-listeners.ts index ecd46179cc..f2fea20f12 100644 --- a/packages/react-native/src/lib/event-listeners.ts +++ b/packages/react-native/src/lib/common/event-listeners.ts @@ -1,32 +1,32 @@ import { addEnvironmentStateExpiryCheckListener, clearEnvironmentStateExpiryCheckListener, -} from "./environment-state"; -import { addPersonStateExpiryCheckListener, clearPersonStateExpiryCheckListener } from "./person-state"; +} from "@/lib/environment/state"; +import { addUserStateExpiryCheckListener, clearUserStateExpiryCheckListener } from "@/lib/user/state"; let areRemoveEventListenersAdded = false; export const addEventListeners = (): void => { addEnvironmentStateExpiryCheckListener(); - addPersonStateExpiryCheckListener(); + addUserStateExpiryCheckListener(); }; export const addCleanupEventListeners = (): void => { if (areRemoveEventListenersAdded) return; clearEnvironmentStateExpiryCheckListener(); - clearPersonStateExpiryCheckListener(); + clearUserStateExpiryCheckListener(); areRemoveEventListenersAdded = true; }; export const removeCleanupEventListeners = (): void => { if (!areRemoveEventListenersAdded) return; clearEnvironmentStateExpiryCheckListener(); - clearPersonStateExpiryCheckListener(); + clearUserStateExpiryCheckListener(); areRemoveEventListenersAdded = false; }; export const removeAllEventListeners = (): void => { clearEnvironmentStateExpiryCheckListener(); - clearPersonStateExpiryCheckListener(); + clearUserStateExpiryCheckListener(); removeCleanupEventListeners(); }; diff --git a/packages/react-native/src/lib/storage.ts b/packages/react-native/src/lib/common/file-upload.ts similarity index 91% rename from packages/react-native/src/lib/storage.ts rename to packages/react-native/src/lib/common/file-upload.ts index ff956c72b7..a4fb21d703 100644 --- a/packages/react-native/src/lib/storage.ts +++ b/packages/react-native/src/lib/common/file-upload.ts @@ -1,12 +1,12 @@ /* eslint-disable no-console -- used for error logging */ -import type { TUploadFileConfig, TUploadFileResponse } from "@formbricks/types/storage"; +import { type TUploadFileConfig, type TUploadFileResponse } from "@/types/storage"; export class StorageAPI { - private apiHost: string; + private appUrl: string; private environmentId: string; - constructor(apiHost: string, environmentId: string) { - this.apiHost = apiHost; + constructor(appUrl: string, environmentId: string) { + this.appUrl = appUrl; this.environmentId = environmentId; } @@ -29,7 +29,7 @@ export class StorageAPI { surveyId, }; - const response = await fetch(`${this.apiHost}/api/v1/client/${this.environmentId}/storage`, { + const response = await fetch(`${this.appUrl}/api/v1/client/${this.environmentId}/storage`, { method: "POST", headers: { "Content-Type": "application/json", @@ -86,7 +86,7 @@ export class StorageAPI { let uploadResponse: Response = {} as Response; - const signedUrlCopy = signedUrl.replace("http://localhost:3000", this.apiHost); + const signedUrlCopy = signedUrl.replace("http://localhost:3000", this.appUrl); try { uploadResponse = await fetch(signedUrlCopy, { diff --git a/packages/react-native/src/lib/common/initialize.ts b/packages/react-native/src/lib/common/initialize.ts new file mode 100644 index 0000000000..f5bd007297 --- /dev/null +++ b/packages/react-native/src/lib/common/initialize.ts @@ -0,0 +1,281 @@ +import { RNConfig, RN_ASYNC_STORAGE_KEY } from "@/lib/common/config"; +import { + addCleanupEventListeners, + addEventListeners, + removeAllEventListeners, +} from "@/lib/common/event-listeners"; +import { Logger } from "@/lib/common/logger"; +import { AsyncStorage } from "@/lib/common/storage"; +import { filterSurveys, isNowExpired, wrapThrowsAsync } from "@/lib/common/utils"; +import { fetchEnvironmentState } from "@/lib/environment/state"; +import { DEFAULT_USER_STATE_NO_USER_ID } from "@/lib/user/state"; +import { sendUpdatesToBackend } from "@/lib/user/update"; +import { type TConfig, type TConfigInput, type TEnvironmentState, type TUserState } from "@/types/config"; +import { + type MissingFieldError, + type MissingPersonError, + type NetworkError, + type NotInitializedError, + type Result, + err, + okVoid, +} from "@/types/error"; + +let isInitialized = false; + +export const setIsInitialize = (state: boolean): void => { + isInitialized = state; +}; + +export const init = async ( + configInput: TConfigInput +): Promise> => { + const appConfig = RNConfig.getInstance(); + const logger = Logger.getInstance(); + + if (isInitialized) { + logger.debug("Already initialized, skipping initialization."); + return okVoid(); + } + + let existingConfig: TConfig | undefined; + try { + existingConfig = appConfig.get(); + logger.debug("Found existing configuration."); + } catch { + logger.debug("No existing configuration found."); + } + + // formbricks is in error state, skip initialization + if (existingConfig?.status.value === "error") { + logger.debug("Formbricks was set to an error state."); + + const expiresAt = existingConfig.status.expiresAt; + + if (expiresAt && isNowExpired(expiresAt)) { + logger.debug("Error state is not expired, skipping initialization"); + return okVoid(); + } + logger.debug("Error state is expired. Continue with initialization."); + } + + logger.debug("Start initialize"); + + if (!configInput.environmentId) { + logger.debug("No environmentId provided"); + return err({ + code: "missing_field", + field: "environmentId", + }); + } + + if (!configInput.appUrl) { + logger.debug("No appUrl provided"); + + return err({ + code: "missing_field", + field: "appUrl", + }); + } + + if ( + existingConfig?.environment && + existingConfig.environmentId === configInput.environmentId && + existingConfig.appUrl === configInput.appUrl + ) { + logger.debug("Configuration fits init parameters."); + let isEnvironmentStateExpired = false; + let isUserStateExpired = false; + + if (isNowExpired(existingConfig.environment.expiresAt)) { + logger.debug("Environment state expired. Syncing."); + isEnvironmentStateExpired = true; + } + + if (existingConfig.user.expiresAt && isNowExpired(existingConfig.user.expiresAt)) { + logger.debug("Person state expired. Syncing."); + isUserStateExpired = true; + } + + try { + // fetch the environment state (if expired) + let environmentState: TEnvironmentState = existingConfig.environment; + let userState: TUserState = existingConfig.user; + + if (isEnvironmentStateExpired) { + const environmentStateResponse = await fetchEnvironmentState({ + appUrl: configInput.appUrl, + environmentId: configInput.environmentId, + }); + + if (environmentStateResponse.ok) { + environmentState = environmentStateResponse.data; + } else { + logger.error( + `Error fetching environment state: ${environmentStateResponse.error.code} - ${environmentStateResponse.error.responseMessage ?? ""}` + ); + return err({ + code: "network_error", + message: "Error fetching environment state", + status: 500, + url: new URL(`${configInput.appUrl}/api/v1/client/${configInput.environmentId}/environment`), + responseMessage: environmentStateResponse.error.message, + }); + } + } + + if (isUserStateExpired) { + // If the existing person state (expired) has a userId, we need to fetch the person state + // If the existing person state (expired) has no userId, we need to set the person state to the default + + if (userState.data.userId) { + const updatesResponse = await sendUpdatesToBackend({ + appUrl: configInput.appUrl, + environmentId: configInput.environmentId, + updates: { + userId: userState.data.userId, + }, + }); + + if (updatesResponse.ok) { + userState = updatesResponse.data.state; + } else { + logger.error( + `Error updating user state: ${updatesResponse.error.code} - ${updatesResponse.error.responseMessage ?? ""}` + ); + return err({ + code: "network_error", + message: "Error updating user state", + status: 500, + url: new URL( + `${configInput.appUrl}/api/v1/client/${configInput.environmentId}/update/contacts/${userState.data.userId}` + ), + responseMessage: "Unknown error", + }); + } + } else { + userState = DEFAULT_USER_STATE_NO_USER_ID; + } + } + + // filter the environment state wrt the person state + const filteredSurveys = filterSurveys(environmentState, userState); + + // update the appConfig with the new filtered surveys and person state + appConfig.update({ + ...existingConfig, + environment: environmentState, + user: userState, + filteredSurveys, + }); + + const surveyNames = filteredSurveys.map((s) => s.name); + logger.debug(`Fetched ${surveyNames.length.toString()} surveys during sync: ${surveyNames.join(", ")}`); + } catch { + logger.debug("Error during sync. Please try again."); + } + } else { + logger.debug("No valid configuration found. Resetting config and creating new one."); + void appConfig.resetConfig(); + logger.debug("Syncing."); + + // During init, if we don't have a valid config, we need to fetch the environment state + // but not the person state, we can set it to the default value. + // The person state will be fetched when the `setUserId` method is called. + + try { + const environmentStateResponse = await fetchEnvironmentState({ + appUrl: configInput.appUrl, + environmentId: configInput.environmentId, + }); + + if (!environmentStateResponse.ok) { + // eslint-disable-next-line @typescript-eslint/only-throw-error -- error is ApiErrorResponse + throw environmentStateResponse.error; + } + + const personState = DEFAULT_USER_STATE_NO_USER_ID; + const environmentState = environmentStateResponse.data; + + const filteredSurveys = filterSurveys(environmentState, personState); + + appConfig.update({ + appUrl: configInput.appUrl, + environmentId: configInput.environmentId, + user: personState, + environment: environmentState, + filteredSurveys, + }); + } catch (e) { + await handleErrorOnFirstInit(e as { code: string; responseMessage: string }); + } + } + + logger.debug("Adding event listeners"); + addEventListeners(); + addCleanupEventListeners(); + + setIsInitialize(true); + logger.debug("Initialized"); + + // check page url if initialized after page load + return okVoid(); +}; + +export const checkInitialized = (): Result => { + const logger = Logger.getInstance(); + logger.debug("Check if initialized"); + + if (!isInitialized) { + return err({ + code: "not_initialized", + message: "Formbricks not initialized. Call initialize() first.", + }); + } + + return okVoid(); +}; + +// eslint-disable-next-line @typescript-eslint/require-await -- disabled for now +export const deinitalize = async (): Promise => { + const logger = Logger.getInstance(); + const appConfig = RNConfig.getInstance(); + + logger.debug("Setting person state to default"); + // clear the user state and set it to the default value + appConfig.update({ + ...appConfig.get(), + user: DEFAULT_USER_STATE_NO_USER_ID, + }); + setIsInitialize(false); + removeAllEventListeners(); +}; + +export const handleErrorOnFirstInit = async (e: { + code: string; + responseMessage: string; +}): Promise => { + const logger = Logger.getInstance(); + + if (e.code === "forbidden") { + logger.error(`Authorization error: ${e.responseMessage}`); + } else { + logger.error( + `Error during first initialization: ${e.code} - ${e.responseMessage}. Please try again later.` + ); + } + + // put formbricks in error state (by creating a new config) and throw error + const initialErrorConfig: Partial = { + status: { + value: "error", + expiresAt: new Date(new Date().getTime() + 10 * 60000), // 10 minutes in the future + }, + }; + + await wrapThrowsAsync(async () => { + await AsyncStorage.setItem(RN_ASYNC_STORAGE_KEY, JSON.stringify(initialErrorConfig)); + })(); + + throw new Error("Could not initialize formbricks"); +}; diff --git a/packages/react-native/src/lib/common/logger.ts b/packages/react-native/src/lib/common/logger.ts new file mode 100644 index 0000000000..5d45ffe90e --- /dev/null +++ b/packages/react-native/src/lib/common/logger.ts @@ -0,0 +1,50 @@ +/* eslint-disable no-console -- Required for logging */ +type LogLevel = "debug" | "error"; + +interface LoggerConfig { + logLevel?: LogLevel; +} + +export class Logger { + private static instance: Logger | undefined; + private logLevel: LogLevel = "error"; + + static getInstance(): Logger { + if (!Logger.instance) { + Logger.instance = new Logger(); + } + return Logger.instance; + } + + configure(config: LoggerConfig): void { + if (config.logLevel !== undefined) { + this.logLevel = config.logLevel; + } + } + + private logger(message: string, level: LogLevel): void { + if (level === "debug" && this.logLevel !== "debug") { + return; + } + + const timestamp = new Date().toISOString(); + const logMessage = `🧱 Formbricks - ${timestamp} [${level.toUpperCase()}] - ${message}`; + if (level === "error") { + console.error(logMessage); + } else { + console.log(logMessage); + } + } + + debug(message: string): void { + this.logger(message, "debug"); + } + + error(message: string): void { + this.logger(message, "error"); + } + + public resetInstance(): void { + Logger.instance = undefined; + } +} diff --git a/packages/react-native/src/lib/common/response-queue.ts b/packages/react-native/src/lib/common/response-queue.ts new file mode 100644 index 0000000000..35c0d8524c --- /dev/null +++ b/packages/react-native/src/lib/common/response-queue.ts @@ -0,0 +1,119 @@ +/* eslint-disable no-console -- required for logging errors */ +import { FormbricksAPI } from "@formbricks/api"; +import { type SurveyState } from "@/lib/survey/state"; +import { type TResponseUpdate } from "@/types/response"; + +interface QueueConfig { + appUrl: string; + environmentId: string; + retryAttempts: number; + onResponseSendingFailed?: (responseUpdate: TResponseUpdate) => void; + onResponseSendingFinished?: () => void; + setSurveyState?: (state: SurveyState) => void; +} + +const delay = (ms: number): Promise => { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +}; + +export class ResponseQueue { + private queue: TResponseUpdate[] = []; + private config: QueueConfig; + private surveyState: SurveyState; + private isRequestInProgress = false; + private api: FormbricksAPI; + + constructor(config: QueueConfig, surveyState: SurveyState, apiInstance?: FormbricksAPI) { + this.config = config; + this.surveyState = surveyState; + this.api = + apiInstance ?? + new FormbricksAPI({ + apiHost: config.appUrl, + environmentId: config.environmentId, + }); + } + + add(responseUpdate: TResponseUpdate): void { + // update survey state + this.surveyState.accumulateResponse(responseUpdate); + if (this.config.setSurveyState) { + this.config.setSurveyState(this.surveyState); + } + // add response to queue + this.queue.push(responseUpdate); + void this.processQueue(); + } + + async processQueue(): Promise { + if (this.isRequestInProgress) return; + if (this.queue.length === 0) return; + + this.isRequestInProgress = true; + + const responseUpdate = this.queue[0]; + let attempts = 0; + + while (attempts < this.config.retryAttempts) { + const success = await this.sendResponse(responseUpdate); + if (success) { + this.queue.shift(); // remove the successfully sent response from the queue + break; // exit the retry loop + } + console.error(`Formbricks: Failed to send response. Retrying... ${attempts.toString()}`); + await delay(1000); // wait for 1 second before retrying + attempts++; + } + + if (attempts >= this.config.retryAttempts) { + // Inform the user after 2 failed attempts + console.error("Failed to send response after 2 attempts."); + // If the response fails finally, inform the user + if (this.config.onResponseSendingFailed) { + this.config.onResponseSendingFailed(responseUpdate); + } + this.isRequestInProgress = false; + } else { + if (responseUpdate.finished && this.config.onResponseSendingFinished) { + this.config.onResponseSendingFinished(); + } + this.isRequestInProgress = false; + void this.processQueue(); // process the next item in the queue if any + } + } + + async sendResponse(responseUpdate: TResponseUpdate): Promise { + try { + if (this.surveyState.responseId !== null) { + await this.api.client.response.update({ ...responseUpdate, responseId: this.surveyState.responseId }); + } else { + const response = await this.api.client.response.create({ + ...responseUpdate, + surveyId: this.surveyState.surveyId, + userId: this.surveyState.userId ?? null, + singleUseId: this.surveyState.singleUseId ?? null, + data: { ...responseUpdate.data, ...responseUpdate.hiddenFields }, + displayId: this.surveyState.displayId, + }); + if (!response.ok) { + throw new Error("Could not create response"); + } + this.surveyState.updateResponseId(response.data.id); + if (this.config.setSurveyState) { + this.config.setSurveyState(this.surveyState); + } + } + return true; + } catch (error) { + console.error(error); + return false; + } + } + + // update surveyState + updateSurveyState(surveyState: SurveyState): void { + this.surveyState = surveyState; + } +} diff --git a/packages/react-native/src/lib/common/storage.ts b/packages/react-native/src/lib/common/storage.ts new file mode 100644 index 0000000000..a3a4ecacff --- /dev/null +++ b/packages/react-native/src/lib/common/storage.ts @@ -0,0 +1,9 @@ +import AsyncStorageModule from "@react-native-async-storage/async-storage"; + +const AsyncStorageWithDefault = AsyncStorageModule as typeof AsyncStorageModule & { + default?: typeof AsyncStorageModule; +}; + +const AsyncStorage = AsyncStorageWithDefault.default ?? AsyncStorageModule; + +export { AsyncStorage }; diff --git a/packages/react-native/src/lib/common/tests/__mocks__/config.mock.ts b/packages/react-native/src/lib/common/tests/__mocks__/config.mock.ts new file mode 100644 index 0000000000..1eb35379ca --- /dev/null +++ b/packages/react-native/src/lib/common/tests/__mocks__/config.mock.ts @@ -0,0 +1,126 @@ +import type { TConfig } from "@/types/config"; + +// ids +export const mockEnvironmentId = "ggskhsue85p2xrxrc7x3qagg"; +export const mockProjectId = "f5kptre0saxmltl7ram364qt"; +export const mockLanguageId = "n4ts6u7wy5lbn4q3jovikqot"; +export const mockSurveyId = "lz5m554yqh1i3moa3y230wei"; +export const mockActionClassId = "wypzu5qw7adgy66vq8s77tso"; + +export const mockConfig: TConfig = { + environmentId: mockEnvironmentId, + appUrl: "https://myapp.example", + environment: { + expiresAt: "2999-12-31T23:59:59Z", + data: { + surveys: [ + { + id: mockSurveyId, + name: "Onboarding Survey", + welcomeCard: null, + questions: [], + variables: [], + type: "app", // "link" or "app" + showLanguageSwitch: true, + endings: [], + autoClose: 5, + status: "inProgress", // whatever statuses you use + recontactDays: 7, + displayLimit: 1, + displayOption: "displayMultiple", + hiddenFields: [], + delay: 5, // e.g. 5s + projectOverwrites: {}, + languages: [ + { + // SurveyLanguage fields + surveyId: mockSurveyId, + default: true, + enabled: true, + languageId: mockLanguageId, + language: { + // Language fields + id: mockLanguageId, + code: "en", + alias: "en", + createdAt: "2025-01-01T10:00:00Z", + updatedAt: "2025-01-01T10:00:00Z", + projectId: mockProjectId, + }, + }, + ], + triggers: [ + { + // { actionClass: ActionClass } + actionClass: { + id: mockActionClassId, + key: "onboardingTrigger", + type: "code", // or "automatic" + name: "Manual Trigger", + createdAt: "2025-01-01T10:00:00Z", + updatedAt: "2025-01-01T10:00:00Z", + environmentId: mockEnvironmentId, + description: "Manual Trigger", + noCodeConfig: {}, + }, + }, + ], + segment: undefined, // or mock your Segment if needed + displayPercentage: 100, + styling: { + // TSurveyStyling + overwriteThemeStyling: false, + brandColor: { light: "#2B6CB0" }, + }, + }, + ], + actionClasses: [ + { + id: mockActionClassId, + key: "onboardingTrigger", + type: "code", + name: "Manual Trigger", + noCodeConfig: {}, + }, + ], + project: { + id: mockProjectId, + recontactDays: 14, + clickOutsideClose: true, + darkOverlay: false, + placement: "bottomRight", + inAppSurveyBranding: true, + styling: { + // TProjectStyling + allowStyleOverwrite: true, + brandColor: { light: "#319795" }, + }, + }, + }, + }, + user: { + expiresAt: null, + data: { + userId: "user_abc", + segments: ["beta-testers"], + displays: [ + { + surveyId: mockSurveyId, + createdAt: "2025-01-01T10:00:00Z", + }, + ], + responses: [mockSurveyId], + lastDisplayAt: "2025-01-02T15:00:00Z", + language: "en", + }, + }, + filteredSurveys: [], // fill if you'd like to pre-filter any surveys + attributes: { + plan: "premium", + region: "US", + }, + status: { + value: "success", + expiresAt: null, + }, +} as unknown as TConfig; diff --git a/packages/react-native/src/lib/common/tests/__mocks__/response-queue.mock.ts b/packages/react-native/src/lib/common/tests/__mocks__/response-queue.mock.ts new file mode 100644 index 0000000000..0238837726 --- /dev/null +++ b/packages/react-native/src/lib/common/tests/__mocks__/response-queue.mock.ts @@ -0,0 +1,10 @@ +export const mockSurveyId = "v6ym64bkch8o2spfajqt95u4"; +export const mockDisplayId = "athxslnasdkkoo18dyjs8y7e"; +export const mockEnvironmentId = "t49wnfhgq9cvarkvdq4316fd"; +export const mockUserId = "user_abc"; +export const mockAppUrl = "https://app.formbricks.com"; +export const mockResponseId = "d5596l4pynnqv03dokbnril4"; +export const mockQuestionId = "u9cnqpimopizdukyry8ye5us"; +export const mockResponseData = { + [mockQuestionId]: "test", +}; diff --git a/packages/react-native/src/lib/common/tests/command-queue.test.ts b/packages/react-native/src/lib/common/tests/command-queue.test.ts new file mode 100644 index 0000000000..8d06ea2182 --- /dev/null +++ b/packages/react-native/src/lib/common/tests/command-queue.test.ts @@ -0,0 +1,165 @@ +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { CommandQueue } from "@/lib/common/command-queue"; +import { checkInitialized } from "@/lib/common/initialize"; +import { type Result } from "@/types/error"; + +// Mock the initialize module so we can control checkInitialized() +vi.mock("@/lib/common/initialize", () => ({ + checkInitialized: vi.fn(), +})); + +describe("CommandQueue", () => { + let queue: CommandQueue; + + beforeEach(() => { + // Clear all mocks before each test + vi.clearAllMocks(); + // Create a fresh CommandQueue instance + queue = new CommandQueue(); + }); + + test("executes commands in FIFO order", async () => { + const executionOrder: string[] = []; + + // Mock commands with proper Result returns + const cmdA = vi.fn(async (): Promise> => { + return new Promise((resolve) => { + setTimeout(() => { + executionOrder.push("A"); + resolve({ ok: true, data: undefined }); + }, 10); + }); + }); + const cmdB = vi.fn(async (): Promise> => { + return new Promise((resolve) => { + setTimeout(() => { + executionOrder.push("B"); + resolve({ ok: true, data: undefined }); + }, 10); + }); + }); + const cmdC = vi.fn(async (): Promise> => { + return new Promise((resolve) => { + setTimeout(() => { + executionOrder.push("C"); + resolve({ ok: true, data: undefined }); + }, 10); + }); + }); + + // We'll assume checkInitialized always ok for this test + vi.mocked(checkInitialized).mockReturnValue({ ok: true, data: undefined }); + + // Enqueue commands + queue.add(cmdA, true); + queue.add(cmdB, true); + queue.add(cmdC, true); + + // Wait for them to finish + await queue.wait(); + + expect(executionOrder).toEqual(["A", "B", "C"]); + }); + + test("skips execution if checkInitialized() fails", async () => { + const cmd = vi.fn(async (): Promise => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 10); + }); + }); + + // Force checkInitialized to fail + vi.mocked(checkInitialized).mockReturnValue({ + ok: false, + error: { + code: "not_initialized", + message: "Not initialized", + }, + }); + + queue.add(cmd, true); + await queue.wait(); + + // Command should never have been called + expect(cmd).not.toHaveBeenCalled(); + }); + + test("executes command if checkInitialized is false (no check)", async () => { + const cmd = vi.fn(async (): Promise> => { + return new Promise((resolve) => { + setTimeout(() => { + resolve({ ok: true, data: undefined }); + }, 10); + }); + }); + + // checkInitialized is irrelevant in this scenario, but let's mock it anyway + vi.mocked(checkInitialized).mockReturnValue({ ok: true, data: undefined }); + + // Here we pass 'false' for the second argument, so no check is performed + queue.add(cmd, false); + await queue.wait(); + + expect(cmd).toHaveBeenCalledTimes(1); + }); + + test("logs errors if a command throws or returns error", async () => { + // Spy on console.error to see if it's called + const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => { + return { + ok: true, + data: undefined, + }; + }); + + // Force checkInitialized to succeed + vi.mocked(checkInitialized).mockReturnValue({ ok: true, data: undefined }); + + // Mock command that fails + const failingCmd = vi.fn(async () => { + await new Promise((resolve) => { + setTimeout(() => { + resolve("some error"); + }, 10); + }); + + throw new Error("some error"); + }); + + queue.add(failingCmd, true); + await queue.wait(); + + expect(consoleErrorSpy).toHaveBeenCalledWith("🧱 Formbricks - Global error: ", expect.any(Error)); + consoleErrorSpy.mockRestore(); + }); + + test("resolves wait() after all commands complete", async () => { + const cmd1 = vi.fn(async (): Promise> => { + return new Promise((resolve) => { + setTimeout(() => { + resolve({ ok: true, data: undefined }); + }, 10); + }); + }); + const cmd2 = vi.fn(async (): Promise> => { + return new Promise((resolve) => { + setTimeout(() => { + resolve({ ok: true, data: undefined }); + }, 10); + }); + }); + + vi.mocked(checkInitialized).mockReturnValue({ ok: true, data: undefined }); + + queue.add(cmd1, true); + queue.add(cmd2, true); + + await queue.wait(); + + // By the time `await queue.wait()` resolves, both commands should be done + expect(cmd1).toHaveBeenCalled(); + expect(cmd2).toHaveBeenCalled(); + }); +}); diff --git a/packages/react-native/src/lib/common/tests/config.test.ts b/packages/react-native/src/lib/common/tests/config.test.ts new file mode 100644 index 0000000000..4d42f84102 --- /dev/null +++ b/packages/react-native/src/lib/common/tests/config.test.ts @@ -0,0 +1,127 @@ +// config.test.ts +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; +import { RNConfig, RN_ASYNC_STORAGE_KEY } from "@/lib/common/config"; +import type { TConfig, TConfigUpdateInput } from "@/types/config"; +import { mockConfig } from "./__mocks__/config.mock"; + +// Define mocks outside of any describe block + +describe("RNConfig", () => { + let configInstance: RNConfig; + + beforeEach(async () => { + // Clear mocks between tests + vi.clearAllMocks(); + + // get the config instance + configInstance = RNConfig.getInstance(); + + // reset the config + await configInstance.resetConfig(); + + // get the config instance again + configInstance = RNConfig.getInstance(); + }); + + afterEach(() => { + // In case we want to restore them after all tests + vi.restoreAllMocks(); + }); + + test("getInstance() returns a singleton", () => { + const secondInstance = RNConfig.getInstance(); + expect(configInstance).toBe(secondInstance); + }); + + test("get() throws if config is null", () => { + // constructor didn't load anything successfully + // so config is still null + expect(() => configInstance.get()).toThrow("config is null, maybe the init function was not called?"); + }); + + test("loadFromStorage() returns ok if valid config is found", async () => { + vi.spyOn(AsyncStorage, "getItem").mockResolvedValueOnce(JSON.stringify(mockConfig)); + + const result = await configInstance.loadFromStorage(); + expect(result.ok).toBe(true); + + if (result.ok) { + expect(result.data).toEqual(mockConfig); + } + }); + + test("loadFromStorage() returns err if config is expired", async () => { + const expiredConfig = { + ...mockConfig, + environment: { + ...mockConfig.environment, + expiresAt: new Date("2000-01-01T00:00:00Z"), + }, + }; + + vi.spyOn(AsyncStorage, "getItem").mockResolvedValueOnce(JSON.stringify(expiredConfig)); + + const result = await configInstance.loadFromStorage(); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.message).toBe("Config in local storage has expired"); + } + }); + + test("loadFromStorage() returns err if no or invalid config in storage", async () => { + // Simulate no data + vi.spyOn(AsyncStorage, "getItem").mockResolvedValueOnce(null); + + const result = await configInstance.loadFromStorage(); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.message).toBe("No or invalid config in local storage"); + } + }); + + test("update() merges new config, calls saveToStorage()", async () => { + vi.spyOn(AsyncStorage, "getItem").mockResolvedValueOnce(JSON.stringify(mockConfig)); + + // Wait for the constructor's async load + await new Promise(setImmediate); + + // Now we call update() + const newStatus = { value: "error", expiresAt: "2100-01-01T00:00:00Z" } as unknown as TConfig["status"]; + + configInstance.update({ ...mockConfig, status: newStatus } as unknown as TConfigUpdateInput); + + // The update call should eventually call setItem on AsyncStorage + expect(AsyncStorage.setItem).toHaveBeenCalled(); + // Let’s check if we can read the updated config: + const updatedConfig = configInstance.get(); + expect(updatedConfig.status.value).toBe("error"); + expect(updatedConfig.status.expiresAt).toBe("2100-01-01T00:00:00Z"); + }); + + test("saveToStorage() is invoked internally on update()", async () => { + vi.spyOn(AsyncStorage, "getItem").mockResolvedValueOnce(JSON.stringify(mockConfig)); + + await new Promise(setImmediate); + + configInstance.update({ status: { value: "success", expiresAt: null } } as unknown as TConfigUpdateInput); + expect(AsyncStorage.setItem).toHaveBeenCalledWith( + RN_ASYNC_STORAGE_KEY, + expect.any(String) // the JSON string + ); + }); + + test("resetConfig() clears config and AsyncStorage", async () => { + vi.spyOn(AsyncStorage, "getItem").mockResolvedValueOnce(JSON.stringify(mockConfig)); + await new Promise(setImmediate); + + // Now reset + const result = await configInstance.resetConfig(); + + expect(result.ok).toBe(true); + // config is now null + expect(() => configInstance.get()).toThrow("config is null"); + // removeItem should be called + expect(AsyncStorage.removeItem).toHaveBeenCalledWith(RN_ASYNC_STORAGE_KEY); + }); +}); diff --git a/packages/react-native/src/lib/common/tests/file-upload.test.ts b/packages/react-native/src/lib/common/tests/file-upload.test.ts new file mode 100644 index 0000000000..4000c69971 --- /dev/null +++ b/packages/react-native/src/lib/common/tests/file-upload.test.ts @@ -0,0 +1,186 @@ +// file-upload.test.ts +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { StorageAPI } from "@/lib/common/file-upload"; +import type { TUploadFileConfig } from "@/types/storage"; + +// A global fetch mock so we can capture fetch calls. +// Alternatively, use `vi.stubGlobal("fetch", ...)`. +const fetchMock = vi.fn(); +global.fetch = fetchMock; + +const mockEnvironmentId = "dv46cywjt1fxkkempq7vwued"; + +describe("StorageAPI", () => { + const APP_URL = "https://myapp.example"; + const ENV_ID = mockEnvironmentId; + + let storage: StorageAPI; + + beforeEach(() => { + vi.clearAllMocks(); + storage = new StorageAPI(APP_URL, ENV_ID); + }); + + test("throws an error if file object is invalid", async () => { + // File missing "name", "type", or "base64" + await expect(storage.uploadFile({ type: "", name: "", base64: "" }, {})).rejects.toThrow( + "Invalid file object" + ); + }); + + test("throws if first fetch (storage route) returns non-OK", async () => { + // We provide a valid file object + const file = { type: "image/png", name: "test.png", base64: "data:image/png;base64,abc" }; + + // First fetch returns not ok + fetchMock.mockResolvedValueOnce({ + ok: false, + status: 400, + } as Response); + + await expect(storage.uploadFile(file)).rejects.toThrow("Upload failed with status: 400"); + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith( + `${APP_URL}/api/v1/client/${ENV_ID}/storage`, + expect.objectContaining({ + method: "POST", + }) + ); + }); + + test("throws if second fetch returns non-OK (local storage w/ signingData)", async () => { + // Suppose the first fetch is OK and returns JSON with signingData + const file = { type: "image/png", name: "test.png", base64: "data:image/png;base64,abc" }; + fetchMock + .mockResolvedValueOnce({ + ok: true, + json: async () => { + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); + + return { + data: { + signedUrl: "https://myapp.example/uploadLocal", + fileUrl: "https://myapp.example/files/test.png", + signingData: { signature: "xxx", timestamp: 1234, uuid: "abc" }, + presignedFields: null, + updatedFileName: "test.png", + }, + }; + }, + } as Response) + // second fetch fails + .mockResolvedValueOnce({ + ok: false, + json: async () => { + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); + + return { message: "File size exceeded your plan limit" }; + }, + } as Response); + + await expect(storage.uploadFile(file)).rejects.toThrow("File size exceeded your plan limit"); + expect(fetchMock).toHaveBeenCalledTimes(2); + }); + + test("throws if second fetch returns non-OK (S3) containing 'EntityTooLarge'", async () => { + const file = { type: "image/png", name: "test.png", base64: "data:image/png;base64,abc" }; + + // First fetch response includes presignedFields => indicates S3 scenario + fetchMock + .mockResolvedValueOnce({ + ok: true, + json: async () => { + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); + + return { + data: { + signedUrl: "https://some-s3-bucket/presigned", + fileUrl: "https://some-s3-bucket/test.png", + signingData: null, // means not local + presignedFields: { + key: "some-key", + policy: "base64policy", + }, + updatedFileName: "test.png", + }, + }; + }, + } as Response) + // second fetch fails with "EntityTooLarge" + .mockResolvedValueOnce({ + ok: false, + text: async () => { + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); + + return "Some error with EntityTooLarge text in it"; + }, + } as Response); + + await expect(storage.uploadFile(file)).rejects.toThrow("File size exceeds the size limit for your plan"); + expect(fetchMock).toHaveBeenCalledTimes(2); + }); + + test("successful upload returns fileUrl", async () => { + const file = { type: "image/png", name: "test.png", base64: "data:image/png;base64,abc" }; + const mockFileUrl = "https://myapp.example/files/test.png"; + + // First fetch => OK, returns JSON with 'signedUrl', 'fileUrl', etc. + fetchMock + .mockResolvedValueOnce({ + ok: true, + json: async () => { + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); + + return { + data: { + signedUrl: "https://myapp.example/uploadLocal", + fileUrl: mockFileUrl, + signingData: { + signature: "xxx", + timestamp: 1234, + uuid: "abc", + }, + presignedFields: null, + updatedFileName: "test.png", + }, + }; + }, + } as Response) + // second fetch => also OK + .mockResolvedValueOnce({ + ok: true, + } as Response); + + const url = await storage.uploadFile(file, { + allowedFileExtensions: [".png", ".jpg"], + surveyId: "survey_123", + } as TUploadFileConfig); + + expect(url).toBe(mockFileUrl); + expect(fetchMock).toHaveBeenCalledTimes(2); + + // We can also check the first fetch request body + const firstCall = fetchMock.mock.calls[0]; + expect(firstCall[0]).toBe(`${APP_URL}/api/v1/client/${ENV_ID}/storage`); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access -- we know it's a string + const bodyPayload = JSON.parse(firstCall[1].body as string); + + expect(bodyPayload).toMatchObject({ + fileName: "test.png", + fileType: "image/png", + allowedFileExtensions: [".png", ".jpg"], + surveyId: "survey_123", + }); + }); +}); diff --git a/packages/react-native/src/lib/common/tests/initialize.test.ts b/packages/react-native/src/lib/common/tests/initialize.test.ts new file mode 100644 index 0000000000..ec2202d0e6 --- /dev/null +++ b/packages/react-native/src/lib/common/tests/initialize.test.ts @@ -0,0 +1,366 @@ +// initialize.test.ts +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { type Mock, type MockInstance, afterEach, beforeEach, describe, expect, test, vi } from "vitest"; +import { RNConfig, RN_ASYNC_STORAGE_KEY } from "@/lib/common/config"; +import { + addCleanupEventListeners, + addEventListeners, + removeAllEventListeners, +} from "@/lib/common/event-listeners"; +import { + checkInitialized, + deinitalize, + handleErrorOnFirstInit, + init, + setIsInitialize, +} from "@/lib/common/initialize"; +import { Logger } from "@/lib/common/logger"; +import { filterSurveys, isNowExpired } from "@/lib/common/utils"; +import { fetchEnvironmentState } from "@/lib/environment/state"; +import { DEFAULT_USER_STATE_NO_USER_ID } from "@/lib/user/state"; +import { sendUpdatesToBackend } from "@/lib/user/update"; + +// 1) Mock AsyncStorage +vi.mock("@react-native-async-storage/async-storage", () => ({ + default: { + setItem: vi.fn(), + getItem: vi.fn(), + removeItem: vi.fn(), + }, +})); + +// 2) Mock RNConfig +vi.mock("@/lib/common/config", () => ({ + RN_ASYNC_STORAGE_KEY: "formbricks-react-native", + RNConfig: { + getInstance: vi.fn(() => ({ + get: vi.fn(), + update: vi.fn(), + resetConfig: vi.fn(), + })), + }, +})); + +// 3) Mock logger +vi.mock("@/lib/common/logger", () => ({ + Logger: { + getInstance: vi.fn(() => ({ + debug: vi.fn(), + error: vi.fn(), + })), + }, +})); + +// 4) Mock event-listeners +vi.mock("@/lib/common/event-listeners", () => ({ + addEventListeners: vi.fn(), + addCleanupEventListeners: vi.fn(), + removeAllEventListeners: vi.fn(), +})); + +// 5) Mock fetchEnvironmentState +vi.mock("@/lib/environment/state", () => ({ + fetchEnvironmentState: vi.fn(), +})); + +// 6) Mock filterSurveys +vi.mock("@/lib/common/utils", async (importOriginal) => { + return { + ...(await importOriginal()), + filterSurveys: vi.fn(), + isNowExpired: vi.fn(), + }; +}); + +// 7) Mock user/update +vi.mock("@/lib/user/update", () => ({ + sendUpdatesToBackend: vi.fn(), +})); + +describe("initialize.ts", () => { + let getInstanceConfigMock: MockInstance<() => RNConfig>; + let getInstanceLoggerMock: MockInstance<() => Logger>; + + const mockLogger = { + debug: vi.fn(), + error: vi.fn(), + }; + + beforeEach(() => { + vi.clearAllMocks(); + // By default, set isInitialize to false so we can test init logic from scratch + setIsInitialize(false); + + getInstanceConfigMock = vi.spyOn(RNConfig, "getInstance"); + getInstanceLoggerMock = vi.spyOn(Logger, "getInstance").mockReturnValue(mockLogger as unknown as Logger); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe("init()", () => { + test("returns ok if already initialized", async () => { + getInstanceLoggerMock.mockReturnValue(mockLogger as unknown as Logger); + setIsInitialize(true); + const result = await init({ environmentId: "env_id", appUrl: "https://my.url" }); + expect(result.ok).toBe(true); + expect(mockLogger.debug).toHaveBeenCalledWith("Already initialized, skipping initialization."); + }); + + test("fails if no environmentId is provided", async () => { + const result = await init({ environmentId: "", appUrl: "https://my.url" }); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe("missing_field"); + } + }); + + test("fails if no appUrl is provided", async () => { + const result = await init({ environmentId: "env_123", appUrl: "" }); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe("missing_field"); + } + }); + + test("skips init if existing config is in error state and not expired", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + environmentId: "env_123", + appUrl: "https://my.url", + environment: {}, + user: { data: {}, expiresAt: null }, + status: { value: "error", expiresAt: new Date(Date.now() + 10000) }, + }), + }; + + getInstanceConfigMock.mockReturnValue(mockConfig as unknown as RNConfig); + + (isNowExpired as unknown as Mock).mockReturnValue(true); + + const result = await init({ environmentId: "env_123", appUrl: "https://my.url" }); + expect(result.ok).toBe(true); + expect(mockLogger.debug).toHaveBeenCalledWith("Formbricks was set to an error state."); + expect(mockLogger.debug).toHaveBeenCalledWith("Error state is not expired, skipping initialization"); + }); + + test("proceeds if error state is expired", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + environmentId: "env_123", + appUrl: "https://my.url", + environment: {}, + user: { data: {}, expiresAt: null }, + status: { value: "error", expiresAt: new Date(Date.now() - 10000) }, // expired + }), + }; + + getInstanceConfigMock.mockReturnValue(mockConfig as unknown as RNConfig); + + const result = await init({ environmentId: "env_123", appUrl: "https://my.url" }); + expect(result.ok).toBe(true); + expect(mockLogger.debug).toHaveBeenCalledWith("Formbricks was set to an error state."); + expect(mockLogger.debug).toHaveBeenCalledWith("Error state is expired. Continue with initialization."); + }); + + test("uses existing config if environmentId/appUrl match, checks for expiration sync", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + environmentId: "env_123", + appUrl: "https://my.url", + environment: { expiresAt: new Date(Date.now() - 5000) }, // environment expired + user: { + data: { userId: "user_abc" }, + expiresAt: new Date(Date.now() - 5000), // also expired + }, + status: { value: "success", expiresAt: null }, + }), + update: vi.fn(), + }; + + getInstanceConfigMock.mockReturnValue(mockConfig as unknown as RNConfig); + + (isNowExpired as unknown as Mock).mockReturnValue(true); + + // Mock environment fetch success + (fetchEnvironmentState as unknown as Mock).mockResolvedValueOnce({ + ok: true, + data: { data: { surveys: [] }, expiresAt: new Date(Date.now() + 60_000) }, + }); + + // Mock sendUpdatesToBackend success + (sendUpdatesToBackend as unknown as Mock).mockResolvedValueOnce({ + ok: true, + data: { + state: { + expiresAt: new Date(), + data: { userId: "user_abc", segments: [] }, + }, + }, + }); + + (filterSurveys as unknown as Mock).mockReturnValueOnce([{ name: "S1" }, { name: "S2" }]); + + const result = await init({ environmentId: "env_123", appUrl: "https://my.url" }); + expect(result.ok).toBe(true); + + // environmentState was fetched + expect(fetchEnvironmentState).toHaveBeenCalled(); + // user state was updated + expect(sendUpdatesToBackend).toHaveBeenCalled(); + // filterSurveys called + expect(filterSurveys).toHaveBeenCalled(); + // config updated + expect(mockConfig.update).toHaveBeenCalledWith( + expect.objectContaining({ + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- required for testing this object + user: expect.objectContaining({ + data: { userId: "user_abc", segments: [] }, + }), + filteredSurveys: [{ name: "S1" }, { name: "S2" }], + }) + ); + }); + + test("resets config if no valid config found, fetches environment, sets default user", async () => { + const mockConfig = { + get: () => { + throw new Error("no config found"); + }, + resetConfig: vi.fn(), + update: vi.fn(), + }; + + getInstanceConfigMock.mockReturnValue(mockConfig as unknown as RNConfig); + + (fetchEnvironmentState as unknown as Mock).mockResolvedValueOnce({ + ok: true, + data: { + data: { + surveys: [{ name: "SurveyA" }], + expiresAt: new Date(Date.now() + 60000), + }, + }, + }); + + (filterSurveys as unknown as Mock).mockReturnValueOnce([{ name: "SurveyA" }]); + + const result = await init({ environmentId: "envX", appUrl: "https://urlX" }); + expect(result.ok).toBe(true); + expect(mockLogger.debug).toHaveBeenCalledWith("No existing configuration found."); + expect(mockLogger.debug).toHaveBeenCalledWith( + "No valid configuration found. Resetting config and creating new one." + ); + expect(mockConfig.resetConfig).toHaveBeenCalled(); + expect(fetchEnvironmentState).toHaveBeenCalled(); + expect(mockConfig.update).toHaveBeenCalledWith({ + appUrl: "https://urlX", + environmentId: "envX", + user: DEFAULT_USER_STATE_NO_USER_ID, + environment: { + data: { + surveys: [{ name: "SurveyA" }], + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- required for testing this object + expiresAt: expect.any(Date), + }, + }, + filteredSurveys: [{ name: "SurveyA" }], + }); + }); + + test("calls handleErrorOnFirstInit if environment fetch fails initially", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue(undefined), + update: vi.fn(), + resetConfig: vi.fn(), + }; + + getInstanceConfigMock.mockReturnValueOnce(mockConfig as unknown as RNConfig); + + (fetchEnvironmentState as unknown as Mock).mockResolvedValueOnce({ + ok: false, + error: { code: "forbidden", responseMessage: "No access" }, + }); + + await expect(init({ environmentId: "envX", appUrl: "https://urlX" })).rejects.toThrow( + "Could not initialize formbricks" + ); + }); + + test("adds event listeners and sets isInitialized", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + environmentId: "env_abc", + appUrl: "https://test.app", + environment: {}, + user: { data: {}, expiresAt: null }, + status: { value: "success", expiresAt: null }, + }), + update: vi.fn(), + }; + + getInstanceConfigMock.mockReturnValueOnce(mockConfig as unknown as RNConfig); + + const result = await init({ environmentId: "env_abc", appUrl: "https://test.app" }); + expect(result.ok).toBe(true); + expect(addEventListeners).toHaveBeenCalled(); + expect(addCleanupEventListeners).toHaveBeenCalled(); + }); + }); + + describe("checkInitialized()", () => { + test("returns err if not initialized", () => { + const res = checkInitialized(); + expect(res.ok).toBe(false); + if (!res.ok) { + expect(res.error.code).toBe("not_initialized"); + } + }); + + test("returns ok if initialized", () => { + setIsInitialize(true); + const res = checkInitialized(); + expect(res.ok).toBe(true); + }); + }); + + describe("deinitalize()", () => { + test("resets user state to default and removes event listeners", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + user: { data: { userId: "XYZ" } }, + }), + update: vi.fn(), + }; + + getInstanceConfigMock.mockReturnValueOnce(mockConfig as unknown as RNConfig); + + await deinitalize(); + + expect(mockConfig.update).toHaveBeenCalledWith( + expect.objectContaining({ + user: DEFAULT_USER_STATE_NO_USER_ID, + }) + ); + expect(removeAllEventListeners).toHaveBeenCalled(); + }); + }); + + describe("handleErrorOnFirstInit()", () => { + test("stores error state in AsyncStorage, throws error", async () => { + // We import the function directly + const errorObj = { code: "forbidden", responseMessage: "No access" }; + + await expect(async () => { + await handleErrorOnFirstInit(errorObj); + }).rejects.toThrow("Could not initialize formbricks"); + + // AsyncStorage setItem should be called with the error config + expect(AsyncStorage.setItem).toHaveBeenCalledWith( + RN_ASYNC_STORAGE_KEY, + expect.stringContaining('"value":"error"') + ); + }); + }); +}); diff --git a/packages/react-native/src/lib/common/tests/logger.test.ts b/packages/react-native/src/lib/common/tests/logger.test.ts new file mode 100644 index 0000000000..abedede805 --- /dev/null +++ b/packages/react-native/src/lib/common/tests/logger.test.ts @@ -0,0 +1,82 @@ +// logger.test.ts +import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; +import { Logger } from "@/lib/common/logger"; + +// adjust import path as needed + +describe("Logger", () => { + let logger: Logger; + let consoleLogSpy: ReturnType; + let consoleErrorSpy: ReturnType; + + beforeEach(() => { + logger = Logger.getInstance(); + + // Reset any existing singleton + logger.resetInstance(); + + logger = Logger.getInstance(); + + // Mock console so we don't actually log in test output + consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => { + return { + ok: true, + data: undefined, + }; + }); + + consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => { + return { + ok: true, + data: undefined, + }; + }); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + test("getInstance() returns a singleton", () => { + const anotherLogger = Logger.getInstance(); + expect(logger).toBe(anotherLogger); + }); + + test("default logLevel is 'error', so debug messages shouldn't appear", () => { + logger.debug("This is a debug log"); + logger.error("This is an error log"); + + // debug should NOT be logged by default + expect(consoleLogSpy).not.toHaveBeenCalledWith(expect.stringContaining("This is a debug log")); + // error should be logged + expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining("[ERROR] - This is an error log")); + }); + + test("configure to logLevel=debug => debug messages appear", () => { + logger.configure({ logLevel: "debug" }); + + logger.debug("Debug log after config"); + logger.error("Error log after config"); + + // debug should now appear + expect(consoleLogSpy).toHaveBeenCalledWith( + expect.stringMatching(/🧱 Formbricks.*\[DEBUG\].*Debug log after config/) + ); + // error should appear as well + expect(consoleErrorSpy).toHaveBeenCalledWith( + expect.stringMatching(/🧱 Formbricks.*\[ERROR\].*Error log after config/) + ); + }); + + test("logs have correct format including timestamp prefix", () => { + logger.configure({ logLevel: "debug" }); + logger.debug("Some message"); + + // Check that the log includes 🧱 Formbricks, timestamp, [DEBUG], and the message + expect(consoleLogSpy).toHaveBeenCalledWith( + expect.stringMatching( + /^🧱 Formbricks - \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z \[DEBUG\] - Some message$/ + ) + ); + }); +}); diff --git a/packages/react-native/src/lib/common/tests/response-queue.test.ts b/packages/react-native/src/lib/common/tests/response-queue.test.ts new file mode 100644 index 0000000000..ac5c695c52 --- /dev/null +++ b/packages/react-native/src/lib/common/tests/response-queue.test.ts @@ -0,0 +1,224 @@ +// response-queue.test.ts +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { type FormbricksAPI } from "@formbricks/api"; +import { ResponseQueue } from "@/lib/common/response-queue"; +import type { SurveyState } from "@/lib/survey/state"; +import type { TResponseUpdate } from "@/types/response"; +import { + mockAppUrl, + mockDisplayId, + mockEnvironmentId, + mockQuestionId, + mockResponseId, + mockSurveyId, + mockUserId, +} from "./__mocks__/response-queue.mock"; + +describe("ResponseQueue", () => { + let responseQueue: ResponseQueue; + let mockSurveyState: Partial; + let mockConfig: { + appUrl: string; + environmentId: string; + retryAttempts: number; + onResponseSendingFailed?: (resp: TResponseUpdate) => void; + onResponseSendingFinished?: () => void; + setSurveyState?: (state: SurveyState) => void; + }; + let mockApi: FormbricksAPI; + + beforeEach(() => { + vi.clearAllMocks(); + + // 2) Setup a "fake" surveyState + mockSurveyState = { + responseId: null, + surveyId: mockSurveyId, + userId: mockUserId, + singleUseId: null, + displayId: mockDisplayId, + accumulateResponse: vi.fn(), + updateResponseId: vi.fn(), + }; + + // 3) Setup a config object + mockConfig = { + appUrl: mockAppUrl, + environmentId: mockEnvironmentId, + retryAttempts: 2, + onResponseSendingFailed: vi.fn(), + onResponseSendingFinished: vi.fn(), + setSurveyState: vi.fn(), + }; + + // Create the queue + mockApi = { + client: { + response: { + create: vi.fn(), + update: vi.fn(), + }, + }, + } as unknown as FormbricksAPI; + + responseQueue = new ResponseQueue(mockConfig, mockSurveyState as SurveyState, mockApi); + }); + + test("add() accumulates response, updates setSurveyState, and calls processQueue()", () => { + // Spy on processQueue + const processQueueMock = vi.spyOn(responseQueue, "processQueue"); + + const update: TResponseUpdate = { + data: { + [mockQuestionId]: "test", + }, + ttc: { + [mockQuestionId]: 1000, + }, + finished: false, + }; + + // Call queue.add + responseQueue.add(update); + + expect(mockSurveyState.accumulateResponse).toHaveBeenCalledWith(update); + expect(mockConfig.setSurveyState).toHaveBeenCalledTimes(1); + expect(processQueueMock).toHaveBeenCalledTimes(1); + }); + + test("processQueue does nothing if already in progress or queue is empty", async () => { + // Because processQueue is called in add() + // Let's set isRequestInProgress artificially and call processQueue directly: + const responseQueueWithIsRequestInProgress = responseQueue as unknown as { + isRequestInProgress: boolean; + }; + responseQueueWithIsRequestInProgress.isRequestInProgress = true; + await responseQueue.processQueue(); + + // No changes, no error + expect(responseQueueWithIsRequestInProgress.isRequestInProgress).toBe(true); + + // Now set queue empty, isRequestInProgress false + responseQueueWithIsRequestInProgress.isRequestInProgress = false; + await responseQueue.processQueue(); + // still no error, but no action + // This just ensures we handle those conditions gracefully + }); + + test("when surveyState has no responseId, it calls create(...) and sets responseId on success", async () => { + const formbricksApiMock = vi.spyOn(mockApi.client.response, "create"); + + formbricksApiMock.mockResolvedValueOnce({ + ok: true, + data: { id: mockResponseId }, + }); + + // Add an item + const update: TResponseUpdate = { + data: { [mockQuestionId]: "test" }, + ttc: { [mockQuestionId]: 1000 }, + finished: false, + }; + + responseQueue.add(update); + + // We need to wait for the queue to process + await responseQueue.processQueue(); + + // fake delay for the queue to process and get empty + await new Promise((r) => { + setTimeout(r, 100); + }); + + // Check create call + expect(formbricksApiMock).toHaveBeenCalledWith({ + ...update, + surveyId: mockSurveyId, + userId: mockUserId, + singleUseId: null, + displayId: mockDisplayId, + data: { + [mockQuestionId]: "test", + }, + }); + + // responseId is updated + expect(mockSurveyState.updateResponseId).toHaveBeenCalledWith(mockResponseId); + + const responseQueueWithQueueArr = responseQueue as unknown as { queue: TResponseUpdate[] }; + expect(responseQueueWithQueueArr.queue).toHaveLength(0); + }); + + test("when surveyState has a responseId, it calls update(...) and empties the queue", async () => { + mockSurveyState.responseId = mockResponseId; + + const formbricksApiMock = vi.spyOn(mockApi.client.response, "update"); + + // Mock update => success + formbricksApiMock.mockResolvedValueOnce({ + ok: true, + data: { id: mockResponseId }, + }); + + const update: TResponseUpdate = { + data: { [mockQuestionId]: "test" }, + ttc: { [mockQuestionId]: 1000 }, + finished: false, + }; + + responseQueue.add(update); + + await responseQueue.processQueue(); + + // fake delay for the queue to process and get empty + await new Promise((r) => { + setTimeout(r, 100); + }); + + expect(formbricksApiMock).toHaveBeenCalledWith({ + ...update, + responseId: mockResponseId, + }); + + const responseQueueWithQueueArr = responseQueue as unknown as { queue: TResponseUpdate[] }; + expect(responseQueueWithQueueArr.queue).toHaveLength(0); + }); + + test("retries up to retryAttempts if sendResponse fails", async () => { + // Force create to fail + const formbricksApiMock = vi.spyOn(mockApi.client.response, "create"); + formbricksApiMock.mockRejectedValueOnce(new Error("Network error")); + + const update: TResponseUpdate = { + data: { [mockQuestionId]: "fail" }, + ttc: { [mockQuestionId]: 0 }, + finished: false, + }; + + responseQueue.add(update); + + await new Promise((r) => { + setTimeout(r, 1000); + }); + + await responseQueue.processQueue(); + + await new Promise((r) => { + setTimeout(r, 1000); + }); + + // It tries 2 times + expect(formbricksApiMock).toHaveBeenCalledTimes(2); + // Ultimately fails => item remains in queue + const responseQueueWithQueueArr = responseQueue as unknown as { queue: TResponseUpdate[] }; + expect(responseQueueWithQueueArr.queue).toHaveLength(1); + }); + + test("updateSurveyState updates the surveyState reference", () => { + const newState = { responseId: mockResponseId } as SurveyState; + responseQueue.updateSurveyState(newState); + + const responseQueueWithSurveyState = responseQueue as unknown as { surveyState: SurveyState }; + expect(responseQueueWithSurveyState.surveyState).toBe(newState); + }); +}); diff --git a/packages/react-native/src/lib/common/tests/utils.test.ts b/packages/react-native/src/lib/common/tests/utils.test.ts new file mode 100644 index 0000000000..36f87d37a7 --- /dev/null +++ b/packages/react-native/src/lib/common/tests/utils.test.ts @@ -0,0 +1,394 @@ +// utils.test.ts +import { mockProjectId, mockSurveyId } from "@/lib/common/tests/__mocks__/config.mock"; +import { + diffInDays, + filterSurveys, + getDefaultLanguageCode, + getLanguageCode, + getStyling, + shouldDisplayBasedOnPercentage, + wrapThrowsAsync, +} from "@/lib/common/utils"; +import type { + TEnvironmentState, + TEnvironmentStateProject, + TEnvironmentStateSurvey, + TSurveyStyling, + TUserState, +} from "@/types/config"; +import { beforeEach, describe, expect, test, vi } from "vitest"; + +const mockSurveyId1 = "e3kxlpnzmdp84op9qzxl9olj"; +const mockSurveyId2 = "qo9rwjmms42hoy3k85fp8vgu"; +const mockSegmentId1 = "p6yrnz3s2tvoe5r0l28unq7k"; +const mockSegmentId2 = "wz43zrxeddhb1uo9cicustar"; + +describe("utils.ts", () => { + // --------------------------------------------------------------------------------- + // diffInDays + // --------------------------------------------------------------------------------- + describe("diffInDays()", () => { + test("calculates correct day difference", () => { + const date1 = new Date("2023-01-01"); + const date2 = new Date("2023-01-05"); + expect(diffInDays(date1, date2)).toBe(4); // four days apart + }); + + test("handles negative differences (abs)", () => { + const date1 = new Date("2023-01-10"); + const date2 = new Date("2023-01-05"); + expect(diffInDays(date1, date2)).toBe(5); + }); + + test("0 if same day", () => { + const date = new Date("2023-01-01"); + expect(diffInDays(date, date)).toBe(0); + }); + }); + + // --------------------------------------------------------------------------------- + // wrapThrowsAsync + // --------------------------------------------------------------------------------- + describe("wrapThrowsAsync()", () => { + test("returns ok on success", async () => { + const fn = vi.fn(async (x: number) => { + await new Promise((r) => { + setTimeout(r, 10); + }); + return x * 2; + }); + + const wrapped = wrapThrowsAsync(fn); + + const result = await wrapped(5); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.data).toBe(10); + } + }); + + test("returns err on error", async () => { + const fn = vi.fn(async () => { + await new Promise((r) => { + setTimeout(r, 10); + }); + throw new Error("Something broke"); + }); + const wrapped = wrapThrowsAsync(fn); + + const result = await wrapped(); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.message).toBe("Something broke"); + } + }); + }); + + // --------------------------------------------------------------------------------- + // filterSurveys + // --------------------------------------------------------------------------------- + describe("filterSurveys()", () => { + // We'll create a minimal environment state + let environment: TEnvironmentState; + let user: TUserState; + const baseSurvey: Partial = { + id: mockSurveyId, + displayOption: "displayOnce", + displayLimit: 1, + recontactDays: null, + languages: [], + }; + + beforeEach(() => { + environment = { + expiresAt: new Date(), + data: { + project: { + id: mockProjectId, + recontactDays: 7, // fallback if survey doesn't have it + clickOutsideClose: false, + darkOverlay: false, + placement: "bottomRight", + inAppSurveyBranding: true, + styling: { allowStyleOverwrite: false }, + } as TEnvironmentStateProject, + surveys: [], + actionClasses: [], + }, + }; + user = { + expiresAt: null, + data: { + userId: null, + segments: [], + displays: [], + responses: [], + lastDisplayAt: null, + }, + }; + }); + + test("returns no surveys if user has no segments and userId is set", () => { + user.data.userId = "user_abc"; + // environment has a single survey + environment.data.surveys = [ + { ...baseSurvey, id: mockSurveyId1, segment: { id: mockSegmentId1 } } as TEnvironmentStateSurvey, + ]; + + const result = filterSurveys(environment, user); + expect(result).toEqual([]); // no segments => none pass + }); + + test("returns surveys if user has no userId but displayOnce and no displays yet", () => { + // userId is null => it won't segment filter + environment.data.surveys = [ + { ...baseSurvey, id: mockSurveyId1, displayOption: "displayOnce" } as TEnvironmentStateSurvey, + ]; + + const result = filterSurveys(environment, user); + expect(result).toHaveLength(1); + expect(result[0].id).toBe(mockSurveyId1); + }); + + test("skips surveys that already displayed if displayOnce is used", () => { + environment.data.surveys = [ + { ...baseSurvey, id: mockSurveyId1, displayOption: "displayOnce" } as TEnvironmentStateSurvey, + ]; + user.data.displays = [{ surveyId: mockSurveyId1, createdAt: new Date() }]; + + const result = filterSurveys(environment, user); + expect(result).toEqual([]); + }); + + test("skips surveys if user responded to them and displayOption=displayMultiple", () => { + environment.data.surveys = [ + { ...baseSurvey, id: mockSurveyId1, displayOption: "displayMultiple" } as TEnvironmentStateSurvey, + ]; + user.data.responses = [mockSurveyId1]; + + const result = filterSurveys(environment, user); + expect(result).toEqual([]); + }); + + test("handles displaySome logic with displayLimit", () => { + environment.data.surveys = [ + { + ...baseSurvey, + id: mockSurveyId1, + displayOption: "displaySome", + displayLimit: 2, + } as TEnvironmentStateSurvey, + ]; + // user has 1 display of s1 + user.data.displays = [{ surveyId: mockSurveyId1, createdAt: new Date() }]; + + // No responses => so it's still allowed + const result = filterSurveys(environment, user); + expect(result).toHaveLength(1); + }); + + test("filters out surveys if recontactDays not met", () => { + // Suppose survey uses project fallback (7 days) + environment.data.surveys = [ + { ...baseSurvey, id: mockSurveyId1, displayOption: "displayOnce" } as TEnvironmentStateSurvey, + ]; + // user last displayAt is only 3 days ago + user.data.lastDisplayAt = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); + + const result = filterSurveys(environment, user); + expect(result).toHaveLength(0); + }); + + test("passes surveys if enough days have passed since lastDisplayAt", () => { + // user last displayAt is 8 days ago + user.data.lastDisplayAt = new Date(Date.now() - 8 * 24 * 60 * 60 * 1000); + + environment.data.surveys = [ + { + ...baseSurvey, + id: mockSurveyId1, + displayOption: "respondMultiple", + recontactDays: null, + } as TEnvironmentStateSurvey, + ]; + const result = filterSurveys(environment, user); + expect(result).toHaveLength(1); + }); + + test("filters by segment if userId is set and user has segments", () => { + user.data.userId = "user_abc"; + user.data.segments = [mockSegmentId1]; + environment.data.surveys = [ + { + ...baseSurvey, + id: mockSurveyId1, + segment: { id: mockSegmentId1 }, + displayOption: "respondMultiple", + } as TEnvironmentStateSurvey, + { + ...baseSurvey, + id: mockSurveyId2, + segment: { id: mockSegmentId2 }, + displayOption: "respondMultiple", + } as TEnvironmentStateSurvey, + ]; + + const result = filterSurveys(environment, user); + // only the one that matches user's segment + expect(result).toHaveLength(1); + expect(result[0].id).toBe(mockSurveyId1); + }); + }); + + // --------------------------------------------------------------------------------- + // getStyling + // --------------------------------------------------------------------------------- + describe("getStyling()", () => { + test("returns project styling if allowStyleOverwrite=false", () => { + const project = { + id: "p1", + styling: { allowStyleOverwrite: false, brandColor: { light: "#fff" } }, + } as TEnvironmentStateProject; + const survey = { + styling: { + overwriteThemeStyling: true, + brandColor: { light: "#000" }, + } as TSurveyStyling, + } as TEnvironmentStateSurvey; + + const result = getStyling(project, survey); + // should get project styling + expect(result).toEqual(project.styling); + }); + + test("returns project styling if allowStyleOverwrite=true but survey overwriteThemeStyling=false", () => { + const project = { + id: "p1", + styling: { allowStyleOverwrite: true, brandColor: { light: "#fff" } }, + } as TEnvironmentStateProject; + const survey = { + styling: { + overwriteThemeStyling: false, + brandColor: { light: "#000" }, + } as TSurveyStyling, + } as TEnvironmentStateSurvey; + + const result = getStyling(project, survey); + // should get project styling still + expect(result).toEqual(project.styling); + }); + + test("returns survey styling if allowStyleOverwrite=true and survey overwriteThemeStyling=true", () => { + const project = { + id: "p1", + styling: { allowStyleOverwrite: true, brandColor: { light: "#fff" } }, + } as TEnvironmentStateProject; + const survey = { + styling: { + overwriteThemeStyling: true, + brandColor: { light: "#000" }, + } as TSurveyStyling, + } as TEnvironmentStateSurvey; + + const result = getStyling(project, survey); + expect(result).toEqual(survey.styling); + }); + }); + + // --------------------------------------------------------------------------------- + // getDefaultLanguageCode + // --------------------------------------------------------------------------------- + describe("getDefaultLanguageCode()", () => { + test("returns code of the language if it is flagged default", () => { + const survey = { + languages: [ + { + language: { code: "en" }, + default: false, + enabled: true, + }, + { + language: { code: "fr" }, + default: true, + enabled: true, + }, + ], + } as unknown as TEnvironmentStateSurvey; + expect(getDefaultLanguageCode(survey)).toBe("fr"); + }); + + test("returns undefined if no default language found", () => { + const survey = { + languages: [ + { language: { code: "en" }, default: false, enabled: true }, + { language: { code: "fr" }, default: false, enabled: true }, + ], + } as unknown as TEnvironmentStateSurvey; + expect(getDefaultLanguageCode(survey)).toBeUndefined(); + }); + }); + + // --------------------------------------------------------------------------------- + // getLanguageCode + // --------------------------------------------------------------------------------- + describe("getLanguageCode()", () => { + test("returns 'default' if no language param is passed", () => { + const survey = { + languages: [{ language: { code: "en" }, default: true, enabled: true }], + } as unknown as TEnvironmentStateSurvey; + const code = getLanguageCode(survey, undefined); + expect(code).toBe("default"); + }); + + test("returns 'default' if the chosen language is the default one", () => { + const survey = { + languages: [ + { language: { code: "en" }, default: true, enabled: true }, + { language: { code: "fr" }, default: false, enabled: true }, + ], + } as unknown as TEnvironmentStateSurvey; + const code = getLanguageCode(survey, "en"); + expect(code).toBe("default"); + }); + + test("returns undefined if language not found or disabled", () => { + const survey = { + languages: [ + { language: { code: "en" }, default: true, enabled: true }, + { language: { code: "fr" }, default: false, enabled: false }, + ], + } as unknown as TEnvironmentStateSurvey; + const code = getLanguageCode(survey, "fr"); + expect(code).toBeUndefined(); + }); + + test("returns the language code if found and enabled", () => { + const survey = { + languages: [ + { language: { code: "en", alias: "English" }, default: true, enabled: true }, + { language: { code: "fr", alias: "fr-FR" }, default: false, enabled: true }, + ], + } as unknown as TEnvironmentStateSurvey; + expect(getLanguageCode(survey, "fr")).toBe("fr"); + expect(getLanguageCode(survey, "fr-FR")).toBe("fr"); + }); + }); + + // --------------------------------------------------------------------------------- + // shouldDisplayBasedOnPercentage + // --------------------------------------------------------------------------------- + describe("shouldDisplayBasedOnPercentage()", () => { + test("returns true if random number <= displayPercentage", () => { + // We'll mock Math.random to return something + const mockedRandom = vi.spyOn(Math, "random").mockReturnValue(0.2); // 0.2 => 20% + // displayPercentage = 30 => 30% => we should display + expect(shouldDisplayBasedOnPercentage(30)).toBe(true); + + mockedRandom.mockReturnValue(0.5); // 50% + expect(shouldDisplayBasedOnPercentage(30)).toBe(false); + + // restore + mockedRandom.mockRestore(); + }); + }); +}); diff --git a/packages/react-native/src/lib/common/utils.ts b/packages/react-native/src/lib/common/utils.ts new file mode 100644 index 0000000000..912487d6ae --- /dev/null +++ b/packages/react-native/src/lib/common/utils.ts @@ -0,0 +1,170 @@ +import type { + TEnvironmentState, + TEnvironmentStateProject, + TEnvironmentStateSurvey, + TProjectStyling, + TSurveyStyling, + TUserState, +} from "@/types/config"; +import type { Result } from "@/types/error"; + +// Helper function to calculate difference in days between two dates +export const diffInDays = (date1: Date, date2: Date): number => { + const diffTime = Math.abs(date2.getTime() - date1.getTime()); + return Math.floor(diffTime / (1000 * 60 * 60 * 24)); +}; + +export const wrapThrowsAsync = + (fn: (...args: A) => Promise) => + async (...args: A): Promise> => { + try { + return { + ok: true, + data: await fn(...args), + }; + } catch (error) { + return { + ok: false, + error: error as Error, + }; + } + }; + +/** + * Filters surveys based on the displayOption, recontactDays, and segments + * @param environmentSate - The environment state + * @param userState - The user state + * @returns The filtered surveys + */ + +// takes the environment and user state and returns the filtered surveys +export const filterSurveys = ( + environmentState: TEnvironmentState, + userState: TUserState +): TEnvironmentStateSurvey[] => { + const { project, surveys } = environmentState.data; + const { displays, responses, lastDisplayAt, segments, userId } = userState.data; + + // Function to filter surveys based on displayOption criteria + let filteredSurveys = surveys.filter((survey: TEnvironmentStateSurvey) => { + switch (survey.displayOption) { + case "respondMultiple": + return true; + case "displayOnce": + return displays.filter((display) => display.surveyId === survey.id).length === 0; + case "displayMultiple": + return responses.filter((surveyId) => surveyId === survey.id).length === 0; + + case "displaySome": + if (survey.displayLimit === null) { + return true; + } + + // Check if survey response exists, if so, stop here + if (responses.filter((surveyId) => surveyId === survey.id).length) { + return false; + } + + // Otherwise, check if displays length is less than displayLimit + return displays.filter((display) => display.surveyId === survey.id).length < survey.displayLimit; + + default: + throw Error("Invalid displayOption"); + } + }); + + // filter surveys that meet the recontactDays criteria + filteredSurveys = filteredSurveys.filter((survey) => { + // if no survey was displayed yet, show the survey + if (!lastDisplayAt) { + return true; + } + + // if survey has recontactDays, check if the last display was more than recontactDays ago + // The previous approach checked the last display for each survey which is why we still have a surveyId in the displays array. + // TODO: Remove the surveyId from the displays array + if (survey.recontactDays !== null) { + return diffInDays(new Date(), new Date(lastDisplayAt)) >= survey.recontactDays; + } + + // use recontactDays of the project if survey does not have recontactDays + if (project.recontactDays) { + return diffInDays(new Date(), new Date(lastDisplayAt)) >= project.recontactDays; + } + + // if no recontactDays is set, show the survey + + return true; + }); + + if (!userId) { + return filteredSurveys; + } + + if (!segments.length) { + return []; + } + + // filter surveys based on segments + return filteredSurveys.filter((survey) => { + return survey.segment?.id && segments.includes(survey.segment.id); + }); +}; + +export const getStyling = ( + project: TEnvironmentStateProject, + survey: TEnvironmentStateSurvey +): TProjectStyling | TSurveyStyling => { + // allow style overwrite is enabled from the project + if (project.styling.allowStyleOverwrite) { + // survey style overwrite is disabled + if (!survey.styling?.overwriteThemeStyling) { + return project.styling; + } + + // survey style overwrite is enabled + return survey.styling; + } + + // allow style overwrite is disabled from the project + return project.styling; +}; + +export const getDefaultLanguageCode = (survey: TEnvironmentStateSurvey): string | undefined => { + const defaultSurveyLanguage = survey.languages.find((surveyLanguage) => { + return surveyLanguage.default; + }); + if (defaultSurveyLanguage) return defaultSurveyLanguage.language.code; +}; + +export const getLanguageCode = (survey: TEnvironmentStateSurvey, language?: string): string | undefined => { + const availableLanguageCodes = survey.languages.map((surveyLanguage) => surveyLanguage.language.code); + if (!language) return "default"; + + const selectedLanguage = survey.languages.find((surveyLanguage) => { + return ( + surveyLanguage.language.code === language.toLowerCase() || + surveyLanguage.language.alias?.toLowerCase() === language.toLowerCase() + ); + }); + if (selectedLanguage?.default) { + return "default"; + } + if ( + !selectedLanguage || + !selectedLanguage.enabled || + !availableLanguageCodes.includes(selectedLanguage.language.code) + ) { + return undefined; + } + return selectedLanguage.language.code; +}; + +export const shouldDisplayBasedOnPercentage = (displayPercentage: number): boolean => { + const randomNum = Math.floor(Math.random() * 10000) / 100; + return randomNum <= displayPercentage; +}; + +export const isNowExpired = (expirationDate: Date): boolean => { + return new Date() >= expirationDate; +}; diff --git a/packages/react-native/src/lib/environment-state.ts b/packages/react-native/src/lib/environment-state.ts deleted file mode 100644 index cdb8887661..0000000000 --- a/packages/react-native/src/lib/environment-state.ts +++ /dev/null @@ -1,130 +0,0 @@ -/* eslint-disable no-console -- logging required for error logging */ -// shared functions for environment and person state(s) -import { type TJsEnvironmentState, type TJsEnvironmentSyncParams } from "@formbricks/types/js"; -import { err } from "../../../js-core/src/lib/errors"; -import { Logger } from "../../../js-core/src/lib/logger"; -import { filterSurveys } from "../../../js-core/src/lib/utils"; -import { RNConfig } from "./config"; - -const appConfig = RNConfig.getInstance(); -const logger = Logger.getInstance(); -let environmentStateSyncIntervalId: number | null = null; - -/** - * Fetch the environment state from the backend - * @param apiHost - The API host - * @param environmentId - The environment ID - * @param noCache - Whether to skip the cache - * @returns The environment state - * @throws NetworkError - */ -export const fetchEnvironmentState = async ( - { apiHost, environmentId }: TJsEnvironmentSyncParams, - noCache = false -): Promise => { - const url = `${apiHost}/api/v1/client/${environmentId}/environment`; - - try { - const fetchOptions: RequestInit = {}; - - if (noCache) { - fetchOptions.cache = "no-cache"; - logger.debug("No cache option set for sync"); - } - - const response = await fetch(url, fetchOptions); - - if (!response.ok) { - const jsonRes = (await response.json()) as { message: string }; - - const error = err({ - code: "network_error", - status: response.status, - message: "Error syncing with backend", - url: new URL(url), - responseMessage: jsonRes.message, - }); - - // eslint-disable-next-line @typescript-eslint/only-throw-error -- error.error is an Error object - throw error.error; - } - - const data = (await response.json()) as { data: TJsEnvironmentState["data"] }; - const { data: state } = data; - - return { - data: { ...state }, - expiresAt: new Date(new Date().getTime() + 1000 * 60 * 30), // 30 minutes - }; - } catch (e: unknown) { - const errorTyped = e as { message?: string }; - - const error = err({ - code: "network_error", - message: errorTyped.message ?? "Error fetching the environment state", - status: 500, - url: new URL(url), - responseMessage: errorTyped.message ?? "Unknown error", - }); - - // eslint-disable-next-line @typescript-eslint/only-throw-error -- error.error is an Error object - throw error.error; - } -}; - -/** - * Add a listener to check if the environment state has expired with a certain interval - */ -export const addEnvironmentStateExpiryCheckListener = (): void => { - const updateInterval = 1000 * 60; // every minute - - if (environmentStateSyncIntervalId === null) { - const intervalHandler = async (): Promise => { - const expiresAt = appConfig.get().environmentState.expiresAt; - - try { - // check if the environmentState has not expired yet - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- expiresAt is checked for null - if (expiresAt && new Date(expiresAt) >= new Date()) { - return; - } - - logger.debug("Environment State has expired. Starting sync."); - - const personState = appConfig.get().personState; - const environmentState = await fetchEnvironmentState( - { - apiHost: appConfig.get().apiHost, - environmentId: appConfig.get().environmentId, - }, - true - ); - - const filteredSurveys = filterSurveys(environmentState, personState); - - appConfig.update({ - ...appConfig.get(), - environmentState, - filteredSurveys, - }); - } catch (e) { - console.error(`Error during expiry check: ${e as string}`); - logger.debug("Extending config and try again later."); - const existingConfig = appConfig.get(); - appConfig.update(existingConfig); - } - }; - - environmentStateSyncIntervalId = setInterval( - () => void intervalHandler(), - updateInterval - ) as unknown as number; - } -}; - -export const clearEnvironmentStateExpiryCheckListener = (): void => { - if (environmentStateSyncIntervalId) { - clearInterval(environmentStateSyncIntervalId); - environmentStateSyncIntervalId = null; - } -}; diff --git a/packages/react-native/src/lib/environment/state.ts b/packages/react-native/src/lib/environment/state.ts new file mode 100644 index 0000000000..f62349df67 --- /dev/null +++ b/packages/react-native/src/lib/environment/state.ts @@ -0,0 +1,118 @@ +/* eslint-disable no-console -- logging required for error logging */ +import { FormbricksAPI } from "@formbricks/api"; +import { RNConfig } from "@/lib/common/config"; +import { Logger } from "@/lib/common/logger"; +import { filterSurveys } from "@/lib/common/utils"; +import type { TConfigInput, TEnvironmentState } from "@/types/config"; +import { type ApiErrorResponse, type Result, err, ok } from "@/types/error"; + +let environmentStateSyncIntervalId: number | null = null; + +/** + * Fetch the environment state from the backend + * @param appUrl - The app URL + * @param environmentId - The environment ID + * @returns The environment state + * @throws NetworkError + */ +export const fetchEnvironmentState = async ({ + appUrl, + environmentId, +}: TConfigInput): Promise> => { + const url = `${appUrl}/api/v1/client/${environmentId}/environment`; + const api = new FormbricksAPI({ apiHost: appUrl, environmentId }); + + try { + const response = await api.client.environment.getState(); + + if (!response.ok) { + return err({ + code: response.error.code, + status: response.error.status, + message: "Error syncing with backend", + url: new URL(url), + responseMessage: response.error.message, + }); + } + + return ok(response.data) as Result; + } catch (e: unknown) { + const errorTyped = e as ApiErrorResponse; + return err({ + code: "network_error", + message: errorTyped.message, + status: 500, + url: new URL(url), + responseMessage: errorTyped.responseMessage ?? "Network error", + }); + } +}; + +/** + * Add a listener to check if the environment state has expired with a certain interval + */ +export const addEnvironmentStateExpiryCheckListener = (): void => { + const appConfig = RNConfig.getInstance(); + const logger = Logger.getInstance(); + + const updateInterval = 1000 * 60; // every minute + + if (environmentStateSyncIntervalId === null) { + const intervalHandler = async (): Promise => { + const expiresAt = appConfig.get().environment.expiresAt; + + try { + // check if the environmentState has not expired yet + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- expiresAt is checked for null + if (expiresAt && new Date(expiresAt) >= new Date()) { + return; + } + + logger.debug("Environment State has expired. Starting sync."); + + const personState = appConfig.get().user; + const environmentState = await fetchEnvironmentState({ + appUrl: appConfig.get().appUrl, + environmentId: appConfig.get().environmentId, + }); + + if (environmentState.ok) { + const { data: state } = environmentState; + const filteredSurveys = filterSurveys(state, personState); + + appConfig.update({ + ...appConfig.get(), + environment: state, + filteredSurveys, + }); + } else { + // eslint-disable-next-line @typescript-eslint/only-throw-error -- error is an ApiErrorResponse + throw environmentState.error; + } + } catch (e) { + console.error(`Error during expiry check: `, e); + logger.debug("Extending config and try again later."); + const existingConfig = appConfig.get(); + appConfig.update({ + ...existingConfig, + environment: { + ...existingConfig.environment, + expiresAt: new Date(new Date().getTime() + 1000 * 60 * 30), // 30 minutes + }, + }); + } + }; + + environmentStateSyncIntervalId = setInterval( + () => void intervalHandler(), + updateInterval + ) as unknown as number; + } +}; + +export const clearEnvironmentStateExpiryCheckListener = (): void => { + if (environmentStateSyncIntervalId) { + clearInterval(environmentStateSyncIntervalId); + environmentStateSyncIntervalId = null; + } +}; diff --git a/packages/react-native/src/lib/environment/tests/state.test.ts b/packages/react-native/src/lib/environment/tests/state.test.ts new file mode 100644 index 0000000000..04807d866c --- /dev/null +++ b/packages/react-native/src/lib/environment/tests/state.test.ts @@ -0,0 +1,306 @@ +// state.test.ts +import { type Mock, type MockInstance, afterEach, beforeEach, describe, expect, test, vi } from "vitest"; +import { FormbricksAPI } from "@formbricks/api"; +import { RNConfig } from "@/lib/common/config"; +import { Logger } from "@/lib/common/logger"; +import { filterSurveys } from "@/lib/common/utils"; +import { + addEnvironmentStateExpiryCheckListener, + clearEnvironmentStateExpiryCheckListener, + fetchEnvironmentState, +} from "@/lib/environment/state"; +import type { TEnvironmentState } from "@/types/config"; + +// Mock the FormbricksAPI so we can control environment.getState +vi.mock("@formbricks/api", () => ({ + FormbricksAPI: vi.fn().mockImplementation(() => ({ + client: { + environment: { + getState: vi.fn(), + }, + }, + })), +})); + +// Mock logger (so we don’t spam console) +vi.mock("@/lib/common/logger", () => ({ + Logger: { + getInstance: vi.fn(() => { + return { + debug: vi.fn(), + error: vi.fn(), + }; + }), + }, +})); + +// Mock filterSurveys +vi.mock("@/lib/common/utils", () => ({ + filterSurveys: vi.fn(), +})); + +// Mock RNConfig +vi.mock("@/lib/common/config", () => { + return { + RN_ASYNC_STORAGE_KEY: "formbricks-react-native", + RNConfig: { + getInstance: vi.fn(() => ({ + get: vi.fn(), + update: vi.fn(), + })), + }, + }; +}); + +describe("environment/state.ts", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + afterEach(() => { + // Use real timers so we don't pollute subsequent test code + vi.useRealTimers(); + }); + + describe("fetchEnvironmentState()", () => { + test("returns ok(...) with environment state", async () => { + // Setup mock + (FormbricksAPI as unknown as Mock).mockImplementationOnce(() => { + return { + client: { + environment: { + getState: vi.fn().mockResolvedValue({ + ok: true, + data: { data: { foo: "bar" }, expiresAt: new Date(Date.now() + 1000 * 60 * 30) }, + }), + }, + }, + }; + }); + + const result = await fetchEnvironmentState({ + appUrl: "https://fake.host", + environmentId: "env_123", + }); + + expect(result.ok).toBe(true); + + if (result.ok) { + const val: TEnvironmentState = result.data; + expect(val.data).toEqual({ foo: "bar" }); + expect(val.expiresAt).toBeInstanceOf(Date); + } + }); + + test("returns err(...) if environment.getState is not ok", async () => { + const mockError = { code: "forbidden", status: 403, message: "Access denied" }; + + (FormbricksAPI as unknown as Mock).mockImplementationOnce(() => { + return { + client: { + environment: { + getState: vi.fn().mockResolvedValue({ + ok: false, + error: mockError, + }), + }, + }, + }; + }); + + const result = await fetchEnvironmentState({ + appUrl: "https://fake.host", + environmentId: "env_123", + }); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe(mockError.code); + expect(result.error.status).toBe(mockError.status); + expect(result.error.responseMessage).toBe(mockError.message); + } + }); + + test("returns err(...) on network error catch", async () => { + const mockNetworkError = { + code: "network_error", + message: "Timeout", + responseMessage: "Network fail", + }; + + (FormbricksAPI as unknown as Mock).mockImplementationOnce(() => { + return { + client: { + environment: { + getState: vi.fn().mockRejectedValue(mockNetworkError), + }, + }, + }; + }); + + const result = await fetchEnvironmentState({ + appUrl: "https://fake.host", + environmentId: "env_123", + }); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe(mockNetworkError.code); + expect(result.error.message).toBe(mockNetworkError.message); + expect(result.error.responseMessage).toBe(mockNetworkError.responseMessage); + } + }); + }); + + describe("addEnvironmentStateExpiryCheckListener()", () => { + let mockRNConfig: MockInstance<() => RNConfig>; + let mockLoggerInstance: MockInstance<() => Logger>; + + const mockLogger = { + debug: vi.fn(), + error: vi.fn(), + }; + + beforeEach(() => { + vi.clearAllMocks(); + vi.useFakeTimers(); + + mockRNConfig = vi.spyOn(RNConfig, "getInstance"); + const mockConfig = { + get: vi.fn().mockReturnValue({ + environment: { + expiresAt: new Date(Date.now() + 60_000), // Not expired for now + }, + user: {}, + environmentId: "env_123", + appUrl: "https://fake.host", + }), + }; + + mockRNConfig.mockReturnValue(mockConfig as unknown as RNConfig); + + mockLoggerInstance = vi.spyOn(Logger, "getInstance"); + mockLoggerInstance.mockReturnValue(mockLogger as unknown as Logger); + }); + + afterEach(() => { + clearEnvironmentStateExpiryCheckListener(); // clear after each test + }); + + test("starts interval check and updates state when expired", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + environment: { + expiresAt: new Date(Date.now() - 1000).toISOString(), // expired + }, + appUrl: "https://test.com", + environmentId: "env_123", + user: { data: {} }, + }), + update: vi.fn(), + }; + + const mockNewState = { + data: { + expiresAt: new Date(Date.now() + 1000 * 60 * 30).toISOString(), + }, + }; + + mockRNConfig.mockReturnValue(mockConfig as unknown as RNConfig); + + (FormbricksAPI as Mock).mockImplementation(() => ({ + client: { + environment: { + getState: vi.fn().mockResolvedValue({ + ok: true, + data: mockNewState, + }), + }, + }, + })); + + (filterSurveys as Mock).mockReturnValue([]); + + // Add listener + addEnvironmentStateExpiryCheckListener(); + + // Fast-forward time + await vi.advanceTimersByTimeAsync(1000 * 60); + + // Verify the update was called + expect(mockConfig.update).toHaveBeenCalled(); + }); + + test("extends expiry on error", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + environment: { + expiresAt: new Date(Date.now() - 1000).toISOString(), + }, + appUrl: "https://test.com", + environmentId: "env_123", + }), + update: vi.fn(), + }; + + mockRNConfig.mockReturnValue(mockConfig as unknown as RNConfig); + + // Mock API to throw an error + (FormbricksAPI as Mock).mockImplementation(() => ({ + client: { + environment: { + getState: vi.fn().mockRejectedValue(new Error("Network error")), + }, + }, + })); + + addEnvironmentStateExpiryCheckListener(); + + // Fast-forward time + await vi.advanceTimersByTimeAsync(1000 * 60); + + // Verify the config was updated with extended expiry + expect(mockConfig.update).toHaveBeenCalled(); + }); + + test("does not fetch new state if not expired", async () => { + const futureDate = new Date(Date.now() + 1000 * 60 * 60); // 1 hour in future + const mockConfig = { + get: vi.fn().mockReturnValue({ + environment: { + expiresAt: futureDate.toISOString(), + }, + appUrl: "https://test.com", + environmentId: "env_123", + }), + update: vi.fn(), + }; + + mockRNConfig.mockReturnValue(mockConfig as unknown as RNConfig); + + const apiMock = vi.fn().mockImplementation(() => ({ + client: { + environment: { + getState: vi.fn(), + }, + }, + })); + + (FormbricksAPI as Mock).mockImplementation(apiMock); + + addEnvironmentStateExpiryCheckListener(); + + // Fast-forward time by less than expiry + await vi.advanceTimersByTimeAsync(1000 * 60); + + expect(mockConfig.update).not.toHaveBeenCalled(); + }); + + test("clears interval when clearEnvironmentStateExpiryCheckListener is called", () => { + const clearIntervalSpy = vi.spyOn(global, "clearInterval"); + + addEnvironmentStateExpiryCheckListener(); + clearEnvironmentStateExpiryCheckListener(); + + expect(clearIntervalSpy).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/react-native/src/lib/index.ts b/packages/react-native/src/lib/index.ts deleted file mode 100644 index 096be58cea..0000000000 --- a/packages/react-native/src/lib/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { type TJsConfigInput } from "@formbricks/types/js"; -import { ErrorHandler } from "../../../js-core/src/lib/errors"; -import { Logger } from "../../../js-core/src/lib/logger"; -import { trackCodeAction } from "./actions"; -import { CommandQueue } from "./command-queue"; -import { initialize } from "./initialize"; - -const logger = Logger.getInstance(); -logger.debug("Create command queue"); -const queue = new CommandQueue(); - -export const init = async (initConfig: TJsConfigInput): Promise => { - ErrorHandler.init(initConfig.errorHandler); - queue.add(initialize, false, initConfig); - await queue.wait(); -}; - -export const track = async (name: string, properties = {}): Promise => { - queue.add(trackCodeAction, true, name, properties); - await queue.wait(); -}; diff --git a/packages/react-native/src/lib/initialize.ts b/packages/react-native/src/lib/initialize.ts deleted file mode 100644 index c7d8142b2c..0000000000 --- a/packages/react-native/src/lib/initialize.ts +++ /dev/null @@ -1,274 +0,0 @@ -import AsyncStorage from "@react-native-async-storage/async-storage"; -import { type TAttributes } from "@formbricks/types/attributes"; -import { wrapThrowsAsync } from "@formbricks/types/error-handlers"; -import { type TJsConfig, type TJsConfigInput } from "@formbricks/types/js"; -import { RN_ASYNC_STORAGE_KEY } from "../../../js-core/src/lib/constants"; -import { - ErrorHandler, - type MissingFieldError, - type MissingPersonError, - type NetworkError, - type NotInitializedError, - type Result, - err, - okVoid, -} from "../../../js-core/src/lib/errors"; -import { Logger } from "../../../js-core/src/lib/logger"; -import { filterSurveys } from "../../../js-core/src/lib/utils"; -import { trackAction } from "./actions"; -import { updateAttributes } from "./attributes"; -import { RNConfig } from "./config"; -import { fetchEnvironmentState } from "./environment-state"; -import { addCleanupEventListeners, addEventListeners, removeAllEventListeners } from "./event-listeners"; -import { DEFAULT_PERSON_STATE_NO_USER_ID, fetchPersonState } from "./person-state"; - -let isInitialized = false; -const appConfig = RNConfig.getInstance(); -const logger = Logger.getInstance(); - -export const setIsInitialize = (state: boolean): void => { - isInitialized = state; -}; - -export const initialize = async ( - configInput: TJsConfigInput -): Promise> => { - if (isInitialized) { - logger.debug("Already initialized, skipping initialization."); - return okVoid(); - } - - let existingConfig: TJsConfig | undefined; - try { - existingConfig = appConfig.get(); - logger.debug("Found existing configuration."); - } catch { - logger.debug("No existing configuration found."); - } - - // formbricks is in error state, skip initialization - if (existingConfig?.status.value === "error") { - logger.debug("Formbricks was set to an error state."); - - const expiresAt = existingConfig.status.expiresAt; - - if (expiresAt && new Date(expiresAt) > new Date()) { - logger.debug("Error state is not expired, skipping initialization"); - return okVoid(); - } - logger.debug("Error state is expired. Continue with initialization."); - } - - ErrorHandler.getInstance().printStatus(); - - logger.debug("Start initialize"); - - if (!configInput.environmentId) { - logger.debug("No environmentId provided"); - return err({ - code: "missing_field", - field: "environmentId", - }); - } - - if (!configInput.apiHost) { - logger.debug("No apiHost provided"); - - return err({ - code: "missing_field", - field: "apiHost", - }); - } - - if ( - existingConfig?.environmentState && - existingConfig.environmentId === configInput.environmentId && - existingConfig.apiHost === configInput.apiHost - ) { - logger.debug("Configuration fits init parameters."); - let isEnvironmentStateExpired = false; - let isPersonStateExpired = false; - - if (new Date(existingConfig.environmentState.expiresAt) < new Date()) { - logger.debug("Environment state expired. Syncing."); - isEnvironmentStateExpired = true; - } - - if ( - configInput.userId && - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- personState could be null - (existingConfig.personState === null || - (existingConfig.personState.expiresAt && new Date(existingConfig.personState.expiresAt) < new Date())) - ) { - logger.debug("Person state needs syncing - either null or expired"); - isPersonStateExpired = true; - } - - try { - // fetch the environment state (if expired) - const environmentState = isEnvironmentStateExpired - ? await fetchEnvironmentState({ - apiHost: configInput.apiHost, - environmentId: configInput.environmentId, - }) - : existingConfig.environmentState; - - // fetch the person state (if expired) - - let { personState } = existingConfig; - - if (isPersonStateExpired) { - if (configInput.userId) { - personState = await fetchPersonState({ - apiHost: configInput.apiHost, - environmentId: configInput.environmentId, - userId: configInput.userId, - }); - } else { - personState = DEFAULT_PERSON_STATE_NO_USER_ID; - } - } - - // filter the environment state wrt the person state - const filteredSurveys = filterSurveys(environmentState, personState); - - // update the appConfig with the new filtered surveys - appConfig.update({ - ...existingConfig, - environmentState, - personState, - filteredSurveys, - attributes: configInput.attributes ?? {}, - }); - - const surveyNames = filteredSurveys.map((s) => s.name); - logger.debug(`Fetched ${surveyNames.length.toString()} surveys during sync: ${surveyNames.join(", ")}`); - } catch { - logger.debug("Error during sync. Please try again."); - } - } else { - logger.debug("No valid configuration found. Resetting config and creating new one."); - void appConfig.resetConfig(); - logger.debug("Syncing."); - - try { - const environmentState = await fetchEnvironmentState( - { - apiHost: configInput.apiHost, - environmentId: configInput.environmentId, - }, - false - ); - - const personState = configInput.userId - ? await fetchPersonState( - { - apiHost: configInput.apiHost, - environmentId: configInput.environmentId, - userId: configInput.userId, - }, - false - ) - : DEFAULT_PERSON_STATE_NO_USER_ID; - - const filteredSurveys = filterSurveys(environmentState, personState); - - let updatedAttributes: TAttributes | null = null; - if (configInput.attributes) { - if (configInput.userId) { - const res = await updateAttributes( - configInput.apiHost, - configInput.environmentId, - configInput.userId, - configInput.attributes - ); - - if (!res.ok) { - if (res.error.code === "forbidden") { - logger.error(`Authorization error: ${res.error.responseMessage ?? ""}`); - } - return err(res.error) as unknown as Result< - void, - MissingFieldError | NetworkError | MissingPersonError - >; - } - - updatedAttributes = res.value; - } else { - updatedAttributes = { ...configInput.attributes }; - } - } - - appConfig.update({ - apiHost: configInput.apiHost, - environmentId: configInput.environmentId, - personState, - environmentState, - filteredSurveys, - attributes: updatedAttributes ?? {}, - }); - } catch (e) { - await handleErrorOnFirstInit(e as { code: string; responseMessage: string }); - } - - // and track the new session event - trackAction("New Session"); - } - - logger.debug("Adding event listeners"); - addEventListeners(); - addCleanupEventListeners(); - - setIsInitialize(true); - logger.debug("Initialized"); - - // check page url if initialized after page load - return okVoid(); -}; - -export const checkInitialized = (): Result => { - logger.debug("Check if initialized"); - - if (!isInitialized || !ErrorHandler.initialized) { - return err({ - code: "not_initialized", - message: "Formbricks not initialized. Call initialize() first.", - }); - } - - return okVoid(); -}; - -export const deinitalize = async (): Promise => { - logger.debug("Deinitializing"); - await appConfig.resetConfig(); - setIsInitialize(false); - removeAllEventListeners(); -}; - -export const handleErrorOnFirstInit = async (e: { - code: string; - responseMessage: string; -}): Promise => { - if (e.code === "forbidden") { - logger.error(`Authorization error: ${e.responseMessage}`); - } else { - logger.error( - `Error during first initialization: ${e.code} - ${e.responseMessage}. Please try again later.` - ); - } - - // put formbricks in error state (by creating a new config) and throw error - const initialErrorConfig: Partial = { - status: { - value: "error", - expiresAt: new Date(new Date().getTime() + 10 * 60000), // 10 minutes in the future - }, - }; - - await wrapThrowsAsync(async () => { - await AsyncStorage.setItem(RN_ASYNC_STORAGE_KEY, JSON.stringify(initialErrorConfig)); - })(); - - throw new Error("Could not initialize formbricks"); -}; diff --git a/packages/react-native/src/lib/person-state.ts b/packages/react-native/src/lib/person-state.ts deleted file mode 100644 index a890dc98e6..0000000000 --- a/packages/react-native/src/lib/person-state.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { type TJsPersonState, type TJsPersonSyncParams } from "@formbricks/types/js"; -import { err } from "../../../js-core/src/lib/errors"; -import { Logger } from "../../../js-core/src/lib/logger"; -import { RNConfig } from "./config"; - -const config = RNConfig.getInstance(); -const logger = Logger.getInstance(); -let personStateSyncIntervalId: number | null = null; - -export const DEFAULT_PERSON_STATE_NO_USER_ID: TJsPersonState = { - expiresAt: null, - data: { - userId: null, - segments: [], - displays: [], - responses: [], - lastDisplayAt: null, - }, -} as const; - -/** - * Fetch the person state from the backend - * @param apiHost - The API host - * @param environmentId - The environment ID - * @param userId - The user ID - * @param noCache - Whether to skip the cache - * @returns The person state - * @throws NetworkError - */ -export const fetchPersonState = async ( - { apiHost, environmentId, userId }: TJsPersonSyncParams, - noCache = false -): Promise => { - const url = `${apiHost}/api/v1/client/${environmentId}/identify/contacts/${userId}`; - - try { - const fetchOptions: RequestInit = {}; - - if (noCache) { - fetchOptions.cache = "no-cache"; - logger.debug("No cache option set for sync"); - } - - const response = await fetch(url, fetchOptions); - - if (!response.ok) { - const jsonRes = (await response.json()) as { code: string; message: string }; - - const error = err({ - code: jsonRes.code === "forbidden" ? "forbidden" : "network_error", - status: response.status, - message: "Error syncing with backend", - url: new URL(url), - responseMessage: jsonRes.message, - }); - - // eslint-disable-next-line @typescript-eslint/only-throw-error -- error.error is an Error object - throw error.error; - } - - const data = (await response.json()) as { data: TJsPersonState["data"] }; - const { data: state } = data; - - const defaultPersonState: TJsPersonState = { - expiresAt: new Date(new Date().getTime() + 1000 * 60 * 30), // 30 minutes - data: { - userId, - segments: [], - displays: [], - responses: [], - lastDisplayAt: null, - }, - }; - - if (!Object.keys(state).length) { - return defaultPersonState; - } - - return { - data: { ...state }, - expiresAt: new Date(new Date().getTime() + 1000 * 60 * 30), // 30 minutes - }; - } catch (e: unknown) { - const errorTyped = e as { message?: string }; - - const error = err({ - code: "network_error", - message: errorTyped.message ?? "Error fetching the person state", - status: 500, - url: new URL(url), - responseMessage: errorTyped.message ?? "Unknown error", - }); - - // eslint-disable-next-line @typescript-eslint/only-throw-error -- error.error is an Error object - throw error.error; - } -}; - -/** - * Add a listener to check if the person state has expired with a certain interval - */ -export const addPersonStateExpiryCheckListener = (): void => { - const updateInterval = 1000 * 60; // every 60 seconds - - if (personStateSyncIntervalId === null) { - const intervalHandler = (): void => { - const userId = config.get().personState.data.userId; - - if (!userId) { - return; - } - - // extend the personState validity by 30 minutes: - config.update({ - ...config.get(), - personState: { - ...config.get().personState, - expiresAt: new Date(new Date().getTime() + 1000 * 60 * 30), // 30 minutes - }, - }); - }; - - personStateSyncIntervalId = setInterval(intervalHandler, updateInterval) as unknown as number; - } -}; - -/** - * Clear the person state expiry check listener - */ -export const clearPersonStateExpiryCheckListener = (): void => { - if (personStateSyncIntervalId) { - clearInterval(personStateSyncIntervalId); - personStateSyncIntervalId = null; - } -}; diff --git a/packages/react-native/src/lib/person.ts b/packages/react-native/src/lib/person.ts deleted file mode 100644 index 1a81da368e..0000000000 --- a/packages/react-native/src/lib/person.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { type NetworkError, type Result, err, okVoid } from "../../../js-core/src/lib/errors"; -import { Logger } from "../../../js-core/src/lib/logger"; -import { RNConfig } from "./config"; -import { deinitalize, initialize } from "./initialize"; - -const appConfig = RNConfig.getInstance(); -const logger = Logger.getInstance(); - -export const logoutPerson = async (): Promise => { - await deinitalize(); - await appConfig.resetConfig(); -}; - -export const resetPerson = async (): Promise> => { - logger.debug("Resetting state & getting new state from backend"); - const userId = appConfig.get().personState.data.userId; - const syncParams = { - environmentId: appConfig.get().environmentId, - apiHost: appConfig.get().apiHost, - ...(userId && { userId }), - attributes: appConfig.get().attributes, - }; - await logoutPerson(); - try { - await initialize(syncParams); - return okVoid(); - } catch (e) { - return err(e as NetworkError); - } -}; diff --git a/packages/react-native/src/lib/actions.ts b/packages/react-native/src/lib/survey/action.ts similarity index 52% rename from packages/react-native/src/lib/actions.ts rename to packages/react-native/src/lib/survey/action.ts index 14ca81292f..e175e88e23 100644 --- a/packages/react-native/src/lib/actions.ts +++ b/packages/react-native/src/lib/survey/action.ts @@ -1,21 +1,18 @@ -import type { TJsEnvironmentStateSurvey } from "@formbricks/types/js"; -import { - type InvalidCodeError, - type NetworkError, - type Result, - err, - okVoid, -} from "../../../js-core/src/lib/errors"; -import { Logger } from "../../../js-core/src/lib/logger"; -import { shouldDisplayBasedOnPercentage } from "../../../js-core/src/lib/utils"; -import { RNConfig } from "./config"; -import { SurveyStore } from "./survey-store"; +import { RNConfig } from "@/lib/common/config"; +import { Logger } from "@/lib/common/logger"; +import { shouldDisplayBasedOnPercentage } from "@/lib/common/utils"; +import { SurveyStore } from "@/lib/survey/store"; +import type { TEnvironmentStateSurvey } from "@/types/config"; +import { type InvalidCodeError, type NetworkError, type Result, err, okVoid } from "@/types/error"; -const appConfig = RNConfig.getInstance(); -const logger = Logger.getInstance(); -const surveyStore = SurveyStore.getInstance(); +/** + * Triggers the display of a survey if it meets the display percentage criteria + * @param survey - The survey configuration to potentially display + */ +export const triggerSurvey = (survey: TEnvironmentStateSurvey): void => { + const surveyStore = SurveyStore.getInstance(); + const logger = Logger.getInstance(); -export const triggerSurvey = (survey: TJsEnvironmentStateSurvey): void => { // Check if the survey should be displayed based on displayPercentage if (survey.displayPercentage) { const shouldDisplaySurvey = shouldDisplayBasedOnPercentage(survey.displayPercentage); @@ -28,7 +25,16 @@ export const triggerSurvey = (survey: TJsEnvironmentStateSurvey): void => { surveyStore.setSurvey(survey); }; +/** + * Tracks an action name and triggers associated surveys + * @param name - The name of the action to track + * @param alias - Optional alias for the action name + * @returns Result indicating success or network error + */ export const trackAction = (name: string, alias?: string): Result => { + const logger = Logger.getInstance(); + const appConfig = RNConfig.getInstance(); + const aliasName = alias ?? name; logger.debug(`Formbricks: Action "${aliasName}" tracked`); @@ -51,11 +57,16 @@ export const trackAction = (name: string, alias?: string): Result | Result => { +/** + * Tracks an action by its code and triggers associated surveys (used for code actions only) + * @param code - The action code to track + * @returns Result indicating success, network error, or invalid code error + */ +export const track = (code: string): Result | Result => { + const appConfig = RNConfig.getInstance(); + const { - environmentState: { + environment: { data: { actionClasses = [] }, }, } = appConfig.get(); diff --git a/packages/react-native/src/lib/survey/state.ts b/packages/react-native/src/lib/survey/state.ts new file mode 100644 index 0000000000..c7af6974a8 --- /dev/null +++ b/packages/react-native/src/lib/survey/state.ts @@ -0,0 +1,98 @@ +import { type TResponseUpdate } from "@/types/response"; + +export class SurveyState { + responseId: string | null = null; + displayId: string | null = null; + userId: string | null = null; + surveyId: string; + responseAcc: TResponseUpdate = { finished: false, data: {}, ttc: {}, variables: {} }; + singleUseId: string | null; + + constructor( + surveyId: string, + singleUseId?: string | null, + responseId?: string | null, + userId?: string | null + ) { + this.surveyId = surveyId; + this.userId = userId ?? null; + this.singleUseId = singleUseId ?? null; + this.responseId = responseId ?? null; + } + + /** + * Set the current survey ID + * @param id - The survey ID + */ + setSurveyId(id: string): void { + this.surveyId = id; + this.clear(); // Reset the state when setting a new surveyId + } + /** + * Get a copy of the current state + */ + copy(): SurveyState { + const copyInstance = new SurveyState( + this.surveyId, + this.singleUseId ?? undefined, + this.responseId ?? undefined, + this.userId ?? undefined + ); + copyInstance.responseId = this.responseId; + copyInstance.responseAcc = this.responseAcc; + return copyInstance; + } + + /** + * Update the response ID after a successful response creation + * @param id - The response ID + */ + updateResponseId(id: string): void { + this.responseId = id; + } + + /** + * Update the display ID after a successful display creation + * @param id - The display ID + */ + updateDisplayId(id: string): void { + this.displayId = id; + } + + /** + * Update the user ID + * @param id - The user ID + */ + updateUserId(id: string): void { + this.userId = id; + } + + /** + * Accumulate the responses + * @param responseUpdate - The new response data to add + */ + accumulateResponse(responseUpdate: TResponseUpdate): void { + this.responseAcc = { + finished: responseUpdate.finished, + ttc: responseUpdate.ttc, + data: { ...this.responseAcc.data, ...responseUpdate.data }, + variables: responseUpdate.variables, + displayId: responseUpdate.displayId, + }; + } + + /** + * Check if the current accumulated response is finished + */ + isResponseFinished(): boolean { + return this.responseAcc.finished; + } + + /** + * Clear the current state + */ + clear(): void { + this.responseId = null; + this.responseAcc = { finished: false, data: {}, ttc: {}, variables: {} }; + } +} diff --git a/packages/react-native/src/lib/survey-store.ts b/packages/react-native/src/lib/survey/store.ts similarity index 68% rename from packages/react-native/src/lib/survey-store.ts rename to packages/react-native/src/lib/survey/store.ts index c67e58be96..92de2fae45 100644 --- a/packages/react-native/src/lib/survey-store.ts +++ b/packages/react-native/src/lib/survey/store.ts @@ -1,13 +1,10 @@ -import { type TJsEnvironmentStateSurvey } from "@formbricks/types/js"; +import type { TEnvironmentStateSurvey } from "@/types/config"; -type Listener = ( - state: TJsEnvironmentStateSurvey | null, - prevSurvey: TJsEnvironmentStateSurvey | null -) => void; +type Listener = (state: TEnvironmentStateSurvey | null, prevSurvey: TEnvironmentStateSurvey | null) => void; export class SurveyStore { private static instance: SurveyStore | undefined; - private survey: TJsEnvironmentStateSurvey | null = null; + private survey: TEnvironmentStateSurvey | null = null; private listeners = new Set(); static getInstance(): SurveyStore { @@ -17,13 +14,13 @@ export class SurveyStore { return SurveyStore.instance; } - public getSurvey(): TJsEnvironmentStateSurvey | null { + public getSurvey(): TEnvironmentStateSurvey | null { return this.survey; } - public setSurvey(survey: TJsEnvironmentStateSurvey): void { + public setSurvey(survey: TEnvironmentStateSurvey): void { const prevSurvey = this.survey; - if (prevSurvey !== survey) { + if (prevSurvey?.id !== survey.id) { this.survey = survey; this.listeners.forEach((listener) => { listener(this.survey, prevSurvey); diff --git a/packages/react-native/src/lib/survey/tests/__mocks__/state.mock.ts b/packages/react-native/src/lib/survey/tests/__mocks__/state.mock.ts new file mode 100644 index 0000000000..8ce2a56637 --- /dev/null +++ b/packages/react-native/src/lib/survey/tests/__mocks__/state.mock.ts @@ -0,0 +1,8 @@ +export const mockSurveyId = "nrlq3epdlgc9zuccy8ngvgca"; +export const mockSingleUseId = "oh40c6hjlbm2lt5gn1bzkhnn"; +export const mockResponseId = "qru1wamotsu8reijra9haej8"; +export const mockUserId = "li7yxnzfgvlnvkp4q7434dpo"; +export const mockDisplayId = "raxvjd8rp35vi0pjt3tg45am"; +export const mockQuestionId = "a3815gq6cld6nrjcmp7keut4"; +export const mockQuestionId2 = "n0u9zu7m8nc3net6x5dpwdl8"; +export const mockSurveyId2 = "rdt38nqnff9xbyrfbtpesmkm"; diff --git a/packages/react-native/src/lib/survey/tests/__mocks__/store.mock.ts b/packages/react-native/src/lib/survey/tests/__mocks__/store.mock.ts new file mode 100644 index 0000000000..8b0aefcdb0 --- /dev/null +++ b/packages/react-native/src/lib/survey/tests/__mocks__/store.mock.ts @@ -0,0 +1,2 @@ +export const mockSurveyId = "jgocyoxk9uifo6u381qahmes"; +export const mockSurveyName = "Test Survey"; diff --git a/packages/react-native/src/lib/survey/tests/action.test.ts b/packages/react-native/src/lib/survey/tests/action.test.ts new file mode 100644 index 0000000000..76e93a36d8 --- /dev/null +++ b/packages/react-native/src/lib/survey/tests/action.test.ts @@ -0,0 +1,176 @@ +import { type Mock, beforeEach, describe, expect, test, vi } from "vitest"; +import { RNConfig } from "@/lib/common/config"; +import { Logger } from "@/lib/common/logger"; +import { shouldDisplayBasedOnPercentage } from "@/lib/common/utils"; +import { track, trackAction, triggerSurvey } from "@/lib/survey/action"; +import { SurveyStore } from "@/lib/survey/store"; +import { type TEnvironmentStateSurvey } from "@/types/config"; + +vi.mock("@/lib/common/config", () => ({ + RNConfig: { + getInstance: vi.fn(() => ({ + get: vi.fn(), + })), + }, +})); + +vi.mock("@/lib/survey/store", () => ({ + SurveyStore: { + getInstance: vi.fn(() => ({ + setSurvey: vi.fn(), + })), + }, +})); + +vi.mock("@/lib/common/logger", () => ({ + Logger: { + getInstance: vi.fn(() => { + return { + debug: vi.fn(), + }; + }), + }, +})); + +vi.mock("@/lib/common/utils", () => ({ + shouldDisplayBasedOnPercentage: vi.fn(), +})); + +describe("survey/action.ts", () => { + const mockSurvey = { + id: "survey_001", + name: "Test Survey", + displayPercentage: 50, + triggers: [ + { + actionClass: { name: "testAction" }, + }, + ], + }; + + const mockAppConfig = { + get: vi.fn(), + }; + + const mockSurveyStore = { + setSurvey: vi.fn(), + }; + + const mockLogger = { + debug: vi.fn(), + }; + + beforeEach(() => { + vi.clearAllMocks(); + + const getInstanceRn = vi.spyOn(RNConfig, "getInstance"); + const getInstanceSurveyStore = vi.spyOn(SurveyStore, "getInstance"); + const getInstanceLogger = vi.spyOn(Logger, "getInstance"); + + // Mock instances + getInstanceRn.mockReturnValue(mockAppConfig as unknown as RNConfig); + getInstanceSurveyStore.mockReturnValue(mockSurveyStore as unknown as SurveyStore); + getInstanceLogger.mockReturnValue(mockLogger as unknown as Logger); + }); + + describe("triggerSurvey", () => { + test("does not trigger survey if displayPercentage criteria is not met", () => { + const shouldDisplayBasedOnPercentageMock = vi.mocked(shouldDisplayBasedOnPercentage); + shouldDisplayBasedOnPercentageMock.mockReturnValueOnce(false); + + triggerSurvey(mockSurvey as unknown as TEnvironmentStateSurvey); + + // Ensure survey is not set + expect(mockSurveyStore.setSurvey).not.toHaveBeenCalled(); + expect(mockLogger.debug).toHaveBeenCalledWith( + 'Survey display of "Test Survey" skipped based on displayPercentage.' + ); + }); + + test("triggers survey if displayPercentage criteria is met", () => { + // Mock `shouldDisplayBasedOnPercentage` to return true + const shouldDisplayBasedOnPercentageMock = vi.mocked(shouldDisplayBasedOnPercentage); + shouldDisplayBasedOnPercentageMock.mockReturnValueOnce(true); + + triggerSurvey(mockSurvey as unknown as TEnvironmentStateSurvey); + + // Ensure survey is set + expect(mockSurveyStore.setSurvey).toHaveBeenCalledWith(mockSurvey); + }); + }); + + describe("trackAction", () => { + const mockActiveSurveys = [mockSurvey]; + + beforeEach(() => { + mockAppConfig.get.mockReturnValue({ + filteredSurveys: mockActiveSurveys, + }); + }); + + test("triggers survey associated with action name", () => { + (shouldDisplayBasedOnPercentage as unknown as Mock).mockReturnValue(true); + + trackAction("testAction"); + + // Ensure triggerSurvey is called for the matching survey + expect(mockSurveyStore.setSurvey).toHaveBeenCalledWith(mockSurvey); + }); + + test("does not trigger survey if no active surveys are found", () => { + mockAppConfig.get.mockReturnValue({ + filteredSurveys: [], + }); + + trackAction("testAction"); + + // Ensure no surveys are triggered + expect(mockSurveyStore.setSurvey).not.toHaveBeenCalled(); + expect(mockLogger.debug).toHaveBeenCalledWith("No active surveys to display"); + }); + + test("logs tracked action name", () => { + trackAction("testAction"); + + expect(mockLogger.debug).toHaveBeenCalledWith('Formbricks: Action "testAction" tracked'); + }); + }); + + describe("track", () => { + const mockActionClasses = [ + { + key: "testCode", + type: "code", + name: "testAction", + }, + ]; + + beforeEach(() => { + mockAppConfig.get.mockReturnValue({ + environment: { + data: { actionClasses: mockActionClasses }, + }, + }); + }); + + test("tracks a valid action by code", () => { + const result = track("testCode"); + + expect(result.ok).toBe(true); + // expect(mockLogger.debug).toHaveBeenCalledWith('Formbricks: Action "testAction" tracked'); + }); + + test("returns error for invalid action code", () => { + const result = track("invalidCode"); + + expect(result.ok).toBe(false); + + if (!result.ok) { + expect(result.error.code).toBe("invalid_code"); + expect(result.error.message).toBe( + "invalidCode action unknown. Please add this action in Formbricks first in order to use it in your code." + ); + } + }); + }); +}); diff --git a/packages/react-native/src/lib/survey/tests/state.test.ts b/packages/react-native/src/lib/survey/tests/state.test.ts new file mode 100644 index 0000000000..5619ef92fa --- /dev/null +++ b/packages/react-native/src/lib/survey/tests/state.test.ts @@ -0,0 +1,158 @@ +import { + mockDisplayId, + mockQuestionId, + mockQuestionId2, + mockResponseId, + mockSingleUseId, + mockSurveyId, + mockSurveyId2, + mockUserId, +} from "./__mocks__/state.mock"; +import { SurveyState } from "@/lib/survey/state"; +import { beforeEach, describe, expect, test } from "vitest"; + +describe("SurveyState", () => { + let surveyState: SurveyState; + + beforeEach(() => { + surveyState = new SurveyState(mockSurveyId); + }); + + describe("constructor", () => { + test("initializes with required surveyId", () => { + expect(surveyState.surveyId).toBe(mockSurveyId); + expect(surveyState.responseId).toBeNull(); + expect(surveyState.userId).toBeNull(); + expect(surveyState.singleUseId).toBeNull(); + }); + + test("initializes with all optional parameters", () => { + const state = new SurveyState(mockSurveyId, mockSingleUseId, mockResponseId, mockUserId); + expect(state.surveyId).toBe(mockSurveyId); + expect(state.singleUseId).toBe(mockSingleUseId); + expect(state.responseId).toBe(mockResponseId); + expect(state.userId).toBe(mockUserId); + }); + }); + + describe("setSurveyId", () => { + test("updates surveyId and clears state", () => { + // First set some data + surveyState.responseId = mockResponseId; + surveyState.responseAcc = { + finished: true, + data: { [mockQuestionId]: "test" }, + ttc: { [mockQuestionId]: 5000 }, + variables: {}, + }; + + // Then update survey ID + surveyState.setSurveyId(mockSurveyId2); + + expect(surveyState.surveyId).toBe(mockSurveyId2); + expect(surveyState.responseId).toBeNull(); + expect(surveyState.responseAcc).toEqual({ finished: false, data: {}, ttc: {}, variables: {} }); + }); + }); + + describe("copy", () => { + test("creates an exact copy of the state", () => { + surveyState.responseId = mockResponseId; + surveyState.userId = mockUserId; + surveyState.singleUseId = mockSingleUseId; + surveyState.responseAcc = { + finished: true, + data: { [mockQuestionId]: "answer1" }, + ttc: { [mockQuestionId]: 3000 }, + variables: { var1: "value1" }, + }; + + const copy = surveyState.copy(); + + expect(copy).toBeInstanceOf(SurveyState); + expect(copy).not.toBe(surveyState); // Different instance + expect(copy.surveyId).toBe(surveyState.surveyId); + expect(copy.responseId).toBe(surveyState.responseId); + expect(copy.userId).toBe(surveyState.userId); + expect(copy.singleUseId).toBe(surveyState.singleUseId); + expect(copy.responseAcc).toEqual(surveyState.responseAcc); + }); + }); + + describe("accumulateResponse", () => { + test("accumulates response data correctly", () => { + const firstUpdate = { + finished: false, + data: { [mockQuestionId]: "answer1" }, + ttc: { [mockQuestionId]: 3000 }, + variables: { var1: "value1" }, + displayId: mockDisplayId, + }; + + const secondUpdate = { + finished: true, + data: { [mockQuestionId2]: "answer2" }, + ttc: { [mockQuestionId2]: 2000 }, + variables: { var2: "value2" }, + displayId: mockDisplayId, + }; + + surveyState.accumulateResponse(firstUpdate); + expect(surveyState.responseAcc.data).toEqual({ [mockQuestionId]: "answer1" }); + expect(surveyState.responseAcc.ttc).toEqual({ [mockQuestionId]: 3000 }); + expect(surveyState.responseAcc.variables).toEqual({ var1: "value1" }); + + surveyState.accumulateResponse(secondUpdate); + expect(surveyState.responseAcc.data).toEqual({ + [mockQuestionId]: "answer1", + [mockQuestionId2]: "answer2", + }); + expect(surveyState.responseAcc.ttc).toEqual({ [mockQuestionId2]: 2000 }); + expect(surveyState.responseAcc.variables).toEqual({ var2: "value2" }); + expect(surveyState.responseAcc.finished).toBe(true); + }); + }); + + describe("state management methods", () => { + test("updateResponseId sets response ID", () => { + surveyState.updateResponseId(mockResponseId); + expect(surveyState.responseId).toBe(mockResponseId); + }); + + test("updateDisplayId sets display ID", () => { + surveyState.updateDisplayId(mockDisplayId); + expect(surveyState.displayId).toBe(mockDisplayId); + }); + + test("updateUserId sets user ID", () => { + surveyState.updateUserId(mockUserId); + expect(surveyState.userId).toBe(mockUserId); + }); + + test("isResponseFinished returns correct state", () => { + expect(surveyState.isResponseFinished()).toBe(false); + surveyState.responseAcc.finished = true; + expect(surveyState.isResponseFinished()).toBe(true); + }); + + test("clear resets response state", () => { + surveyState.responseId = mockResponseId; + surveyState.responseAcc = { + finished: true, + data: { [mockQuestionId]: "test" }, + ttc: { [mockQuestionId]: 5000 }, + variables: { var1: "test" }, + }; + + surveyState.clear(); + + expect(surveyState.responseId).toBeNull(); + expect(surveyState.responseAcc).toEqual({ + finished: false, + data: {}, + ttc: {}, + variables: {}, + }); + }); + }); +}); diff --git a/packages/react-native/src/lib/survey/tests/store.test.ts b/packages/react-native/src/lib/survey/tests/store.test.ts new file mode 100644 index 0000000000..b11f663817 --- /dev/null +++ b/packages/react-native/src/lib/survey/tests/store.test.ts @@ -0,0 +1,129 @@ +import { mockSurveyId, mockSurveyName } from "@/lib/survey/tests/__mocks__/store.mock"; +import { SurveyStore } from "@/lib/survey/store"; +import type { TEnvironmentStateSurvey } from "@/types/config"; +import { beforeEach, describe, expect, test, vi } from "vitest"; + +describe("SurveyStore", () => { + let store: SurveyStore; + + beforeEach(() => { + // Reset the singleton instance before each test + // @ts-expect-error accessing private static property + SurveyStore.instance = undefined; + store = SurveyStore.getInstance(); + }); + + describe("getInstance", () => { + test("returns singleton instance", () => { + const instance1 = SurveyStore.getInstance(); + const instance2 = SurveyStore.getInstance(); + expect(instance1).toBe(instance2); + }); + }); + + describe("getSurvey", () => { + test("returns null when no survey is set", () => { + expect(store.getSurvey()).toBeNull(); + }); + + test("returns current survey when set", () => { + const mockSurvey: TEnvironmentStateSurvey = { + id: mockSurveyId, + name: mockSurveyName, + } as TEnvironmentStateSurvey; + + store.setSurvey(mockSurvey); + expect(store.getSurvey()).toBe(mockSurvey); + }); + }); + + describe("setSurvey", () => { + test("updates survey and notifies listeners when survey changes", () => { + const listener = vi.fn(); + const mockSurvey: TEnvironmentStateSurvey = { + id: mockSurveyId, + name: mockSurveyName, + } as TEnvironmentStateSurvey; + + store.subscribe(listener); + store.setSurvey(mockSurvey); + + expect(listener).toHaveBeenCalledWith(mockSurvey, null); + expect(store.getSurvey()).toBe(mockSurvey); + }); + + test("does not notify listeners when setting same survey", () => { + const listener = vi.fn(); + const mockSurvey: TEnvironmentStateSurvey = { + id: mockSurveyId, + name: mockSurveyName, + } as TEnvironmentStateSurvey; + + store.setSurvey(mockSurvey); + store.subscribe(listener); + store.setSurvey(mockSurvey); + + expect(listener).not.toHaveBeenCalled(); + }); + }); + + describe("resetSurvey", () => { + test("resets survey to null and notifies listeners", () => { + const listener = vi.fn(); + const mockSurvey: TEnvironmentStateSurvey = { + id: mockSurveyId, + name: mockSurveyName, + } as TEnvironmentStateSurvey; + + store.setSurvey(mockSurvey); + store.subscribe(listener); + store.resetSurvey(); + + expect(listener).toHaveBeenCalledWith(null, mockSurvey); + expect(store.getSurvey()).toBeNull(); + }); + + test("does not notify listeners when already null", () => { + const listener = vi.fn(); + store.subscribe(listener); + store.resetSurvey(); + + expect(listener).not.toHaveBeenCalled(); + expect(store.getSurvey()).toBeNull(); + }); + }); + + describe("subscribe", () => { + test("adds listener and returns unsubscribe function", () => { + const listener = vi.fn(); + const mockSurvey: TEnvironmentStateSurvey = { + id: mockSurveyId, + name: mockSurveyName, + } as TEnvironmentStateSurvey; + + const unsubscribe = store.subscribe(listener); + store.setSurvey(mockSurvey); + expect(listener).toHaveBeenCalledTimes(1); + + unsubscribe(); + store.setSurvey({ ...mockSurvey, name: "Updated Survey" } as TEnvironmentStateSurvey); + expect(listener).toHaveBeenCalledTimes(1); // Still 1, not called after unsubscribe + }); + + test("multiple listeners receive updates", () => { + const listener1 = vi.fn(); + const listener2 = vi.fn(); + const mockSurvey: TEnvironmentStateSurvey = { + id: mockSurveyId, + name: mockSurveyName, + } as TEnvironmentStateSurvey; + + store.subscribe(listener1); + store.subscribe(listener2); + store.setSurvey(mockSurvey); + + expect(listener1).toHaveBeenCalledWith(mockSurvey, null); + expect(listener2).toHaveBeenCalledWith(mockSurvey, null); + }); + }); +}); diff --git a/packages/react-native/src/lib/user/attribute.ts b/packages/react-native/src/lib/user/attribute.ts new file mode 100644 index 0000000000..cdb3c67e2f --- /dev/null +++ b/packages/react-native/src/lib/user/attribute.ts @@ -0,0 +1,12 @@ +import { UpdateQueue } from "@/lib/user/update-queue"; +import { type NetworkError, type Result, okVoid } from "@/types/error"; + +export const setAttributes = async ( + attributes: Record + // eslint-disable-next-line @typescript-eslint/require-await -- we want to use promises here +): Promise> => { + const updateQueue = UpdateQueue.getInstance(); + updateQueue.updateAttributes(attributes); + void updateQueue.processUpdates(); + return okVoid(); +}; diff --git a/packages/react-native/src/lib/user/state.ts b/packages/react-native/src/lib/user/state.ts new file mode 100644 index 0000000000..99497f0ad2 --- /dev/null +++ b/packages/react-native/src/lib/user/state.ts @@ -0,0 +1,54 @@ +import { RNConfig } from "@/lib/common/config"; +import type { TUserState } from "@/types/config"; + +let userStateSyncIntervalId: number | null = null; + +export const DEFAULT_USER_STATE_NO_USER_ID: TUserState = { + expiresAt: null, + data: { + userId: null, + segments: [], + displays: [], + responses: [], + lastDisplayAt: null, + }, +} as const; + +/** + * Add a listener to check if the user state has expired with a certain interval + */ +export const addUserStateExpiryCheckListener = (): void => { + const config = RNConfig.getInstance(); + const updateInterval = 1000 * 60; // every 60 seconds + + if (userStateSyncIntervalId === null) { + const intervalHandler = (): void => { + const userId = config.get().user.data.userId; + + if (!userId) { + return; + } + + // extend the personState validity by 30 minutes: + config.update({ + ...config.get(), + user: { + ...config.get().user, + expiresAt: new Date(new Date().getTime() + 1000 * 60 * 30), // 30 minutes + }, + }); + }; + + userStateSyncIntervalId = setInterval(intervalHandler, updateInterval) as unknown as number; + } +}; + +/** + * Clear the person state expiry check listener + */ +export const clearUserStateExpiryCheckListener = (): void => { + if (userStateSyncIntervalId) { + clearInterval(userStateSyncIntervalId); + userStateSyncIntervalId = null; + } +}; diff --git a/packages/react-native/src/lib/user/tests/__mocks__/update-queue.mock.ts b/packages/react-native/src/lib/user/tests/__mocks__/update-queue.mock.ts new file mode 100644 index 0000000000..0402553306 --- /dev/null +++ b/packages/react-native/src/lib/user/tests/__mocks__/update-queue.mock.ts @@ -0,0 +1,6 @@ +export const mockUserId1 = "user_123"; +export const mockUserId2 = "user_456"; +export const mockAttributes = { + name: "John Doe", + email: "john@example.com", +}; diff --git a/packages/react-native/src/lib/user/tests/__mocks__/update.mock.ts b/packages/react-native/src/lib/user/tests/__mocks__/update.mock.ts new file mode 100644 index 0000000000..32c2d6452d --- /dev/null +++ b/packages/react-native/src/lib/user/tests/__mocks__/update.mock.ts @@ -0,0 +1,7 @@ +export const mockUserId = "user_123"; +export const mockEnvironmentId = "ew9ba7urnv7u3eo11k5c1z0r"; +export const mockAppUrl = "https://app.formbricks.com"; +export const mockAttributes = { + name: "John Doe", + email: "john@example.com", +}; diff --git a/packages/react-native/src/lib/user/tests/attribute.test.ts b/packages/react-native/src/lib/user/tests/attribute.test.ts new file mode 100644 index 0000000000..b827c71803 --- /dev/null +++ b/packages/react-native/src/lib/user/tests/attribute.test.ts @@ -0,0 +1,76 @@ +import { beforeEach, describe, expect, test, vi } from "vitest"; +import { setAttributes } from "@/lib/user/attribute"; +import { UpdateQueue } from "@/lib/user/update-queue"; + +export const mockAttributes = { + name: "John Doe", + email: "john@example.com", +}; + +// Mock the UpdateQueue +vi.mock("@/lib/user/update-queue", () => ({ + UpdateQueue: { + getInstance: vi.fn(() => ({ + updateAttributes: vi.fn(), + processUpdates: vi.fn(), + })), + }, +})); + +describe("User Attributes", () => { + const mockUpdateQueue = { + updateAttributes: vi.fn(), + processUpdates: vi.fn(), + }; + + beforeEach(() => { + vi.clearAllMocks(); + + const getInstanceUpdateQueue = vi.spyOn(UpdateQueue, "getInstance"); + getInstanceUpdateQueue.mockReturnValue(mockUpdateQueue as unknown as UpdateQueue); + }); + + describe("setAttributes", () => { + test("successfully updates attributes and triggers processing", async () => { + const result = await setAttributes(mockAttributes); + + // Verify UpdateQueue methods were called correctly + expect(mockUpdateQueue.updateAttributes).toHaveBeenCalledWith(mockAttributes); + expect(mockUpdateQueue.processUpdates).toHaveBeenCalled(); + + // Verify result is ok + expect(result.ok).toBe(true); + }); + + test("processes multiple attribute updates", async () => { + const firstAttributes = { name: mockAttributes.name }; + const secondAttributes = { email: mockAttributes.email }; + + await setAttributes(firstAttributes); + await setAttributes(secondAttributes); + + expect(mockUpdateQueue.updateAttributes).toHaveBeenCalledTimes(2); + expect(mockUpdateQueue.updateAttributes).toHaveBeenNthCalledWith(1, firstAttributes); + expect(mockUpdateQueue.updateAttributes).toHaveBeenNthCalledWith(2, secondAttributes); + expect(mockUpdateQueue.processUpdates).toHaveBeenCalledTimes(2); + }); + + test("processes updates asynchronously", async () => { + const attributes = { name: mockAttributes.name }; + + // Mock processUpdates to be async + mockUpdateQueue.processUpdates.mockImplementation( + () => + new Promise((resolve) => { + setTimeout(resolve, 100); + }) + ); + + const result = await setAttributes(attributes); + + expect(result.ok).toBe(true); + expect(mockUpdateQueue.processUpdates).toHaveBeenCalled(); + // The function returns before processUpdates completes due to void operator + }); + }); +}); diff --git a/packages/react-native/src/lib/user/tests/state.test.ts b/packages/react-native/src/lib/user/tests/state.test.ts new file mode 100644 index 0000000000..decc0d3adf --- /dev/null +++ b/packages/react-native/src/lib/user/tests/state.test.ts @@ -0,0 +1,103 @@ +import { type MockInstance, afterEach, beforeEach, describe, expect, test, vi } from "vitest"; +import { RNConfig } from "@/lib/common/config"; +import { addUserStateExpiryCheckListener, clearUserStateExpiryCheckListener } from "@/lib/user/state"; + +const mockUserId = "user_123"; + +vi.mock("@/lib/common/config", () => ({ + RNConfig: { + getInstance: vi.fn(() => ({ + get: vi.fn(), + update: vi.fn(), + })), + }, +})); + +describe("User State Expiry Check Listener", () => { + let mockRNConfig: MockInstance<() => RNConfig>; + + beforeEach(() => { + vi.clearAllMocks(); + vi.useFakeTimers(); // Simulate timers + + mockRNConfig = vi.spyOn(RNConfig, "getInstance"); + }); + + afterEach(() => { + clearUserStateExpiryCheckListener(); // Ensure cleanup after each test + }); + + test("should set an interval if not already set and update user state expiry when userId exists", () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + user: { data: { userId: mockUserId } }, + }), + update: vi.fn(), + }; + + mockRNConfig.mockReturnValue(mockConfig as unknown as RNConfig); + + addUserStateExpiryCheckListener(); + + // Fast-forward time by 1 minute (60,000 ms) + vi.advanceTimersByTime(60_000); + + // Ensure config.update was called with extended expiry time + expect(mockConfig.update).toHaveBeenCalledWith({ + user: { + data: { userId: mockUserId }, + expiresAt: expect.any(Date) as Date, + }, + }); + }); + + test("should not update user state expiry if userId does not exist", () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + user: { data: { userId: null } }, + }), + update: vi.fn(), + }; + + mockRNConfig.mockReturnValue(mockConfig as unknown as RNConfig); + + addUserStateExpiryCheckListener(); + vi.advanceTimersByTime(60_000); // Fast-forward 1 minute + + expect(mockConfig.update).not.toHaveBeenCalled(); // Ensures no update when no userId + }); + + test("should not set multiple intervals if already set", () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + user: { data: { userId: mockUserId } }, + }), + update: vi.fn(), + }; + + mockRNConfig.mockReturnValue(mockConfig as unknown as RNConfig); + + addUserStateExpiryCheckListener(); + addUserStateExpiryCheckListener(); // Call again to check if it prevents multiple intervals + + vi.advanceTimersByTime(60_000); // Fast-forward 1 minute + + expect(mockConfig.update).toHaveBeenCalledTimes(1); + }); + + test("should clear interval when clearUserStateExpiryCheckListener is called", () => { + const mockConfig = { + get: vi.fn(), + update: vi.fn(), + }; + + mockRNConfig.mockReturnValue(mockConfig as unknown as RNConfig); + + addUserStateExpiryCheckListener(); + clearUserStateExpiryCheckListener(); + + vi.advanceTimersByTime(60_000); // Fast-forward 1 minute + + expect(mockConfig.update).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/react-native/src/lib/user/tests/update-queue.test.ts b/packages/react-native/src/lib/user/tests/update-queue.test.ts new file mode 100644 index 0000000000..8dfe6742e2 --- /dev/null +++ b/packages/react-native/src/lib/user/tests/update-queue.test.ts @@ -0,0 +1,161 @@ +import { type Mock, beforeEach, describe, expect, test, vi } from "vitest"; +import { mockAttributes, mockUserId1, mockUserId2 } from "@/lib/user/tests/__mocks__/update-queue.mock"; +import { RNConfig } from "@/lib/common/config"; +import { sendUpdates } from "@/lib/user/update"; +import { UpdateQueue } from "@/lib/user/update-queue"; + +// Mock dependencies +vi.mock("@/lib/common/config", () => ({ + RNConfig: { + getInstance: vi.fn(() => ({ + get: vi.fn(() => ({ + user: { + data: { + userId: "mock-user-id", + }, + }, + })), + update: vi.fn(), + })), + }, +})); + +vi.mock("@/lib/common/logger", () => ({ + Logger: { + getInstance: vi.fn(() => ({ + debug: vi.fn(), + error: vi.fn(), + })), + }, +})); + +vi.mock("@/lib/user/update", () => ({ + sendUpdates: vi.fn(), +})); + +describe("UpdateQueue", () => { + let updateQueue: UpdateQueue; + + beforeEach(() => { + vi.clearAllMocks(); + // Reset singleton instance + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- accessing private static property + (UpdateQueue as any).instance = null; + updateQueue = UpdateQueue.getInstance(); + }); + + test("getInstance returns singleton instance", () => { + const instance1 = UpdateQueue.getInstance(); + const instance2 = UpdateQueue.getInstance(); + expect(instance1).toBe(instance2); + }); + + test("updateUserId sets userId correctly when updates is null", () => { + const userId = mockUserId1; + updateQueue.updateUserId(userId); + expect(updateQueue.getUpdates()).toEqual({ + userId, + attributes: {}, + }); + }); + + test("updateUserId updates existing userId correctly", () => { + const userId1 = mockUserId1; + const userId2 = mockUserId2; + + updateQueue.updateUserId(userId1); + updateQueue.updateUserId(userId2); + + expect(updateQueue.getUpdates()).toEqual({ + userId: userId2, + attributes: {}, + }); + }); + + test("updateAttributes sets attributes correctly when updates is null", () => { + const attributes = mockAttributes; + updateQueue.updateAttributes(attributes); + + expect(updateQueue.getUpdates()).toEqual({ + userId: "mock-user-id", // from mocked config + attributes, + }); + }); + + test("updateAttributes merges with existing attributes", () => { + updateQueue.updateAttributes({ name: mockAttributes.name }); + updateQueue.updateAttributes({ email: mockAttributes.email }); + + expect(updateQueue.getUpdates()).toEqual({ + userId: "mock-user-id", + attributes: { + name: mockAttributes.name, + email: mockAttributes.email, + }, + }); + }); + + test("clearUpdates resets updates to null", () => { + updateQueue.updateAttributes({ name: mockAttributes.name }); + updateQueue.clearUpdates(); + expect(updateQueue.getUpdates()).toBeNull(); + }); + + test("isEmpty returns true when updates is null", () => { + expect(updateQueue.isEmpty()).toBe(true); + }); + + test("isEmpty returns false when updates exist", () => { + updateQueue.updateAttributes({ name: mockAttributes.name }); + expect(updateQueue.isEmpty()).toBe(false); + }); + + test("processUpdates debounces multiple calls", async () => { + // Call processUpdates multiple times in quick succession + + (sendUpdates as Mock).mockReturnValue({ + ok: true, + }); + + updateQueue.updateAttributes({ name: mockAttributes.name }); + updateQueue.updateAttributes({ email: mockAttributes.email }); + + // Wait for debounce timeout + await new Promise((resolve) => { + setTimeout(resolve, 600); + }); + + await updateQueue.processUpdates(); + + // Should only be called once with the merged updates + expect(sendUpdates).toHaveBeenCalledTimes(1); + }); + + test("processUpdates handles language attribute specially when no userId", async () => { + const configUpdateMock = vi.fn(); + (RNConfig.getInstance as Mock).mockImplementation(() => ({ + get: vi.fn(() => ({ + user: { data: { userId: "" } }, + })), + update: configUpdateMock, + })); + + updateQueue.updateAttributes({ language: "en" }); + await updateQueue.processUpdates(); + + expect(configUpdateMock).toHaveBeenCalled(); + }); + + test("processUpdates throws error when setting attributes without userId", async () => { + (RNConfig.getInstance as Mock).mockImplementation(() => ({ + get: vi.fn(() => ({ + user: { data: { userId: "" } }, + })), + })); + + updateQueue.updateAttributes({ name: mockAttributes.name }); + await expect(updateQueue.processUpdates()).rejects.toThrow( + "Formbricks can't set attributes without a userId!" + ); + }); +}); diff --git a/packages/react-native/src/lib/user/tests/update.test.ts b/packages/react-native/src/lib/user/tests/update.test.ts new file mode 100644 index 0000000000..14c42f1f60 --- /dev/null +++ b/packages/react-native/src/lib/user/tests/update.test.ts @@ -0,0 +1,221 @@ +import { type Mock, beforeEach, describe, expect, test, vi } from "vitest"; +import { FormbricksAPI } from "@formbricks/api"; +import { + mockAppUrl, + mockAttributes, + mockEnvironmentId, + mockUserId, +} from "@/lib/user/tests/__mocks__/update.mock"; +import { RNConfig } from "@/lib/common/config"; +import { Logger } from "@/lib/common/logger"; +import { sendUpdates, sendUpdatesToBackend } from "@/lib/user/update"; +import { type TUpdates } from "@/types/config"; + +vi.mock("@/lib/common/config", () => ({ + RNConfig: { + getInstance: vi.fn(() => ({ + get: vi.fn(), + update: vi.fn(), + })), + }, +})); + +vi.mock("@/lib/common/logger", () => ({ + Logger: { + getInstance: vi.fn(() => ({ + debug: vi.fn(), + })), + }, +})); + +vi.mock("@/lib/common/utils", () => ({ + filterSurveys: vi.fn(), +})); + +vi.mock("@formbricks/api", () => ({ + FormbricksAPI: vi.fn().mockImplementation(() => ({ + client: { + user: { + createOrUpdate: vi.fn(), + }, + }, + })), +})); + +describe("sendUpdatesToBackend", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + test("sends user updates to backend and returns updated state", async () => { + const mockResponse = { + ok: true, + data: { + state: { + data: { + userId: mockUserId, + attributes: mockAttributes, + }, + }, + }, + }; + + (FormbricksAPI as Mock).mockImplementation(() => ({ + client: { + user: { + createOrUpdate: vi.fn().mockResolvedValue(mockResponse), + }, + }, + })); + + const result = await sendUpdatesToBackend({ + appUrl: mockAppUrl, + environmentId: mockEnvironmentId, + updates: { userId: mockUserId, attributes: mockAttributes }, + }); + + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.data.state.data).toEqual({ userId: mockUserId, attributes: mockAttributes }); + } + }); + + test("returns network error if API call fails", async () => { + const mockUpdates: TUpdates = { userId: mockUserId, attributes: mockAttributes }; + + (FormbricksAPI as Mock).mockImplementation(() => ({ + client: { + user: { + createOrUpdate: vi.fn().mockResolvedValue({ + ok: false, + error: { code: "network_error", message: "Request failed", status: 500 }, + }), + }, + }, + })); + + const result = await sendUpdatesToBackend({ + appUrl: mockAppUrl, + environmentId: mockEnvironmentId, + updates: mockUpdates, + }); + + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe("network_error"); + expect(result.error.message).toBe("Error updating user with userId user_123"); + } + }); + + test("throws error if network request fails", async () => { + const mockUpdates: TUpdates = { userId: mockUserId, attributes: { plan: "premium" } }; + + (FormbricksAPI as Mock).mockImplementation(() => ({ + client: { + user: { + createOrUpdate: vi.fn().mockRejectedValue(new Error("Network error")), + }, + }, + })); + + await expect( + sendUpdatesToBackend({ + appUrl: mockAppUrl, + environmentId: mockEnvironmentId, + updates: mockUpdates, + }) + ).rejects.toThrow("Network error"); + }); +}); + +describe("sendUpdates", () => { + beforeEach(() => { + (RNConfig.getInstance as Mock).mockImplementation(() => ({ + get: vi.fn().mockReturnValue({ + appUrl: mockAppUrl, + environmentId: mockEnvironmentId, + environment: { + data: { + surveys: [], + }, + }, + }), + update: vi.fn(), + })); + + (Logger.getInstance as Mock).mockImplementation(() => ({ + debug: vi.fn(), + })); + }); + + test("successfully processes updates", async () => { + const mockResponse = { + ok: true, + data: { + state: { + data: { + userId: mockUserId, + attributes: mockAttributes, + }, + expiresAt: new Date(Date.now() + 1000 * 60 * 30), + }, + }, + }; + + (FormbricksAPI as Mock).mockImplementation(() => ({ + client: { + user: { + createOrUpdate: vi.fn().mockResolvedValue(mockResponse), + }, + }, + })); + + const result = await sendUpdates({ updates: { userId: mockUserId, attributes: mockAttributes } }); + + expect(result.ok).toBe(true); + }); + + test("handles backend errors", async () => { + const mockErrorResponse = { + ok: false, + error: { + code: "invalid_request", + status: 400, + message: "Invalid request", + }, + }; + + (FormbricksAPI as Mock).mockImplementation(() => ({ + client: { + user: { + createOrUpdate: vi.fn().mockResolvedValue(mockErrorResponse), + }, + }, + })); + + const result = await sendUpdates({ updates: { userId: mockUserId, attributes: mockAttributes } }); + + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe("invalid_request"); + } + }); + + test("handles unexpected errors", async () => { + (FormbricksAPI as Mock).mockImplementation(() => ({ + client: { + user: { + createOrUpdate: vi.fn().mockRejectedValue(new Error("Unexpected error")), + }, + }, + })); + + const result = await sendUpdates({ updates: { userId: mockUserId, attributes: mockAttributes } }); + + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe("network_error"); + expect(result.error.status).toBe(500); + } + }); +}); diff --git a/packages/react-native/src/lib/user/tests/user.test.ts b/packages/react-native/src/lib/user/tests/user.test.ts new file mode 100644 index 0000000000..095cfae64b --- /dev/null +++ b/packages/react-native/src/lib/user/tests/user.test.ts @@ -0,0 +1,176 @@ +import { type Mock, type MockInstance, beforeEach, describe, expect, test, vi } from "vitest"; +import { RNConfig } from "@/lib/common/config"; +import { deinitalize, init } from "@/lib/common/initialize"; +import { Logger } from "@/lib/common/logger"; +import { UpdateQueue } from "@/lib/user/update-queue"; +import { logout, logoutUser, setUserId } from "@/lib/user/user"; + +// Mock dependencies +vi.mock("@/lib/common/config", () => ({ + RNConfig: { + getInstance: vi.fn(() => ({ + get: vi.fn(), + })), + }, +})); + +vi.mock("@/lib/common/logger", () => ({ + Logger: { + getInstance: vi.fn(() => ({ + error: vi.fn(), + debug: vi.fn(), + })), + }, +})); + +vi.mock("@/lib/user/update-queue", () => ({ + UpdateQueue: { + getInstance: vi.fn(() => ({ + updateUserId: vi.fn(), + processUpdates: vi.fn(), + })), + }, +})); + +vi.mock("@/lib/common/initialize", () => ({ + deinitalize: vi.fn(), + init: vi.fn(), +})); + +describe("user.ts", () => { + const mockUserId = "test-user-123"; + const mockEnvironmentId = "env-123"; + const mockAppUrl = "https://test.com"; + + let getInstanceConfigMock: MockInstance<() => RNConfig>; + let getInstanceLoggerMock: MockInstance<() => Logger>; + let getInstanceUpdateQueueMock: MockInstance<() => UpdateQueue>; + + beforeEach(() => { + vi.clearAllMocks(); + getInstanceConfigMock = vi.spyOn(RNConfig, "getInstance"); + getInstanceLoggerMock = vi.spyOn(Logger, "getInstance"); + getInstanceUpdateQueueMock = vi.spyOn(UpdateQueue, "getInstance"); + }); + + describe("setUserId", () => { + test("returns error if userId is already set", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + user: { + data: { + userId: "existing-user", + }, + }, + }), + }; + + const mockLogger = { + debug: vi.fn(), + error: vi.fn(), + }; + + getInstanceConfigMock.mockReturnValue(mockConfig as unknown as RNConfig); + getInstanceLoggerMock.mockReturnValue(mockLogger as unknown as Logger); + + const result = await setUserId(mockUserId); + + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe("forbidden"); + expect(result.error.status).toBe(403); + } + expect(mockLogger.error).toHaveBeenCalled(); + }); + + test("successfully sets userId when none exists", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + user: { + data: { + userId: null, + }, + }, + }), + }; + + const mockLogger = { + debug: vi.fn(), + error: vi.fn(), + }; + + const mockUpdateQueue = { + updateUserId: vi.fn(), + processUpdates: vi.fn(), + }; + + getInstanceConfigMock.mockReturnValue(mockConfig as unknown as RNConfig); + getInstanceLoggerMock.mockReturnValue(mockLogger as unknown as Logger); + getInstanceUpdateQueueMock.mockReturnValue(mockUpdateQueue as unknown as UpdateQueue); + const result = await setUserId(mockUserId); + + expect(result.ok).toBe(true); + expect(mockUpdateQueue.updateUserId).toHaveBeenCalledWith(mockUserId); + expect(mockUpdateQueue.processUpdates).toHaveBeenCalled(); + }); + }); + + describe("logoutUser", () => { + test("calls deinitalize", async () => { + await logoutUser(); + expect(deinitalize).toHaveBeenCalled(); + }); + }); + + describe("logout", () => { + test("successfully reinitializes after logout", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + environmentId: mockEnvironmentId, + appUrl: mockAppUrl, + user: { data: { userId: mockUserId } }, + }), + }; + + getInstanceConfigMock.mockReturnValue(mockConfig as unknown as RNConfig); + + (init as Mock).mockResolvedValue(undefined); + + const result = await logout(); + + expect(deinitalize).toHaveBeenCalled(); + expect(init).toHaveBeenCalledWith({ + environmentId: mockEnvironmentId, + appUrl: mockAppUrl, + }); + expect(result.ok).toBe(true); + }); + + test("returns error if initialization fails", async () => { + const mockConfig = { + get: vi.fn().mockReturnValue({ + environmentId: mockEnvironmentId, + appUrl: mockAppUrl, + user: { data: { userId: mockUserId } }, + }), + }; + + getInstanceConfigMock.mockReturnValue(mockConfig as unknown as RNConfig); + + const mockError = { code: "network_error", message: "Failed to connect" }; + (init as Mock).mockRejectedValue(mockError); + + const result = await logout(); + + expect(deinitalize).toHaveBeenCalled(); + expect(init).toHaveBeenCalledWith({ + environmentId: mockEnvironmentId, + appUrl: mockAppUrl, + }); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error).toEqual(mockError); + } + }); + }); +}); diff --git a/packages/react-native/src/lib/user/update-queue.ts b/packages/react-native/src/lib/user/update-queue.ts new file mode 100644 index 0000000000..9edab77ef0 --- /dev/null +++ b/packages/react-native/src/lib/user/update-queue.ts @@ -0,0 +1,153 @@ +/* eslint-disable @typescript-eslint/no-empty-function -- required for singleton pattern */ +import { RNConfig } from "@/lib/common/config"; +import { Logger } from "@/lib/common/logger"; +import { sendUpdates } from "@/lib/user/update"; +import type { TAttributes, TUpdates } from "@/types/config"; + +const logger = Logger.getInstance(); + +export class UpdateQueue { + private static instance: UpdateQueue | null = null; + private updates: TUpdates | null = null; + private debounceTimeout: NodeJS.Timeout | null = null; + private readonly DEBOUNCE_DELAY = 500; + + private constructor() {} + + public static getInstance(): UpdateQueue { + if (!UpdateQueue.instance) { + UpdateQueue.instance = new UpdateQueue(); + } + + return UpdateQueue.instance; + } + + public updateUserId(userId: string): void { + if (!this.updates) { + this.updates = { + userId, + attributes: {}, + }; + } else { + this.updates = { + ...this.updates, + userId, + }; + } + } + + public updateAttributes(attributes: TAttributes): void { + const config = RNConfig.getInstance(); + // Get userId from updates first, then fallback to config + const userId = this.updates?.userId ?? config.get().user.data.userId ?? ""; + + if (!this.updates) { + this.updates = { + userId, + attributes, + }; + } else { + this.updates = { + ...this.updates, + userId, + attributes: { ...this.updates.attributes, ...attributes }, + }; + } + } + + public getUpdates(): TUpdates | null { + return this.updates; + } + + public clearUpdates(): void { + this.updates = null; + } + + public isEmpty(): boolean { + return !this.updates; + } + + public async processUpdates(): Promise { + if (!this.updates) { + return; + } + + if (this.debounceTimeout) { + clearTimeout(this.debounceTimeout); + } + + return new Promise((resolve, reject) => { + const handler = async (): Promise => { + try { + let currentUpdates = { ...this.updates }; + const config = RNConfig.getInstance(); + + if (Object.keys(currentUpdates).length > 0) { + // Get userId from either updates or config + const effectiveUserId = currentUpdates.userId ?? config.get().user.data.userId; + const isLanguageInUpdates = currentUpdates.attributes?.language; + + if (!effectiveUserId && isLanguageInUpdates) { + // no user id set but the updates contain a language + // we need to set this language in the local config: + config.update({ + ...config.get(), + user: { + ...config.get().user, + data: { + ...config.get().user.data, + language: currentUpdates.attributes?.language, + }, + }, + }); + + logger.debug("Updated language successfully"); + + const { language: _, ...remainingAttributes } = currentUpdates.attributes ?? {}; + + // remove language from attributes + currentUpdates = { + ...currentUpdates, + attributes: remainingAttributes, + }; + } + + if (Object.keys(currentUpdates.attributes ?? {}).length > 0 && !effectiveUserId) { + const errorMessage = + "Formbricks can't set attributes without a userId! Please set a userId first with the setUserId function"; + logger.error(errorMessage); + this.clearUpdates(); + throw new Error(errorMessage); + } + + // Only send updates if we have a userId (either from updates or local storage) + if (effectiveUserId) { + const result = await sendUpdates({ + updates: { + userId: effectiveUserId, + attributes: currentUpdates.attributes ?? {}, + }, + }); + + if (result.ok) { + logger.debug("Updates sent successfully"); + } else { + logger.error("Failed to send updates"); + } + } + } + + this.clearUpdates(); + resolve(); + } catch (error: unknown) { + logger.error( + `Failed to process updates: ${error instanceof Error ? error.message : "Unknown error"}` + ); + reject(error as Error); + } + }; + + this.debounceTimeout = setTimeout(() => void handler(), this.DEBOUNCE_DELAY); + }); + } +} diff --git a/packages/react-native/src/lib/user/update.ts b/packages/react-native/src/lib/user/update.ts new file mode 100644 index 0000000000..2237a945e0 --- /dev/null +++ b/packages/react-native/src/lib/user/update.ts @@ -0,0 +1,114 @@ +/* eslint-disable no-console -- required for logging errors */ +import { FormbricksAPI } from "@formbricks/api"; +import { RNConfig } from "@/lib/common/config"; +import { Logger } from "@/lib/common/logger"; +import { filterSurveys } from "@/lib/common/utils"; +import { type TUpdates, type TUserState } from "@/types/config"; +import { type ApiErrorResponse, type Result, err, ok, okVoid } from "@/types/error"; + +export const sendUpdatesToBackend = async ({ + appUrl, + environmentId, + updates, +}: { + appUrl: string; + environmentId: string; + updates: TUpdates; +}): Promise< + Result< + { + state: TUserState; + messages?: string[]; + }, + ApiErrorResponse + > +> => { + const url = `${appUrl}/api/v1/client/${environmentId}/user`; + const api = new FormbricksAPI({ apiHost: appUrl, environmentId }); + + try { + const response = await api.client.user.createOrUpdate({ + userId: updates.userId, + attributes: updates.attributes, + }); + + if (!response.ok) { + return err({ + code: response.error.code, + status: response.error.status, + message: `Error updating user with userId ${updates.userId}`, + url: new URL(url), + responseMessage: response.error.message, + }); + } + + return ok(response.data); + } catch (e: unknown) { + const errorTyped = e as { message?: string }; + + const error = err({ + code: "network_error", + message: errorTyped.message ?? "Error fetching the person state", + status: 500, + url: new URL(url), + responseMessage: errorTyped.message ?? "Unknown error", + }); + + // eslint-disable-next-line @typescript-eslint/only-throw-error -- error.error is an Error object + throw error.error; + } +}; + +export const sendUpdates = async ({ + updates, +}: { + updates: TUpdates; +}): Promise> => { + const config = RNConfig.getInstance(); + const logger = Logger.getInstance(); + + const { appUrl, environmentId } = config.get(); + // update endpoint call + const url = `${appUrl}/api/v1/client/${environmentId}/user`; + + try { + const updatesResponse = await sendUpdatesToBackend({ appUrl, environmentId, updates }); + + if (updatesResponse.ok) { + const userState = updatesResponse.data.state; + const filteredSurveys = filterSurveys(config.get().environment, userState); + + // messages => string[] - contains the details of the attributes update + // for example, if the attribute "email" was being used for some user or not + const messages = updatesResponse.data.messages; + + if (messages && messages.length > 0) { + for (const message of messages) { + logger.debug(`User update message: ${message}`); + } + } + + config.update({ + ...config.get(), + user: { + ...userState, + }, + filteredSurveys, + }); + + return okVoid(); + } + + return err(updatesResponse.error); + } catch (e) { + console.error("error in sending updates: ", e); + + return err({ + code: "network_error", + message: "Error sending updates", + status: 500, + url: new URL(url), + responseMessage: "Unknown error", + }); + } +}; diff --git a/packages/react-native/src/lib/user/user.ts b/packages/react-native/src/lib/user/user.ts new file mode 100644 index 0000000000..d5cbb43038 --- /dev/null +++ b/packages/react-native/src/lib/user/user.ts @@ -0,0 +1,63 @@ +import { RNConfig } from "@/lib/common/config"; +import { deinitalize, init } from "@/lib/common/initialize"; +import { Logger } from "@/lib/common/logger"; +import { UpdateQueue } from "@/lib/user/update-queue"; +import { type ApiErrorResponse, type NetworkError, type Result, err, okVoid } from "@/types/error"; + +// eslint-disable-next-line @typescript-eslint/require-await -- we want to use promises here +export const setUserId = async (userId: string): Promise> => { + const appConfig = RNConfig.getInstance(); + const logger = Logger.getInstance(); + const updateQueue = UpdateQueue.getInstance(); + + const { + data: { userId: currentUserId }, + } = appConfig.get().user; + + if (currentUserId) { + logger.error( + "A userId is already set in formbricks, please first call the logout function and then set a new userId" + ); + return err({ + code: "forbidden", + message: "User already set", + responseMessage: "User already set", + status: 403, + }); + } + + updateQueue.updateUserId(userId); + void updateQueue.processUpdates(); + return okVoid(); +}; + +export const logoutUser = async (): Promise => { + await deinitalize(); +}; + +export const logout = async (): Promise> => { + const logger = Logger.getInstance(); + const appConfig = RNConfig.getInstance(); + + const { userId } = appConfig.get().user.data; + + if (!userId) { + logger.debug("No userId is set, please use the setUserId function to set a userId first"); + return okVoid(); + } + + logger.debug("Resetting state & getting new state from backend"); + const initParams = { + environmentId: appConfig.get().environmentId, + appUrl: appConfig.get().appUrl, + }; + + void logoutUser(); + + try { + await init(initParams); + return okVoid(); + } catch (e) { + return err(e as NetworkError); + } +}; diff --git a/packages/react-native/src/types/config.ts b/packages/react-native/src/types/config.ts new file mode 100644 index 0000000000..ddb6826cb2 --- /dev/null +++ b/packages/react-native/src/types/config.ts @@ -0,0 +1,154 @@ +/* eslint-disable import/no-extraneous-dependencies -- required for Prisma types */ +import type { ActionClass, Language, Project, Segment, Survey, SurveyLanguage } from "@prisma/client"; +import { z } from "zod"; +import { type TResponseUpdate, ZResponseUpdate } from "@/types/response"; +import { type TFileUploadParams, ZFileUploadParams } from "@/types/storage"; + +export type TEnvironmentStateSurvey = Pick< + Survey, + | "id" + | "name" + | "welcomeCard" + | "questions" + | "variables" + | "type" + | "showLanguageSwitch" + | "endings" + | "autoClose" + | "status" + | "recontactDays" + | "displayLimit" + | "displayOption" + | "hiddenFields" + | "delay" + | "projectOverwrites" +> & { + languages: (SurveyLanguage & { language: Language })[]; + triggers: { actionClass: ActionClass }[]; + segment?: Segment; + displayPercentage: number; + type: "link" | "app"; + styling?: TSurveyStyling; +}; + +export type TEnvironmentStateProject = Pick< + Project, + "id" | "recontactDays" | "clickOutsideClose" | "darkOverlay" | "placement" | "inAppSurveyBranding" +> & { + styling: TProjectStyling; +}; + +export type TEnvironmentStateActionClass = Pick; + +export interface TEnvironmentState { + expiresAt: Date; + data: { + surveys: TEnvironmentStateSurvey[]; + actionClasses: TEnvironmentStateActionClass[]; + project: TEnvironmentStateProject; + }; +} + +export interface TUserState { + expiresAt: Date | null; + data: { + userId: string | null; + segments: string[]; + displays: { surveyId: string; createdAt: Date }[]; + responses: string[]; + lastDisplayAt: Date | null; + language?: string; + }; +} + +export interface TConfig { + environmentId: string; + appUrl: string; + environment: TEnvironmentState; + user: TUserState; + filteredSurveys: TEnvironmentStateSurvey[]; + status: { + value: "success" | "error"; + expiresAt: Date | null; + }; +} + +export type TConfigUpdateInput = Omit & { + status?: { + value: "success" | "error"; + expiresAt: Date | null; + }; +}; + +export type TAttributes = Record; + +export interface TConfigInput { + environmentId: string; + appUrl: string; +} + +export interface TStylingColor { + light: string; + dark?: string | null | undefined; +} + +export interface TBaseStyling { + brandColor?: TStylingColor | null; + questionColor?: TStylingColor | null; + inputColor?: TStylingColor | null; + inputBorderColor?: TStylingColor | null; + cardBackgroundColor?: TStylingColor | null; + cardBorderColor?: TStylingColor | null; + cardShadowColor?: TStylingColor | null; + highlightBorderColor?: TStylingColor | null; + isDarkModeEnabled?: boolean | null; + roundness?: number | null; + cardArrangement?: { + linkSurveys: "casual" | "straight" | "simple"; + appSurveys: "casual" | "straight" | "simple"; + } | null; + background?: { + bg?: string | null; + bgType?: "animation" | "color" | "image" | "upload" | null; + brightness?: number | null; + } | null; + hideProgressBar?: boolean | null; + isLogoHidden?: boolean | null; +} + +export interface TProjectStyling extends TBaseStyling { + allowStyleOverwrite: boolean; +} + +export interface TSurveyStyling extends TBaseStyling { + overwriteThemeStyling?: boolean | null; +} + +export interface TWebViewOnMessageData { + onFinished?: boolean | null; + onDisplay?: boolean | null; + onResponse?: boolean | null; + responseUpdate?: TResponseUpdate | null; + onRetry?: boolean | null; + onClose?: boolean | null; + onFileUpload?: boolean | null; + fileUploadParams?: TFileUploadParams | null; + uploadId?: string | null; +} + +export const ZJsRNWebViewOnMessageData = z.object({ + onFinished: z.boolean().nullish(), + onDisplay: z.boolean().nullish(), + onResponse: z.boolean().nullish(), + responseUpdate: ZResponseUpdate.nullish(), + onRetry: z.boolean().nullish(), + onClose: z.boolean().nullish(), + onFileUpload: z.boolean().nullish(), + fileUploadParams: ZFileUploadParams.nullish(), + uploadId: z.string().nullish(), +}); + +export interface TUpdates { + userId: string; + attributes?: TAttributes; +} diff --git a/packages/react-native/src/types/error.ts b/packages/react-native/src/types/error.ts new file mode 100644 index 0000000000..744efc4596 --- /dev/null +++ b/packages/react-native/src/types/error.ts @@ -0,0 +1,65 @@ +export interface ResultError { + ok: false; + error: T; +} + +export interface ResultOk { + ok: true; + value: T; +} + +export type Result = { ok: true; data: T } | { ok: false; error: E }; + +export const ok = (data: T): Result => ({ ok: true, data }); + +export const okVoid = (): Result => ({ ok: true, data: undefined }); + +export const err = (error: E): ResultError => ({ + ok: false, + error, +}); + +export interface ApiErrorResponse { + code: + | "not_found" + | "gone" + | "bad_request" + | "internal_server_error" + | "unauthorized" + | "method_not_allowed" + | "not_authenticated" + | "forbidden" + | "network_error"; + message: string; + status: number; + url?: URL; + details?: Record; + responseMessage?: string; +} + +export interface MissingFieldError { + code: "missing_field"; + field: string; +} + +export interface MissingPersonError { + code: "missing_person"; + message: string; +} + +export interface NetworkError { + code: "network_error"; + status: number; + message: string; + url: URL; + responseMessage: string; +} +export interface NotInitializedError { + code: "not_initialized"; + message: string; +} + +export interface InvalidCodeError { + code: "invalid_code"; + message: string; +} diff --git a/packages/react-native/src/types/response.ts b/packages/react-native/src/types/response.ts new file mode 100644 index 0000000000..c912262e12 --- /dev/null +++ b/packages/react-native/src/types/response.ts @@ -0,0 +1,44 @@ +import { z } from "zod"; + +export type TResponseData = Record>; + +export type TResponseTtc = Record; + +export type TResponseVariables = Record; + +export type TResponseHiddenFieldValue = Record; + +export interface TResponseUpdate { + finished: boolean; + data: TResponseData; + language?: string; + variables?: TResponseVariables; + ttc?: TResponseTtc; + meta?: { url?: string; source?: string; action?: string }; + hiddenFields?: TResponseHiddenFieldValue; + displayId?: string | null; + endingId?: string | null; +} + +export const ZResponseData = z.record(z.union([z.string(), z.number(), z.array(z.string())])); +export const ZResponseVariables = z.record(z.union([z.string(), z.number()])); +export const ZResponseTtc = z.record(z.number()); +export const ZResponseHiddenFieldValue = z.record(z.union([z.string(), z.number(), z.array(z.string())])); + +export const ZResponseUpdate = z.object({ + finished: z.boolean(), + data: ZResponseData, + language: z.string().optional(), + variables: ZResponseVariables.optional(), + ttc: ZResponseTtc.optional(), + meta: z + .object({ + url: z.string().optional(), + source: z.string().optional(), + action: z.string().optional(), + }) + .optional(), + hiddenFields: ZResponseHiddenFieldValue.optional(), + displayId: z.string().nullish(), + endingId: z.string().nullish(), +}); diff --git a/packages/react-native/src/types/storage.ts b/packages/react-native/src/types/storage.ts new file mode 100644 index 0000000000..a6099a1577 --- /dev/null +++ b/packages/react-native/src/types/storage.ts @@ -0,0 +1,35 @@ +import { z } from "zod"; + +export interface TUploadFileConfig { + allowedFileExtensions?: string[] | undefined; + surveyId?: string | undefined; +} + +export interface TUploadFileResponse { + data: { + signedUrl: string; + fileUrl: string; + signingData: { + signature: string; + timestamp: number; + uuid: string; + } | null; + updatedFileName: string; + presignedFields?: Record | undefined; + }; +} + +export interface TFileUploadParams { + file: { type: string; name: string; base64: string }; + params: TUploadFileConfig; +} + +export const ZUploadFileConfig = z.object({ + allowedFileExtensions: z.array(z.string()).optional(), + surveyId: z.string().optional(), +}); + +export const ZFileUploadParams = z.object({ + file: z.object({ type: z.string(), name: z.string(), base64: z.string() }), + params: ZUploadFileConfig, +}); diff --git a/packages/react-native/src/types/survey.ts b/packages/react-native/src/types/survey.ts new file mode 100644 index 0000000000..40e2be1728 --- /dev/null +++ b/packages/react-native/src/types/survey.ts @@ -0,0 +1,35 @@ +import type { TEnvironmentStateSurvey, TProjectStyling, TSurveyStyling } from "@/types/config"; +import type { TResponseData, TResponseUpdate } from "@/types/response"; +import type { TFileUploadParams, TUploadFileConfig } from "@/types/storage"; + +export interface SurveyBaseProps { + survey: TEnvironmentStateSurvey; + styling: TSurveyStyling | TProjectStyling; + isBrandingEnabled: boolean; + getSetIsError?: (getSetError: (value: boolean) => void) => void; + getSetIsResponseSendingFinished?: (getSetIsResponseSendingFinished: (value: boolean) => void) => void; + getSetQuestionId?: (getSetQuestionId: (value: string) => void) => void; + getSetResponseData?: (getSetResponseData: (value: TResponseData) => void) => void; + onDisplay?: () => void; + onResponse?: (response: TResponseUpdate) => void; + onFinished?: () => void; + onClose?: () => void; + onRetry?: () => void; + autoFocus?: boolean; + isRedirectDisabled?: boolean; + prefillResponseData?: TResponseData; + skipPrefilled?: boolean; + languageCode: string; + onFileUpload: (file: TFileUploadParams["file"], config?: TUploadFileConfig) => Promise; + responseCount?: number; + isCardBorderVisible?: boolean; + startAtQuestionId?: string; + clickOutside?: boolean; + hiddenFieldsRecord?: TResponseData; + shouldResetQuestionId?: boolean; + fullSizeCards?: boolean; +} + +export interface SurveyInlineProps extends SurveyBaseProps { + containerId: string; +} diff --git a/packages/react-native/tsconfig.json b/packages/react-native/tsconfig.json index f3d5c3dc3c..77603fb57e 100644 --- a/packages/react-native/tsconfig.json +++ b/packages/react-native/tsconfig.json @@ -1,5 +1,9 @@ { "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, "strict": true }, "exclude": ["dist", "build", "node_modules"], diff --git a/packages/react-native/vite.config.ts b/packages/react-native/vite.config.ts index fa184e9a70..b0fc7f72cf 100644 --- a/packages/react-native/vite.config.ts +++ b/packages/react-native/vite.config.ts @@ -1,9 +1,14 @@ import { resolve } from "node:path"; -import { defineConfig } from "vite"; +import { type UserConfig, defineConfig } from "vite"; import dts from "vite-plugin-dts"; -const config = () => { +const config = (): UserConfig => { return defineConfig({ + resolve: { + alias: { + "@": resolve(__dirname, "src"), + }, + }, optimizeDeps: { exclude: ["react-native"], }, @@ -12,7 +17,13 @@ const config = () => { minify: "terser", sourcemap: true, rollupOptions: { - external: ["react", "react-native", "react-dom", "react-native-webview"], + external: [ + "react", + "react-native", + "react-dom", + "react-native-webview", + "@react-native-async-storage/async-storage", + ], }, lib: { entry: resolve(__dirname, "src/index.ts"), @@ -22,6 +33,14 @@ const config = () => { }, }, plugins: [dts({ rollupTypes: true, bundledPackages: ["@formbricks/api", "@formbricks/types"] })], + test: { + setupFiles: ["./vitest.setup.ts"], + coverage: { + provider: "v8", + reporter: ["text", "json", "html"], + include: ["src/lib/**/*.ts"], + }, + }, }); }; diff --git a/packages/react-native/vitest.setup.ts b/packages/react-native/vitest.setup.ts new file mode 100644 index 0000000000..4761029a38 --- /dev/null +++ b/packages/react-native/vitest.setup.ts @@ -0,0 +1,28 @@ +import { afterEach, beforeEach, vi } from "vitest"; + +beforeEach(() => { + vi.resetModules(); + vi.resetAllMocks(); +}); + +afterEach(() => { + vi.clearAllMocks(); +}); + +// Mock react-native +vi.mock("react-native", () => ({ + Platform: { OS: "ios" }, +})); + +// Mock react-native-webview +vi.mock("react-native-webview", () => ({ + WebView: vi.fn(), +})); + +vi.mock("@react-native-async-storage/async-storage", () => ({ + default: { + getItem: vi.fn(), + setItem: vi.fn(), + removeItem: vi.fn(), + }, +})); diff --git a/packages/surveys/src/components/general/subheader.tsx b/packages/surveys/src/components/general/subheader.tsx index 705886bbf2..66443fe75c 100644 --- a/packages/surveys/src/components/general/subheader.tsx +++ b/packages/surveys/src/components/general/subheader.tsx @@ -9,7 +9,7 @@ export function Subheader({ subheader, questionId }: SubheaderProps) { return ( diff --git a/packages/types/invites.ts b/packages/types/invites.ts deleted file mode 100644 index 346764297f..0000000000 --- a/packages/types/invites.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { z } from "zod"; -import { ZOrganizationRole } from "./memberships"; -import { ZUserName } from "./user"; - -export const ZInvite = z.object({ - id: z.string(), - email: z.string().email(), - name: z.string().nullish(), - organizationId: z.string(), - creatorId: z.string(), - acceptorId: z.string().nullish(), - createdAt: z.date(), - expiresAt: z.date(), - role: ZOrganizationRole, - teamIds: z.array(z.string()), -}); -export type TInvite = z.infer; - -export const ZInvitee = z.object({ - email: z.string().email(), - name: ZUserName, - role: ZOrganizationRole, - teamIds: z.array(z.string()), -}); -export type TInvitee = z.infer; - -export const ZInvitees = z.array(ZInvitee); - -export const ZCurrentUser = z.object({ - id: z.string(), - name: z.string().nullable(), -}); -export type TCurrentUser = z.infer; - -export const ZInviteUpdateInput = z.object({ - role: ZOrganizationRole, -}); -export type TInviteUpdateInput = z.infer; - -export const ZInviteMembersFormSchema = z.record( - z.object({ - email: z.string().email("Invalid email address"), - name: ZUserName, - }) -); - -export type TInviteMembersFormSchema = z.infer; diff --git a/packages/types/js.ts b/packages/types/js.ts index 1c8c193961..4490563558 100644 --- a/packages/types/js.ts +++ b/packages/types/js.ts @@ -92,17 +92,18 @@ export const ZJsPersonState = z.object({ ), responses: z.array(ZId), // responded survey ids lastDisplayAt: z.date().nullable(), + language: z.string().optional(), }), }); export type TJsPersonState = z.infer; -export const ZJsPersonIdentifyInput = z.object({ +export const ZJsUserIdentifyInput = z.object({ environmentId: z.string().cuid(), userId: z.string(), }); -export type TJsPersonIdentifyInput = z.infer; +export type TJsPersonIdentifyInput = z.infer; export const ZJsConfig = z.object({ environmentId: z.string().cuid(), @@ -149,6 +150,11 @@ export const ZJsContactsUpdateAttributeInput = z.object({ attributes: ZAttributes, }); +export const ZJsUserUpdateInput = z.object({ + userId: z.string().trim().min(1), + attributes: ZAttributes.optional(), +}); + export type TJsPeopleUpdateAttributeInput = z.infer; export type TJsPeopleUserIdInput = z.infer; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f95ed8af66..13b7fc14a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -73,12 +73,15 @@ importers: '@formbricks/react-native': specifier: workspace:* version: link:../../packages/react-native + '@react-native-async-storage/async-storage': + specifier: 2.1.0 + version: 2.1.0(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1)) expo: - specifier: 52.0.18 - version: 52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + specifier: 52.0.28 + version: 52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) expo-status-bar: - specifier: 2.0.0 - version: 2.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + specifier: 2.0.1 + version: 2.0.1(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) react: specifier: 18.3.1 version: 18.3.1 @@ -86,18 +89,18 @@ importers: specifier: 18.3.1 version: 18.3.1(react@18.3.1) react-native: - specifier: 0.76.5 - version: 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1) + specifier: 0.76.6 + version: 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1) react-native-webview: specifier: 13.12.5 - version: 13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + version: 13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) devDependencies: '@babel/core': specifier: 7.26.0 version: 7.26.0 '@types/react': - specifier: 19.0.1 - version: 19.0.1 + specifier: 18.3.18 + version: 18.3.18 typescript: specifier: 5.7.2 version: 5.7.2 @@ -106,16 +109,16 @@ importers: dependencies: '@algolia/autocomplete-core': specifier: 1.17.8 - version: 1.17.8(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.17.3) + version: 1.17.8(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3) '@calcom/embed-react': specifier: 1.5.1 version: 1.5.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@docsearch/css': specifier: '3' - version: 3.8.2 + version: 3.8.3 '@docsearch/react': specifier: 3.8.0 - version: 3.8.0(@algolia/client-search@5.19.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3) + version: 3.8.0(@algolia/client-search@5.20.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3) '@formbricks/lib': specifier: workspace:* version: link:../../packages/lib @@ -154,7 +157,7 @@ importers: version: 8.14.0 autoprefixer: specifier: 10.4.20 - version: 10.4.20(postcss@8.5.1) + version: 10.4.20(postcss@8.4.49) clsx: specifier: 2.1.1 version: 2.1.1 @@ -315,7 +318,7 @@ importers: version: 8.4.7(@storybook/test@8.4.7(storybook@8.4.7(prettier@3.4.2)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2) '@storybook/react-vite': specifier: 8.4.7 - version: 8.4.7(@storybook/test@8.4.7(storybook@8.4.7(prettier@3.4.2)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.31.0)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 8.4.7(@storybook/test@8.4.7(storybook@8.4.7(prettier@3.4.2)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.32.1)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) '@storybook/test': specifier: 8.4.7 version: 8.4.7(storybook@8.4.7(prettier@3.4.2)) @@ -342,7 +345,7 @@ importers: version: 8.4.7(prettier@3.4.2) tsup: specifier: 8.3.5 - version: 8.3.5(@microsoft/api-extractor@7.49.1(@types/node@22.10.2))(jiti@2.4.1)(postcss@8.5.1)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.7.0) + version: 8.3.5(@microsoft/api-extractor@7.49.1(@types/node@22.10.2))(jiti@2.4.1)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.7.0) vite: specifier: 6.0.9 version: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) @@ -350,8 +353,8 @@ importers: apps/web: dependencies: '@ai-sdk/azure': - specifier: 1.0.10 - version: 1.0.10(zod@3.24.1) + specifier: 1.1.9 + version: 1.1.9(zod@3.24.1) '@dnd-kit/core': specifier: 6.3.1 version: 6.3.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -408,7 +411,7 @@ importers: version: 0.21.0 '@lexical/react': specifier: 0.21.0 - version: 0.21.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(yjs@13.6.21) + version: 0.21.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(yjs@13.6.23) '@lexical/rich-text': specifier: 0.21.0 version: 0.21.0 @@ -432,61 +435,61 @@ importers: version: 6.0.1(prisma@6.0.1) '@radix-ui/react-accordion': specifier: 1.2.2 - version: 1.2.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.2.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-checkbox': specifier: 1.1.3 - version: 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-collapsible': specifier: 1.1.2 - version: 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-dialog': specifier: 1.1.3 - version: 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-dropdown-menu': specifier: 2.1.3 - version: 2.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-label': specifier: 2.1.1 - version: 2.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-popover': specifier: 1.1.3 - version: 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-radio-group': specifier: 1.2.2 - version: 1.2.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.2.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-select': specifier: 2.1.3 - version: 2.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-separator': specifier: 1.1.1 - version: 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-slider': specifier: 1.2.2 - version: 1.2.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.2.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-slot': specifier: 1.1.1 - version: 1.1.1(@types/react@18.3.11)(react@19.0.0) + version: 1.1.1(@types/react@18.3.18)(react@19.0.0) '@radix-ui/react-switch': specifier: 1.1.2 - version: 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-tabs': specifier: 1.1.2 - version: 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-toggle': specifier: 1.1.1 - version: 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-toggle-group': specifier: 1.1.1 - version: 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-tooltip': specifier: 1.1.5 - version: 1.1.5(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.5(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@react-email/components': specifier: 0.0.31 version: 0.0.31(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@sentry/nextjs': - specifier: 8.45.1 - version: 8.45.1(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(webpack@5.97.1) + specifier: 8.52.0 + version: 8.52.0(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(webpack@5.97.1) '@tailwindcss/forms': specifier: 0.5.9 version: 0.5.9(tailwindcss@3.4.16(ts-node@10.9.2(@types/node@22.10.2)(typescript@5.7.2))) @@ -498,19 +501,19 @@ importers: version: 8.20.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@vercel/functions': specifier: 1.5.2 - version: 1.5.2(@aws-sdk/credential-provider-web-identity@3.709.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))) + version: 1.5.2(@aws-sdk/credential-provider-web-identity@3.734.0) '@vercel/og': specifier: 0.6.4 version: 0.6.4 '@vercel/otel': specifier: 1.10.0 - version: 1.10.0(@opentelemetry/api-logs@0.56.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0)) + version: 1.10.0(@opentelemetry/api-logs@0.56.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0)) '@vercel/speed-insights': specifier: 1.1.0 version: 1.1.0(next@15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) ai: - specifier: 4.0.18 - version: 4.0.18(react@19.0.0)(zod@3.24.1) + specifier: 4.1.17 + version: 4.1.17(react@19.0.0)(zod@3.24.1) autoprefixer: specifier: 10.4.20 version: 10.4.20(postcss@8.4.49) @@ -528,7 +531,7 @@ importers: version: 2.1.1 cmdk: specifier: 1.0.4 - version: 1.0.4(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.0.4(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) csv-parse: specifier: 5.6.0 version: 5.6.0 @@ -558,7 +561,7 @@ importers: version: 9.0.2 langfuse-vercel: specifier: 3.31.3 - version: 3.31.3(ai@4.0.18(react@19.0.0)(zod@3.24.1)) + version: 3.31.3(ai@4.1.17(react@19.0.0)(zod@3.24.1)) lexical: specifier: 0.21.0 version: 0.21.0 @@ -695,6 +698,9 @@ importers: '@types/qrcode': specifier: 1.5.5 version: 1.5.5 + '@vitest/coverage-v8': + specifier: 2.1.8 + version: 2.1.8(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)) vite: specifier: 6.0.9 version: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) @@ -718,7 +724,7 @@ importers: version: link:../types '@rollup/plugin-inject': specifier: 5.0.5 - version: 5.0.5(rollup@4.31.0) + version: 5.0.5(rollup@4.32.1) buffer: specifier: 6.0.3 version: 6.0.3 @@ -730,10 +736,10 @@ importers: version: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) vite-plugin-dts: specifier: 4.3.0 - version: 4.3.0(@types/node@22.10.2)(rollup@4.31.0)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 4.3.0(@types/node@22.10.2)(rollup@4.32.1)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) vite-plugin-node-polyfills: specifier: 0.22.0 - version: 0.22.0(rollup@4.31.0)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 0.22.0(rollup@4.32.1)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) packages/config-eslint: devDependencies: @@ -748,7 +754,7 @@ importers: version: 8.18.0(eslint@8.57.0)(typescript@5.7.2) '@vercel/style-guide': specifier: 6.0.0 - version: 6.0.0(@next/eslint-plugin-next@15.1.0)(eslint@8.57.0)(prettier@3.4.2)(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)) + version: 6.0.0(@next/eslint-plugin-next@15.1.0)(eslint@8.57.0)(prettier@3.4.2)(typescript@5.7.2)(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) eslint-config-next: specifier: 15.1.0 version: 15.1.0(eslint@8.57.0)(typescript@5.7.2) @@ -864,7 +870,7 @@ importers: version: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) vite-plugin-dts: specifier: 4.3.0 - version: 4.3.0(@types/node@22.10.2)(rollup@4.31.0)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 4.3.0(@types/node@22.10.2)(rollup@4.32.1)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) packages/js-core: devDependencies: @@ -891,22 +897,22 @@ importers: version: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) vite-plugin-dts: specifier: 4.3.0 - version: 4.3.0(@types/node@22.10.2)(rollup@4.31.0)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 4.3.0(@types/node@22.10.2)(rollup@4.32.1)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) packages/lib: dependencies: '@ai-sdk/azure': - specifier: 1.0.10 - version: 1.0.10(zod@3.24.1) + specifier: 1.1.9 + version: 1.1.9(zod@3.24.1) '@aws-sdk/client-s3': - specifier: 3.712.0 - version: 3.712.0(aws-crt@1.24.0) + specifier: 3.741.0 + version: 3.741.0(aws-crt@1.25.3) '@aws-sdk/s3-presigned-post': - specifier: 3.712.0 - version: 3.712.0(aws-crt@1.24.0) + specifier: 3.741.0 + version: 3.741.0(aws-crt@1.25.3) '@aws-sdk/s3-request-presigner': - specifier: 3.712.0 - version: 3.712.0 + specifier: 3.741.0 + version: 3.741.0 '@formbricks/api': specifier: workspace:* version: link:../api @@ -920,14 +926,14 @@ importers: specifier: 2.2.2 version: 2.2.2 '@t3-oss/env-nextjs': - specifier: 0.11.1 - version: 0.11.1(typescript@5.7.2)(zod@3.24.1) + specifier: 0.12.0 + version: 0.12.0(typescript@5.7.2)(zod@3.24.1) '@ungap/structured-clone': - specifier: 1.2.1 - version: 1.2.1 + specifier: 1.3.0 + version: 1.3.0 aws-crt: - specifier: 1.24.0 - version: 1.24.0 + specifier: 1.25.3 + version: 1.25.3 date-fns: specifier: 4.1.0 version: 4.1.0 @@ -947,8 +953,8 @@ importers: specifier: 4.24.11 version: 4.24.11(next@15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(nodemailer@6.9.16)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) posthog-node: - specifier: 4.3.2 - version: 4.3.2 + specifier: 4.4.1 + version: 4.4.1 qrcode: specifier: 1.5.4 version: 1.5.4 @@ -959,8 +965,8 @@ importers: specifier: 2.2.2 version: 2.2.2 tailwind-merge: - specifier: 2.5.5 - version: 2.5.5 + specifier: 3.0.1 + version: 3.0.1 devDependencies: '@formbricks/config-typescript': specifier: workspace:* @@ -969,8 +975,8 @@ importers: specifier: workspace:* version: link:../config-eslint '@types/jsonwebtoken': - specifier: 9.0.7 - version: 9.0.7 + specifier: 9.0.8 + version: 9.0.8 '@types/mime-types': specifier: 2.1.4 version: 2.1.4 @@ -984,17 +990,23 @@ importers: specifier: 10.9.2 version: 10.9.2(@types/node@22.10.2)(typescript@5.7.2) vitest: - specifier: 2.1.8 - version: 2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0) + specifier: 3.0.5 + version: 3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) vitest-mock-extended: specifier: 2.0.2 - version: 2.0.2(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)) + version: 2.0.2(typescript@5.7.2)(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) packages/react-native: dependencies: + '@react-native-async-storage/async-storage': + specifier: '>=2.1.0' + version: 2.1.0(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1)) react-native-webview: specifier: '>=13.0.0' - version: 13.12.5(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + version: 13.12.5(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + zod: + specifier: 3.24.1 + version: 3.24.1 devDependencies: '@formbricks/api': specifier: workspace:* @@ -1002,24 +1014,18 @@ importers: '@formbricks/config-typescript': specifier: workspace:* version: link:../config-typescript - '@formbricks/lib': - specifier: workspace:* - version: link:../lib - '@formbricks/types': - specifier: workspace:* - version: link:../types - '@react-native-async-storage/async-storage': - specifier: 2.1.0 - version: 2.1.0(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1)) '@types/react': specifier: 18.3.11 version: 18.3.11 + '@vitest/coverage-v8': + specifier: 3.0.4 + version: 3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) react: specifier: 18.3.1 version: 18.3.1 react-native: specifier: 0.74.5 - version: 0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1) + version: 0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1) terser: specifier: 5.37.0 version: 5.37.0 @@ -1028,7 +1034,10 @@ importers: version: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) vite-plugin-dts: specifier: 4.3.0 - version: 4.3.0(@types/node@22.10.2)(rollup@4.31.0)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 4.3.0(@types/node@22.10.2)(rollup@4.32.1)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + vitest: + specifier: 3.0.4 + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) packages/surveys: dependencies: @@ -1092,7 +1101,7 @@ importers: version: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) vite-plugin-dts: specifier: 4.3.0 - version: 4.3.0(@types/node@22.10.2)(rollup@4.31.0)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + version: 4.3.0(@types/node@22.10.2)(rollup@4.32.1)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) vite-tsconfig-paths: specifier: 5.1.4 version: 5.1.4(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) @@ -1135,20 +1144,20 @@ packages: '@adobe/css-tools@4.4.1': resolution: {integrity: sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==} - '@ai-sdk/azure@1.0.10': - resolution: {integrity: sha512-drbmzYS0iPRU/I3xnzphxNsYvSMjYhoq8gK34zShjeJGpPbewZPsXbPrncX6gdrkD4JR021yCahPc9E6RpXr5Q==} + '@ai-sdk/azure@1.1.9': + resolution: {integrity: sha512-bQL/HGsj8NndoXomgZ7m3LVj/hyRy3alTZNPT8OkUL24Uj3P0mWad4sejaRdSVLr2joBb74YqQ2jlMvRGGG/bw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 - '@ai-sdk/openai@1.0.8': - resolution: {integrity: sha512-wcTHM9qgRWGYVO3WxPSTN/RwnZ9R5/17xyo61iUCCSCZaAuJyh6fKddO0/oamwDp3BG7g+4wbfAyuTo32H+fHw==} + '@ai-sdk/openai@1.1.9': + resolution: {integrity: sha512-t/CpC4TLipdbgBJTMX/otzzqzCMBSPQwUOkYPGbT/jyuC86F+YO9o+LS0Ty2pGUE1kyT+B3WmJ318B16ZCg4hw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 - '@ai-sdk/provider-utils@2.0.4': - resolution: {integrity: sha512-GMhcQCZbwM6RoZCri0MWeEWXRt/T+uCxsmHEsTwNvEH3GDjNzchfX25C8ftry2MeEOOn6KfqCLSKomcgK6RoOg==} + '@ai-sdk/provider-utils@2.1.6': + resolution: {integrity: sha512-Pfyaj0QZS22qyVn5Iz7IXcJ8nKIKlu2MeSAdKJzTwkAks7zdLaKVB+396Rqcp1bfQnxl7vaduQVMQiXUrgK8Gw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -1156,12 +1165,12 @@ packages: zod: optional: true - '@ai-sdk/provider@1.0.2': - resolution: {integrity: sha512-YYtP6xWQyaAf5LiWLJ+ycGTOeBLWrED7LUrvc+SQIWhGaneylqbaGsyQL7VouQUeQ4JZ1qKYZuhmi3W56HADPA==} + '@ai-sdk/provider@1.0.7': + resolution: {integrity: sha512-q1PJEZ0qD9rVR+8JFEd01/QM++csMT5UVwYXSN2u54BrVw/D8TZLTeg2FEfKK00DgAx0UtWd8XOhhwITP9BT5g==} engines: {node: '>=18'} - '@ai-sdk/react@1.0.6': - resolution: {integrity: sha512-8Hkserq0Ge6AEi7N4hlv2FkfglAGbkoAXEZ8YSp255c3PbnZz6+/5fppw+aROmZMOfNwallSRuy1i/iPa2rBpQ==} + '@ai-sdk/react@1.1.8': + resolution: {integrity: sha512-buHm7hP21xEOksnRQtJX9fKbi7cAUwanEBa5niddTDibCDKd+kIXP2vaJGy8+heB3rff+XSW3BWlA8pscK+n1g==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc @@ -1172,8 +1181,8 @@ packages: zod: optional: true - '@ai-sdk/ui-utils@1.0.5': - resolution: {integrity: sha512-DGJSbDf+vJyWmFNexSPUsS1AAy7gtsmFmoSyNbNbJjwl9hRIf2dknfA1V0ahx6pg3NNklNYFm53L8Nphjovfvg==} + '@ai-sdk/ui-utils@1.1.8': + resolution: {integrity: sha512-nbok53K1EalO2sZjBLFB33cqs+8SxiL6pe7ekZ7+5f2MJTwdvpShl6d9U4O8fO3DnZ9pYLzaVC0XNMxnJt030Q==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 @@ -1215,56 +1224,56 @@ packages: '@algolia/client-search': '>= 4.9.1 < 6' algoliasearch: '>= 4.9.1 < 6' - '@algolia/client-abtesting@5.19.0': - resolution: {integrity: sha512-dMHwy2+nBL0SnIsC1iHvkBao64h4z+roGelOz11cxrDBrAdASxLxmfVMop8gmodQ2yZSacX0Rzevtxa+9SqxCw==} + '@algolia/client-abtesting@5.20.0': + resolution: {integrity: sha512-YaEoNc1Xf2Yk6oCfXXkZ4+dIPLulCx8Ivqj0OsdkHWnsI3aOJChY5qsfyHhDBNSOhqn2ilgHWxSfyZrjxBcAww==} engines: {node: '>= 14.0.0'} - '@algolia/client-analytics@5.19.0': - resolution: {integrity: sha512-CDW4RwnCHzU10upPJqS6N6YwDpDHno7w6/qXT9KPbPbt8szIIzCHrva4O9KIfx1OhdsHzfGSI5hMAiOOYl4DEQ==} + '@algolia/client-analytics@5.20.0': + resolution: {integrity: sha512-CIT9ni0+5sYwqehw+t5cesjho3ugKQjPVy/iPiJvtJX4g8Cdb6je6SPt2uX72cf2ISiXCAX9U3cY0nN0efnRDw==} engines: {node: '>= 14.0.0'} - '@algolia/client-common@5.19.0': - resolution: {integrity: sha512-2ERRbICHXvtj5kfFpY5r8qu9pJII/NAHsdgUXnUitQFwPdPL7wXiupcvZJC7DSntOnE8AE0lM7oDsPhrJfj5nQ==} + '@algolia/client-common@5.20.0': + resolution: {integrity: sha512-iSTFT3IU8KNpbAHcBUJw2HUrPnMXeXLyGajmCL7gIzWOsYM4GabZDHXOFx93WGiXMti1dymz8k8R+bfHv1YZmA==} engines: {node: '>= 14.0.0'} - '@algolia/client-insights@5.19.0': - resolution: {integrity: sha512-xPOiGjo6I9mfjdJO7Y+p035aWePcbsItizIp+qVyfkfZiGgD+TbNxM12g7QhFAHIkx/mlYaocxPY/TmwPzTe+A==} + '@algolia/client-insights@5.20.0': + resolution: {integrity: sha512-w9RIojD45z1csvW1vZmAko82fqE/Dm+Ovsy2ElTsjFDB0HMAiLh2FO86hMHbEXDPz6GhHKgGNmBRiRP8dDPgJg==} engines: {node: '>= 14.0.0'} - '@algolia/client-personalization@5.19.0': - resolution: {integrity: sha512-B9eoce/fk8NLboGje+pMr72pw+PV7c5Z01On477heTZ7jkxoZ4X92dobeGuEQop61cJ93Gaevd1of4mBr4hu2A==} + '@algolia/client-personalization@5.20.0': + resolution: {integrity: sha512-p/hftHhrbiHaEcxubYOzqVV4gUqYWLpTwK+nl2xN3eTrSW9SNuFlAvUBFqPXSVBqc6J5XL9dNKn3y8OA1KElSQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-query-suggestions@5.19.0': - resolution: {integrity: sha512-6fcP8d4S8XRDtVogrDvmSM6g5g6DndLc0pEm1GCKe9/ZkAzCmM3ZmW1wFYYPxdjMeifWy1vVEDMJK7sbE4W7MA==} + '@algolia/client-query-suggestions@5.20.0': + resolution: {integrity: sha512-m4aAuis5vZi7P4gTfiEs6YPrk/9hNTESj3gEmGFgfJw3hO2ubdS4jSId1URd6dGdt0ax2QuapXufcrN58hPUcw==} engines: {node: '>= 14.0.0'} - '@algolia/client-search@5.19.0': - resolution: {integrity: sha512-Ctg3xXD/1VtcwmkulR5+cKGOMj4r0wC49Y/KZdGQcqpydKn+e86F6l3tb3utLJQVq4lpEJud6kdRykFgcNsp8Q==} + '@algolia/client-search@5.20.0': + resolution: {integrity: sha512-KL1zWTzrlN4MSiaK1ea560iCA/UewMbS4ZsLQRPoDTWyrbDKVbztkPwwv764LAqgXk0fvkNZvJ3IelcK7DqhjQ==} engines: {node: '>= 14.0.0'} - '@algolia/ingestion@1.19.0': - resolution: {integrity: sha512-LO7w1MDV+ZLESwfPmXkp+KLeYeFrYEgtbCZG6buWjddhYraPQ9MuQWLhLLiaMlKxZ/sZvFTcZYuyI6Jx4WBhcg==} + '@algolia/ingestion@1.20.0': + resolution: {integrity: sha512-shj2lTdzl9un4XJblrgqg54DoK6JeKFO8K8qInMu4XhE2JuB8De6PUuXAQwiRigZupbI0xq8aM0LKdc9+qiLQA==} engines: {node: '>= 14.0.0'} - '@algolia/monitoring@1.19.0': - resolution: {integrity: sha512-Mg4uoS0aIKeTpu6iv6O0Hj81s8UHagi5TLm9k2mLIib4vmMtX7WgIAHAcFIaqIZp5D6s5EVy1BaDOoZ7buuJHA==} + '@algolia/monitoring@1.20.0': + resolution: {integrity: sha512-aF9blPwOhKtWvkjyyXh9P5peqmhCA1XxLBRgItT+K6pbT0q4hBDQrCid+pQZJYy4HFUKjB/NDDwyzFhj/rwKhw==} engines: {node: '>= 14.0.0'} - '@algolia/recommend@5.19.0': - resolution: {integrity: sha512-PbgrMTbUPlmwfJsxjFhal4XqZO2kpBNRjemLVTkUiti4w/+kzcYO4Hg5zaBgVqPwvFDNQ8JS4SS3TBBem88u+g==} + '@algolia/recommend@5.20.0': + resolution: {integrity: sha512-T6B/WPdZR3b89/F9Vvk6QCbt/wrLAtrGoL8z4qPXDFApQ8MuTFWbleN/4rHn6APWO3ps+BUePIEbue2rY5MlRw==} engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@5.19.0': - resolution: {integrity: sha512-GfnhnQBT23mW/VMNs7m1qyEyZzhZz093aY2x8p0era96MMyNv8+FxGek5pjVX0b57tmSCZPf4EqNCpkGcGsmbw==} + '@algolia/requester-browser-xhr@5.20.0': + resolution: {integrity: sha512-t6//lXsq8E85JMenHrI6mhViipUT5riNhEfCcvtRsTV+KIBpC6Od18eK864dmBhoc5MubM0f+sGpKOqJIlBSCg==} engines: {node: '>= 14.0.0'} - '@algolia/requester-fetch@5.19.0': - resolution: {integrity: sha512-oyTt8ZJ4T4fYvW5avAnuEc6Laedcme9fAFryMD9ndUTIUe/P0kn3BuGcCLFjN3FDmdrETHSFkgPPf1hGy3sLCw==} + '@algolia/requester-fetch@5.20.0': + resolution: {integrity: sha512-FHxYGqRY+6bgjKsK4aUsTAg6xMs2S21elPe4Y50GB0Y041ihvw41Vlwy2QS6K9ldoftX4JvXodbKTcmuQxywdQ==} engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@5.19.0': - resolution: {integrity: sha512-p6t8ue0XZNjcRiqNkb5QAM0qQRAKsCiebZ6n9JjWA+p8fWf8BvnhO55y2fO28g3GW0Imj7PrAuyBuxq8aDVQwQ==} + '@algolia/requester-node-http@5.20.0': + resolution: {integrity: sha512-kmtQClq/w3vtPteDSPvaW9SPZL/xrIgMrxZyAgsFwrJk0vJxqyC5/hwHmrCraDnStnGSADnLpBf4SpZnwnkwWw==} engines: {node: '>= 14.0.0'} '@alloc/quick-lru@5.2.0': @@ -1279,8 +1288,8 @@ packages: resolution: {integrity: sha512-O0Uv9LbLDSoEg26fnMDdDRiPwFJnQSoD4WnrflDwKCJm8Cx/0mV4cGxwBLXan5mGIrpK4Dd7vizf4rQm0QCEAA==} hasBin: true - '@asamuzakjp/css-color@2.8.2': - resolution: {integrity: sha512-RtWv9jFN2/bLExuZgFFZ0I3pWWeezAHGgrmjqGGWclATl1aDe3yhCUaI0Ilkp6OCk9zX7+FjvDasEX8Q9Rxc5w==} + '@asamuzakjp/css-color@2.8.3': + resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==} '@aws-crypto/crc32@5.2.0': resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} @@ -1305,148 +1314,136 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.712.0': - resolution: {integrity: sha512-Hq1IIwOFutmHtTz3mROR1XhTDL8rxcYbYw3ajjgeMJB5tjcvodpfkfz/L4dxXZMwqylWf6SNQNAiaGh5mlsGGQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-s3@3.741.0': + resolution: {integrity: sha512-sZvdbRZ+E9/GcOMUOkZvYvob95N6c9LdzDneXHFASA7OIaEOQxQT1Arimz7JpEhfq/h9K2/j7wNO4jh4x80bmA==} + engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso-oidc@3.712.0': - resolution: {integrity: sha512-xNFrG9syrG6pxUP7Ld/nu3afQ9+rbJM9qrE+wDNz4VnNZ3vLiJty4fH85zBFhOQ5OF2DIJTWsFzXGi2FYjsCMA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.712.0 + '@aws-sdk/client-sso@3.734.0': + resolution: {integrity: sha512-oerepp0mut9VlgTwnG5Ds/lb0C0b2/rQ+hL/rF6q+HGKPfGsCuPvFx1GtwGKCXd49ase88/jVgrhcA9OQbz3kg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso@3.712.0': - resolution: {integrity: sha512-tBo/eW3YpZ9f3Q1qA7aA8uliNFJJX0OP7R2IUJ8t6rqVTk15wWCEPNmXzUZKgruDnKUfCaF4+r9q/Yy4fBc9PA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.734.0': + resolution: {integrity: sha512-SxnDqf3vobdm50OLyAKfqZetv6zzwnSqwIwd3jrbopxxHKqNIM/I0xcYjD6Tn+mPig+u7iRKb9q3QnEooFTlmg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/client-sts@3.712.0': - resolution: {integrity: sha512-gIO6BD+hkEe3GKQhbiFP0zcNQv0EkP1Cl9SOstxS+X9CeudEgVX/xEPUjyoFVkfkntPBJ1g0I1u5xOzzRExl4g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-env@3.734.0': + resolution: {integrity: sha512-gtRkzYTGafnm1FPpiNO8VBmJrYMoxhDlGPYDVcijzx3DlF8dhWnowuSBCxLSi+MJMx5hvwrX2A+e/q0QAeHqmw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.709.0': - resolution: {integrity: sha512-7kuSpzdOTAE026j85wq/fN9UDZ70n0OHw81vFqMWwlEFtm5IQ/MRCLKcC4HkXxTdfy1PqFlmoXxWqeBa15tujw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.734.0': + resolution: {integrity: sha512-JFSL6xhONsq+hKM8xroIPhM5/FOhiQ1cov0lZxhzZWj6Ai3UAjucy3zyIFDr9MgP1KfCYNdvyaUq9/o+HWvEDg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.709.0': - resolution: {integrity: sha512-ZMAp9LSikvHDFVa84dKpQmow6wsg956Um20cKuioPpX2GGreJFur7oduD+tRJT6FtIOHn+64YH+0MwiXLhsaIQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-ini@3.741.0': + resolution: {integrity: sha512-/XvnVp6zZXsyUlP1FtmspcWnd+Z1u2WK0wwzTE/x277M0oIhAezCW79VmcY4jcDQbYH+qMbtnBexfwgFDARxQg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.709.0': - resolution: {integrity: sha512-lIS7XLwCOyJnLD70f+VIRr8DNV1HPQe9oN6aguYrhoczqz7vDiVZLe3lh714cJqq9rdxzFypK5DqKHmcscMEPQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.741.0': + resolution: {integrity: sha512-iz/puK9CZZkZjrKXX2W+PaiewHtlcD7RKUIsw4YHFyb8lrOt7yTYpM6VjeI+T//1sozjymmAnnp1SST9TXApLQ==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.712.0': - resolution: {integrity: sha512-sTsdQ/Fm/suqMdpjhMuss/5uKL18vcuWnNTQVrG9iGNRqZLbq65MXquwbUpgzfoUmIcH+4CrY6H2ebpTIECIag==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.712.0 + '@aws-sdk/credential-provider-process@3.734.0': + resolution: {integrity: sha512-zvjsUo+bkYn2vjT+EtLWu3eD6me+uun+Hws1IyWej/fKFAqiBPwyeyCgU7qjkiPQSXqk1U9+/HG9IQ6Iiz+eBw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-node@3.712.0': - resolution: {integrity: sha512-gXrHymW3rMRYORkPVQwL8Gi5Lu92F16SoZR543x03qCi7rm00oL9tRD85ACxkhprS1Wh8lUIUMNoeiwnYWTNuQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.734.0': + resolution: {integrity: sha512-cCwwcgUBJOsV/ddyh1OGb4gKYWEaTeTsqaAK19hiNINfYV/DO9r4RMlnWAo84sSBfJuj9shUNsxzyoe6K7R92Q==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.709.0': - resolution: {integrity: sha512-IAC+jPlGQII6jhIylHOwh3RgSobqlgL59nw2qYTURr8hMCI0Z1p5y2ee646HTVt4WeCYyzUAXfxr6YI/Vitv+Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-web-identity@3.734.0': + resolution: {integrity: sha512-t4OSOerc+ppK541/Iyn1AS40+2vT/qE+MFMotFkhCgCJbApeRF2ozEdnDN6tGmnl4ybcUuxnp9JWLjwDVlR/4g==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.712.0': - resolution: {integrity: sha512-8lCMxY7Lb9VK9qdlNXRJXE3W1UDVURnJZ3a4XWYNY6yr1TfQaN40mMyXX1oNlXXJtMV0szRvjM8dZj37E/ESAw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.734.0': + resolution: {integrity: sha512-etC7G18aF7KdZguW27GE/wpbrNmYLVT755EsFc8kXpZj8D6AFKxc7OuveinJmiy0bYXAMspJUWsF6CrGpOw6CQ==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.709.0': - resolution: {integrity: sha512-2lbDfE0IQ6gma/7BB2JpkjW5G0wGe4AS0x80oybYAYYviJmUtIR3Cn2pXun6bnAWElt4wYKl4su7oC36rs5rNA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.709.0 + '@aws-sdk/middleware-expect-continue@3.734.0': + resolution: {integrity: sha512-P38/v1l6HjuB2aFUewt7ueAW5IvKkFcv5dalPtbMGRhLeyivBOHwbCyuRKgVs7z7ClTpu9EaViEGki2jEQqEsQ==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-bucket-endpoint@3.709.0': - resolution: {integrity: sha512-03+tJOd7KIZOiqWH7Z8BOfQIWkKJgjcpKOJKZ6FR2KjWGUOE1G+bo11wF4UuHQ0RmpKnApt+pQghZmSnE7WEeg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.735.0': + resolution: {integrity: sha512-Tx7lYTPwQFRe/wQEHMR6Drh/S+X0ToAEq1Ava9QyxV1riwtepzRLojpNDELFb3YQVVYbX7FEiBMCJLMkmIIY+A==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-expect-continue@3.709.0': - resolution: {integrity: sha512-Tbl/DFvE4rHl8lMb9IzetwK4tf5R3VeHZkvEXQalsWoK0tbEQ8kXWi7wAYO4qbE7bFVvaxKX+irjJjTxf3BrCQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-host-header@3.734.0': + resolution: {integrity: sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.709.0': - resolution: {integrity: sha512-wbYm9tkyCaqMeU82yjaXw7V5BxCSlSLNupENW63LC7Fvyo/aQzj6LjSMHcBpR2QwjBEhXCtF47L7aQ8SPTNhdw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-location-constraint@3.734.0': + resolution: {integrity: sha512-EJEIXwCQhto/cBfHdm3ZOeLxd2NlJD+X2F+ZTOxzokuhBtY0IONfC/91hOo5tWQweerojwshSMHRCKzRv1tlwg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-host-header@3.709.0': - resolution: {integrity: sha512-8gQYCYAaIw4lOCd5WYdf15Y/61MgRsAnrb2eiTl+icMlUOOzl8aOl5iDwm/Idp0oHZTflwxM4XSvGXO83PRWcw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-logger@3.734.0': + resolution: {integrity: sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-location-constraint@3.709.0': - resolution: {integrity: sha512-5YQWPXfZq7OE0jB2G0PP8K10GBod/YPJXb+1CfJS6FbQaglRoIm8KZmVEvJNnptSKyGtE62veeCcCQcfAUfFig==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-recursion-detection@3.734.0': + resolution: {integrity: sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-logger@3.709.0': - resolution: {integrity: sha512-jDoGSccXv9zebnpUoisjWd5u5ZPIalrmm6TjvPzZ8UqzQt3Beiz0tnQwmxQD6KRc7ADweWP5Ntiqzbw9xpVajg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-sdk-s3@3.740.0': + resolution: {integrity: sha512-VML9TzNoQdAs5lSPQSEgZiPgMUSz2H7SltaLb9g4tHwKK5xQoTq5WcDd6V1d2aPxSN5Q2Q63aiVUBby6MdUN/Q==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-recursion-detection@3.709.0': - resolution: {integrity: sha512-PObL/wLr4lkfbQ0yXUWaoCWu/jcwfwZzCjsUiXW/H6hW9b/00enZxmx7OhtJYaR6xmh/Lcx5wbhIoDCbzdv0tw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-ssec@3.734.0': + resolution: {integrity: sha512-d4yd1RrPW/sspEXizq2NSOUivnheac6LPeLSLnaeTbBG9g1KqIqvCzP1TfXEqv2CrWfHEsWtJpX7oyjySSPvDQ==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-sdk-s3@3.709.0': - resolution: {integrity: sha512-FwtOG9t9xsLoLOQZ6qAdsWOjx9dsO6t28IjIDV1l6Ixiu2oC0Yks7goONjJUH0IDE4pDDDGzmuq0sn1XtHhheA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.734.0': + resolution: {integrity: sha512-MFVzLWRkfFz02GqGPjqSOteLe5kPfElUrXZft1eElnqulqs6RJfVSpOV7mO90gu293tNAeggMWAVSGRPKIYVMg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-ssec@3.709.0': - resolution: {integrity: sha512-2muiLe7YkmlwZp2SKz+goZrDThGfRq3o0FcJF3Puc0XGmcEPEDjih537mCoTrGgcXNFlBc7YChd84r3t72ySaQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/nested-clients@3.734.0': + resolution: {integrity: sha512-iph2XUy8UzIfdJFWo1r0Zng9uWj3253yvW9gljhtu+y/LNmNvSnJxQk1f3D2BC5WmcoPZqTS3UsycT3mLPSzWA==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.709.0': - resolution: {integrity: sha512-ooc9ZJvgkjPhi9q05XwSfNTXkEBEIfL4hleo5rQBKwHG3aTHvwOM7LLzhdX56QZVa6sorPBp6fwULuRDSqiQHw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/region-config-resolver@3.734.0': + resolution: {integrity: sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==} + engines: {node: '>=18.0.0'} - '@aws-sdk/region-config-resolver@3.709.0': - resolution: {integrity: sha512-/NoCAMEVKAg3kBKOrNtgOfL+ECt6nrl+L7q2SyYmrcY4tVCmwuECVqewQaHc03fTnJijfKLccw0Fj+6wOCnB6w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/s3-presigned-post@3.741.0': + resolution: {integrity: sha512-zWGTVKT+RWXaPvkwr3zURvTau8NTfsAnCKCYqjkkKwWuntKNeS0XCbClrUSkIeGjlQNA/8B5Z3cXFFZbyJmQAg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/s3-presigned-post@3.712.0': - resolution: {integrity: sha512-79cP6Wejee3JNdxwo/tn49nT6JfL+ncex7fL773JHQTOYTj9czVprxL2sfbfgJ+e0mclg3PTujetwIm9LEHSyw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/s3-request-presigner@3.741.0': + resolution: {integrity: sha512-qrYYS+XG6wRwNDt60tcFKDCkQoLiBHhNlHaUtsHwdmSnlwA4aIuxCGXMkuskX93FsoLUDpuxtA0MZth3JL36dw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/s3-request-presigner@3.712.0': - resolution: {integrity: sha512-LE+uNtGDyypRMxBfrJmkpWaW+x0QFp4qYH+nZYMDLdD0um8UrTrbVSfvIxcVm9QsL1gVy6WkpUj+5cU3YZBgyQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/signature-v4-multi-region@3.740.0': + resolution: {integrity: sha512-w+psidN3i+kl51nQEV3V+fKjKUqcEbqUA1GtubruDBvBqrl5El/fU2NF3Lo53y8CfI9wCdf3V7KOEpHIqxHNng==} + engines: {node: '>=18.0.0'} - '@aws-sdk/signature-v4-multi-region@3.709.0': - resolution: {integrity: sha512-m0vhJEy6SLbjL11K9cHzX/ZhCIj//1GkTbYk2d4tTQFSuPyJEkjmoeHk9dYm2mJy0wH48j29OJadI1JUsR5bOw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/token-providers@3.734.0': + resolution: {integrity: sha512-2U6yWKrjWjZO8Y5SHQxkFvMVWHQWbS0ufqfAIBROqmIZNubOL7jXCiVdEFekz6MZ9LF2tvYGnOW4jX8OKDGfIw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/token-providers@3.709.0': - resolution: {integrity: sha512-q5Ar6k71nci43IbULFgC8a89d/3EHpmd7HvBzqVGRcHnoPwh8eZDBfbBXKH83NGwcS1qPSRYiDbVfeWPm4/1jA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.709.0 + '@aws-sdk/types@3.734.0': + resolution: {integrity: sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/types@3.709.0': - resolution: {integrity: sha512-ArtLTMxgjf13Kfu3gWH3Ez9Q5TkDdcRZUofpKH3pMGB/C6KAbeSCtIIDKfoRTUABzyGlPyCrZdnFjKyH+ypIpg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-arn-parser@3.723.0': + resolution: {integrity: sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==} + engines: {node: '>=18.0.0'} - '@aws-sdk/util-arn-parser@3.693.0': - resolution: {integrity: sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.734.0': + resolution: {integrity: sha512-w2+/E88NUbqql6uCVAsmMxDQKu7vsKV0KqhlQb0lL+RCq4zy07yXYptVNs13qrnuTfyX7uPXkXrlugvK9R1Ucg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/util-endpoints@3.709.0': - resolution: {integrity: sha512-Mbc7AtL5WGCTKC16IGeUTz+sjpC3ptBda2t0CcK0kMVw3THDdcSq6ZlNKO747cNqdbwUvW34oHteUiHv4/z88Q==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/util-format-url@3.709.0': - resolution: {integrity: sha512-HGR11hx1KeFfoub/TACf+Yyal37lR85791Di2QPaElQThaqztLlppxale3EohKboOFf7Q/zvslJyM0fmgrlpQw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-format-url@3.734.0': + resolution: {integrity: sha512-TxZMVm8V4aR/QkW9/NhujvYpPZjUYqzLwSge5imKZbWFR806NP7RMwc5ilVuHF/bMOln/cVHkl42kATElWBvNw==} + engines: {node: '>=18.0.0'} '@aws-sdk/util-locate-window@3.723.0': resolution: {integrity: sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-user-agent-browser@3.709.0': - resolution: {integrity: sha512-/rL2GasJzdTWUURCQKFldw2wqBtY4k4kCiA2tVZSKg3y4Ey7zO34SW8ebaeCE2/xoWOyLR2/etdKyphoo4Zrtg==} + '@aws-sdk/util-user-agent-browser@3.734.0': + resolution: {integrity: sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==} - '@aws-sdk/util-user-agent-node@3.712.0': - resolution: {integrity: sha512-26X21bZ4FWsVpqs33uOXiB60TOWQdVlr7T7XONDFL/XN7GEpUJkWuuIB4PTok6VOmh1viYcdxZQqekXPuzXexQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-user-agent-node@3.734.0': + resolution: {integrity: sha512-c6Iinh+RVQKs6jYUFQ64htOU2HUXFQ3TVx+8Tu3EDF19+9vzWi9UukhIMH9rqyyEXIAkk9XL7avt8y2Uyw2dGA==} + engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' peerDependenciesMeta: @@ -1456,9 +1453,9 @@ packages: '@aws-sdk/util-utf8-browser@3.259.0': resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} - '@aws-sdk/xml-builder@3.709.0': - resolution: {integrity: sha512-2GPCwlNxeHspoK/Mc8nbk9cBOkSpp3j2SJUQmFnyQK6V/pR6II2oPRyZkMomug1Rc10hqlBHByMecq4zhV2uUw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/xml-builder@3.734.0': + resolution: {integrity: sha512-Zrjxi5qwGEcUsJ0ru7fRtW74WcTS0rbLcehoFB+rN1GRi2hbLcFaYs4PwVA5diLeAJH0gszv3x4Hr/S87MfbKQ==} + engines: {node: '>=18.0.0'} '@azure/abort-controller@2.1.2': resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} @@ -1500,8 +1497,8 @@ packages: resolution: {integrity: sha512-J4FYAqakGXcbfeZjwjMzjNcpcH4E+JtEBv+xcV1yL0Ydn/6wbQfeFKTCHh9wttAi0lmajHw7yBbHPRG+YHckZQ==} engines: {node: '>=18.0.0'} - '@azure/identity@4.5.0': - resolution: {integrity: sha512-EknvVmtBuSIic47xkOqyNabAme0RYTw52BTMz8eBgU1ysTyMrD1uOoM+JdS0J/4Yfp98IBT3osqq3BfwSaNaGQ==} + '@azure/identity@4.6.0': + resolution: {integrity: sha512-ANpO1iAvcZmpD4QY7/kaE/P2n66pRXsDp3nMUC6Ow3c9KfXOZF7qMU9VgqPw8m7adP7TVIbVyrCEmD9cth3KQQ==} engines: {node: '>=18.0.0'} '@azure/logger@1.1.4': @@ -1514,14 +1511,18 @@ packages: peerDependencies: '@playwright/test': ^1.43.1 - '@azure/msal-browser@3.28.0': - resolution: {integrity: sha512-1c1qUF6vB52mWlyoMem4xR1gdwiQWYEQB2uhDkbAL4wVJr8WmAcXybc1Qs33y19N4BdPI8/DHI7rPE8L5jMtWw==} + '@azure/msal-browser@4.0.2': + resolution: {integrity: sha512-bq6PasUpJgBSOSMeSlh8gXh4LZGgAaPoJFNcu5u0zxwueh+I8NpMb9oxlCfS/8CJHyXUhTUAMLSnvThemNdyQw==} engines: {node: '>=0.8.0'} '@azure/msal-common@14.16.0': resolution: {integrity: sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==} engines: {node: '>=0.8.0'} + '@azure/msal-common@15.0.2': + resolution: {integrity: sha512-RQHmI5vOMYLNSO0ER7d/O9TojWWEn4m0YtWbL8mZthkKGQI7ALn5ONHUVTUSxSVYwGYdHGNrwiHAzQhboqwZzQ==} + engines: {node: '>=0.8.0'} + '@azure/msal-node@2.16.2': resolution: {integrity: sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==} engines: {node: '>=16'} @@ -1537,10 +1538,6 @@ packages: resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.3': - resolution: {integrity: sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==} - engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.5': resolution: {integrity: sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==} engines: {node: '>=6.9.0'} @@ -1549,17 +1546,13 @@ packages: resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} engines: {node: '>=6.9.0'} - '@babel/eslint-parser@7.25.9': - resolution: {integrity: sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ==} + '@babel/eslint-parser@7.26.5': + resolution: {integrity: sha512-Kkm8C8uxI842AwQADxl0GbcG1rupELYLShazYEZO/2DYjhyWXJIOUVOE3tBYm6JXzUCNJOZEzqc4rCW/jsEQYQ==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} peerDependencies: '@babel/core': ^7.11.0 eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 - '@babel/generator@7.26.3': - resolution: {integrity: sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==} - engines: {node: '>=6.9.0'} - '@babel/generator@7.26.5': resolution: {integrity: sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==} engines: {node: '>=6.9.0'} @@ -1568,10 +1561,6 @@ packages: resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.25.9': - resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.26.5': resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} engines: {node: '>=6.9.0'} @@ -1615,10 +1604,6 @@ packages: resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.25.9': - resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} - engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.26.5': resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} engines: {node: '>=6.9.0'} @@ -1629,12 +1614,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.25.9': - resolution: {integrity: sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.26.5': resolution: {integrity: sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==} engines: {node: '>=6.9.0'} @@ -1661,21 +1640,16 @@ packages: resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.26.0': - resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + '@babel/helpers@7.26.7': + resolution: {integrity: sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==} engines: {node: '>=6.9.0'} '@babel/highlight@7.25.9': resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.26.3': - resolution: {integrity: sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/parser@7.26.5': - resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} + '@babel/parser@7.26.7': + resolution: {integrity: sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==} engines: {node: '>=6.0.0'} hasBin: true @@ -2005,8 +1979,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-flow-strip-types@7.25.9': - resolution: {integrity: sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==} + '@babel/plugin-transform-flow-strip-types@7.26.5': + resolution: {integrity: sha512-eGK26RsbIkYUns3Y8qKl362juDDYK+wEdPGHGrhzUl6CewZFo55VZ7hg+CyMFU4dd5QQakBN86nBMpRsFpRvbQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -2083,12 +2057,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-nullish-coalescing-operator@7.25.9': - resolution: {integrity: sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-nullish-coalescing-operator@7.26.6': resolution: {integrity: sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==} engines: {node: '>=6.9.0'} @@ -2233,14 +2201,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typeof-symbol@7.25.9': - resolution: {integrity: sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==} + '@babel/plugin-transform-typeof-symbol@7.26.7': + resolution: {integrity: sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.26.3': - resolution: {integrity: sha512-6+5hpdr6mETwSKjmJUdYw0EIkATiQhnELWlE3kJFBwSg/BGIVwVaVbX+gOXBCdc7Ln1RXZxyWGecIXhUfnl7oA==} + '@babel/plugin-transform-typescript@7.26.7': + resolution: {integrity: sha512-5cJurntg+AT+cgelGP9Bt788DKiAw9gIMSMU2NJrLAilnj0m8WZWUNZPSLOmadYsujHutpgElO+50foX+ib/Wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -2269,8 +2237,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.26.0': - resolution: {integrity: sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==} + '@babel/preset-env@7.26.7': + resolution: {integrity: sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -2304,29 +2272,28 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.26.0': - resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + '@babel/runtime@7.26.7': + resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==} engines: {node: '>=6.9.0'} '@babel/template@7.25.9': resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.26.4': - resolution: {integrity: sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==} + '@babel/traverse@7.26.7': + resolution: {integrity: sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.26.5': - resolution: {integrity: sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==} + '@babel/types@7.26.7': + resolution: {integrity: sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==} engines: {node: '>=6.9.0'} - '@babel/types@7.26.3': - resolution: {integrity: sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==} - engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@babel/types@7.26.5': - resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} - engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} '@calcom/embed-core@1.5.1': resolution: {integrity: sha512-wykzh1GKj5xhGxDJeCRJ7OulAgn9GVMYD/mmOBbvn06c3m9Lqoqn09E5kJ+DY+aokUncQPcstNsdiHsURjMuVw==} @@ -2347,8 +2314,8 @@ packages: react: '>=18' react-dom: '>=18' - '@changesets/apply-release-plan@7.0.7': - resolution: {integrity: sha512-qnPOcmmmnD0MfMg9DjU1/onORFyRpDXkMMl2IJg9mECY6RnxL3wN0TCCc92b2sXt1jt8DgjAUUsZYGUGTdYIXA==} + '@changesets/apply-release-plan@7.0.8': + resolution: {integrity: sha512-qjMUj4DYQ1Z6qHawsn7S71SujrExJ+nceyKKyI9iB+M5p9lCL55afuEd6uLBPRpLGWQwkwvWegDHtwHJb1UjpA==} '@changesets/assemble-release-plan@6.0.5': resolution: {integrity: sha512-IgvBWLNKZd6k4t72MBTBK3nkygi0j3t3zdC1zrfusYo0KpdsvnDjrMM9vPnTCLCMlfNs55jRL4gIMybxa64FCQ==} @@ -2477,8 +2444,8 @@ packages: '@docsearch/css@3.8.0': resolution: {integrity: sha512-pieeipSOW4sQ0+bE5UFC51AOZp9NGxg89wAlZ1BAQFaiRAGK1IKUaPQ0UGZeNctJXyqZ1UvBtOQh2HH+U5GtmA==} - '@docsearch/css@3.8.2': - resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} + '@docsearch/css@3.8.3': + resolution: {integrity: sha512-1nELpMV40JDLJ6rpVVFX48R1jsBFIQ6RnEQDsLFGmzOjPWTOMlZqUcXcvRx8VmYV/TqnS1l784Ofz+ZEb+wEOQ==} '@docsearch/react@3.8.0': resolution: {integrity: sha512-WnFK720+iwTVt94CxY3u+FgX6exb3BfN5kE9xUY6uuAH/9W/UFboBZFLlrw/zxFRHoHZCOXRtOylsXF+6LHI+Q==} @@ -3110,53 +3077,53 @@ packages: resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} engines: {node: '>=0.10.0'} - '@expo/cli@0.22.5': - resolution: {integrity: sha512-A2wYKtcBbEEyRUAyUeMDd356UROo1xaMl7ZaZC5tQOdIhvWKelRd4f3QCaI56D9B4EMWLg9pVuPVbAMz8zJ4+A==} + '@expo/cli@0.22.11': + resolution: {integrity: sha512-D5Vl7IBLi53WmL57NAFYB1mIqlMQxDIZVzbi/FTpo5a3oIHELKr0ElTKeOLf1f1/Y3FA7cxgphoawdA0+O1JWQ==} hasBin: true '@expo/code-signing-certificates@0.0.5': resolution: {integrity: sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw==} - '@expo/config-plugins@9.0.13': - resolution: {integrity: sha512-9mSjuMoCijA0O4JONJwWXg+xaD4tVeVv7pXWQovnQGxorgMNgygOGEzGi9GXFMki8FJ1Zlt2gyXrcPFXiId7Hw==} + '@expo/config-plugins@9.0.14': + resolution: {integrity: sha512-Lx1ebV95rTFKKQmbu4wMPLz65rKn7mqSpfANdCx+KwRxuLY2JQls8V4h3lQjG6dW8NWf9qV5QaEFAgNB6VMyOQ==} - '@expo/config-types@52.0.2': - resolution: {integrity: sha512-4hYwnaCxOLlXXF1TE17RY+GU1CyBqzRx7s13VUDhU1PQ8Zr9/kzGoJI0McmfayncO9RIeSqeDWO6dELZWk/0uw==} + '@expo/config-types@52.0.3': + resolution: {integrity: sha512-muxvuARmbysH5OGaiBRlh1Y6vfdmL56JtpXxB+y2Hfhu0ezG1U4FjZYBIacthckZPvnDCcP3xIu1R+eTo7/QFA==} - '@expo/config@10.0.7': - resolution: {integrity: sha512-fS9xuxH3U9tuiXofwxrmsan8TfzlDXgPiX38SDMkq/AQctmRtWllD8GNHRIk9Bdz3vODeBv7vRVGKXPBYG72cQ==} + '@expo/config@10.0.8': + resolution: {integrity: sha512-RaKwi8e6PbkMilRexdsxObLMdQwxhY6mlgel+l/eW+IfIw8HEydSU0ERlzYUjlGJxHLHUXe4rC2vw8FEvaowyQ==} '@expo/devcert@1.1.4': resolution: {integrity: sha512-fqBODr8c72+gBSX5Ty3SIzaY4bXainlpab78+vEYEKL3fXmsOswMLf0+KE36mUEAa36BYabX7K3EiXOXX5OPMw==} - '@expo/env@0.4.0': - resolution: {integrity: sha512-g2JYFqck3xKIwJyK+8LxZ2ENZPWtRgjFWpeht9abnKgzXVXBeSNECFBkg+WQjQocSIdxXhEWM6hz4ZAe7Tc4ng==} + '@expo/env@0.4.1': + resolution: {integrity: sha512-oDtbO3i9yXD1nx93acWiPTWGljJ3vABn35x1NAbqtQ2JL6mFOcRcArt1dwi4imZyLnG4VCcjabT9irj+LgYntw==} - '@expo/fingerprint@0.11.3': - resolution: {integrity: sha512-9lgXmcIePvZ7Wef63XtvuN3HfCUevF4E4tQPdEbH9/dUWwpOvvwQ3KT4OJ9jdh8JJ3nTdO9eDQ/8k8xr1aQ5Kg==} + '@expo/fingerprint@0.11.7': + resolution: {integrity: sha512-2rfYVS4nqWmOPQk+AL5GPfPSawbqqmI5mL++bxAhWADt+d+fjoQYfIrGtjZxQ30f9o/a1PrRPVSuh2j09+diVg==} hasBin: true - '@expo/image-utils@0.6.3': - resolution: {integrity: sha512-v/JbCKBrHeudxn1gN1TgfPE/pWJSlLPrl29uXJBgrJFQVkViQvUHQNDhaS+UEa9wYI5HHh7XYmtzAehyG4L+GA==} + '@expo/image-utils@0.6.4': + resolution: {integrity: sha512-L++1PBzSvf5iYc6UHJ8Db8GcYNkfLDw+a+zqEFBQ3xqRXP/muxb/O7wuiMFlXrj/cfkx4e0U+z1a4ceV0A7S7Q==} - '@expo/json-file@9.0.0': - resolution: {integrity: sha512-M+55xFVrFzDcgMDf+52lPDLjKB5xwRfStWlv/b/Vu2OLgxGZLWpxoPYjlRoHqxjPbCQIi2ZCbobK+0KuNhsELg==} + '@expo/json-file@9.0.1': + resolution: {integrity: sha512-ZVPhbbEBEwafPCJ0+kI25O2Iivt3XKHEKAADCml1q2cmOIbQnKgLyn8DpOJXqWEyRQr/VWS+hflBh8DU2YFSqg==} - '@expo/metro-config@0.19.7': - resolution: {integrity: sha512-6Ti05d6AyvXstMpaRGh2EsdGSJzmOh9ju3gMmcjxckn/cimNL39qRQSrnqYc0R/DEZiRFL7N9mVE/0uG668ojw==} + '@expo/metro-config@0.19.9': + resolution: {integrity: sha512-JAsLWhFQqwLH0KsI4OMbPXsKFji5KJEmsi+/02Sz1GCT17YrjRmv1fZ91regUS/FUH2Y/PDAE/+2ulrTgMeG7A==} - '@expo/osascript@2.1.4': - resolution: {integrity: sha512-LcPjxJ5FOFpqPORm+5MRLV0CuYWMthJYV6eerF+lQVXKlvgSn3EOqaHC3Vf3H+vmB0f6G4kdvvFtg40vG4bIhA==} + '@expo/osascript@2.1.5': + resolution: {integrity: sha512-Cp7YF7msGiTAIbFdzNovwHBfecdMLVL5XzSqq4xQz72ALFCQ3uSIUXRph1QV2r61ugH7Yem0gY8yi7RcDlI4qg==} engines: {node: '>=12'} - '@expo/package-manager@1.7.0': - resolution: {integrity: sha512-yWn5TIjd42wLHZjNtdZkvCkcxqUGxlI4YHb+bQmgm3tWZ8aBHnLhPb0rgU8+hVHCofmRvVUXfVZv8Uh+kkLXgw==} + '@expo/package-manager@1.7.1': + resolution: {integrity: sha512-DKbELrTOdl7U3KT0C07Aka9P+sUP3LL+1UTKf1KmLx2x2gPH1IC+c68N7iQlwNt+yA37qIw6/vKoqyTGu5EL9g==} - '@expo/plist@0.2.0': - resolution: {integrity: sha512-F/IZJQaf8OIVnVA6XWUeMPC3OH6MV00Wxf0WC0JhTQht2QgjyHUa3U5Gs3vRtDq8tXNsZneOQRDVwpaOnd4zTQ==} + '@expo/plist@0.2.1': + resolution: {integrity: sha512-9TaXGuNxa0LQwHQn4rYiU6YaERv6dPnQgsdKWq2rKKTr6LWOtGNQCi/yOk/HBLeZSxBm59APT5/6x60uRvr0Mg==} - '@expo/prebuild-config@8.0.24': - resolution: {integrity: sha512-zxbKW+oHn0/QwKaShjbxD7tv+5WtK2+C5ZJTHztyXJXrBP6BOL5dK4lP2djQVzpHYU1p6ZzKFvp9d1bW/+S32Q==} + '@expo/prebuild-config@8.0.25': + resolution: {integrity: sha512-xYHV8eiydZEDedf2AGaOFRFwcGlaSzrqQH94dwX42urNCU03FO0RUb7yPp4nkb7WNFg5Ov6PDsV7ES+YwzNgYQ==} '@expo/rudder-sdk-node@1.1.1': resolution: {integrity: sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ==} @@ -3203,8 +3170,8 @@ packages: '@formatjs/fast-memoize@2.2.6': resolution: {integrity: sha512-luIXeE2LJbQnnzotY1f2U2m7xuQNj2DA8Vq4ce1BY9ebRZaoPB1+8eZ6nXpLzsxuW5spQxr7LdCg+CApZwkqkw==} - '@formatjs/icu-messageformat-parser@2.9.8': - resolution: {integrity: sha512-hZlLNI3+Lev8IAXuwehLoN7QTKqbx3XXwFW1jh0AdIA9XJdzn9Uzr+2LLBspPm/PX0+NLIfykj/8IKxQqHUcUQ==} + '@formatjs/icu-messageformat-parser@2.11.0': + resolution: {integrity: sha512-Hp81uTjjdTk3FLh/dggU5NK7EIsVWc5/ZDWrIldmf2rBuPejuZ13CZ/wpVE2SToyi4EiroPTQ1XJcJuZFIxTtw==} '@formatjs/icu-skeleton-parser@1.8.12': resolution: {integrity: sha512-QRAY2jC1BomFQHYDMcZtClqHR55EEnB96V7Xbk/UiBodsuFc5kujybzt87+qj1KqmJozFhk6n4KiT1HKwAkcfg==} @@ -3643,8 +3610,8 @@ packages: '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} - '@noble/hashes@1.7.0': - resolution: {integrity: sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==} + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} engines: {node: ^14.21.3 || >=16} '@nodelib/fs.scandir@2.1.5': @@ -3683,8 +3650,8 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} - '@opentelemetry/context-async-hooks@1.30.0': - resolution: {integrity: sha512-roCetrG/cz0r/gugQm/jFo75UxblVvHaNSRoR0kSSRSzXFAiIBqFCZuH458BHBNRtRe+0yJdIJ21L9t94bw7+g==} + '@opentelemetry/context-async-hooks@1.30.1': + resolution: {integrity: sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -3695,8 +3662,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@1.30.0': - resolution: {integrity: sha512-Q/3u/K73KUjTCnFUP97ZY+pBjQ1kPEgjOfXj/bJl8zW7GbXdkw6cwuyZk6ZTXkVgCBsYRYUzx4fvYK1jxdb9MA==} + '@opentelemetry/core@1.30.1': + resolution: {integrity: sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -3867,8 +3834,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/resources@1.30.0': - resolution: {integrity: sha512-5mGMjL0Uld/99t7/pcd7CuVtJbkARckLVuiOX84nO8RtLtIz0/J6EOHM2TGvPZ6F4K+XjUq13gMx14w80SVCQg==} + '@opentelemetry/resources@1.30.1': + resolution: {integrity: sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -3879,14 +3846,14 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.4.0 <1.10.0' - '@opentelemetry/sdk-metrics@1.30.0': - resolution: {integrity: sha512-5kcj6APyRMvv6dEIP5plz2qfJAD4OMipBRT11u/pa1a68rHKI2Ln+iXVkAGKgx8o7CXbD7FdPypTUY88ZQgP4Q==} + '@opentelemetry/sdk-metrics@1.30.1': + resolution: {integrity: sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace-base@1.30.0': - resolution: {integrity: sha512-RKQDaDIkV7PwizmHw+rE/FgfB2a6MBx+AEVVlAHXRG1YYxLiBpPX2KhmoB99R5vA4b72iJrjle68NDWnbrE9Dg==} + '@opentelemetry/sdk-trace-base@1.30.1': + resolution: {integrity: sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -4013,8 +3980,8 @@ packages: '@prisma/get-platform@6.0.1': resolution: {integrity: sha512-zspC9vlxAqx4E6epMPMLLBMED2VD8axDe8sPnquZ8GOsn6tiacWK0oxrGK4UAHYzYUVuMVUApJbdXB2dFpLhvg==} - '@prisma/instrumentation@5.19.1': - resolution: {integrity: sha512-VLnzMQq7CWroL5AeaW0Py2huiNKeoMfCH3SUxstdzPrlWQi6UQ9UrfcbUkNHlVFqOMacqy8X/8YtE0kuKDpD9w==} + '@prisma/instrumentation@5.22.0': + resolution: {integrity: sha512-LxccF392NN37ISGxIurUljZSh1YWnphO34V5a0+T7FVQG2u9bhAXRTJpgmQ3483woVhkraQZFF7cbRrpbw/F4Q==} '@prisma/internals@5.0.0': resolution: {integrity: sha512-VGWyFk6QlSBXT8z65Alq5F3o9E8IiTtaBoa3rmKkGpZjUk85kJy3jZz4xkRv53TaeghGE5rWfwkfak26KtY5yQ==} @@ -4499,15 +4466,17 @@ packages: '@radix-ui/rect@1.1.0': resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} - '@react-aria/focus@3.19.0': - resolution: {integrity: sha512-hPF9EXoUQeQl1Y21/rbV2H4FdUR2v+4/I0/vB+8U3bT1CJ+1AFj1hc/rqx2DqEwDlEwOHN+E4+mRahQmlybq0A==} + '@react-aria/focus@3.19.1': + resolution: {integrity: sha512-bix9Bu1Ue7RPcYmjwcjhB14BMu2qzfJ3tMQLqDc9pweJA66nOw8DThy3IfVr8Z7j2PHktOLf9kcbiZpydKHqzg==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-aria/interactions@3.22.5': - resolution: {integrity: sha512-kMwiAD9E0TQp+XNnOs13yVJghiy8ET8L0cbkeuTgNI96sOAp/63EJ1FSrDf17iD8sdjt41LafwX/dKXW9nCcLQ==} + '@react-aria/interactions@3.23.0': + resolution: {integrity: sha512-0qR1atBIWrb7FzQ+Tmr3s8uH5mQdyRH78n0krYaG8tng9+u1JlSi8DGRSaC9ezKyNB84m7vHT207xnHXGeJ3Fg==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 '@react-aria/ssr@3.9.7': resolution: {integrity: sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==} @@ -4515,10 +4484,11 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-aria/utils@3.26.0': - resolution: {integrity: sha512-LkZouGSjjQ0rEqo4XJosS4L3YC/zzQkfRM3KoqK6fUOmUJ9t0jQ09WjiF+uOoG9u+p30AVg3TrZRUWmoTS+koQ==} + '@react-aria/utils@3.27.0': + resolution: {integrity: sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 '@react-email/body@0.0.11': resolution: {integrity: sha512-ZSD2SxVSgUjHGrB0Wi+4tu3MEpB4fYSbezsFNEJk2xCWDBkFiOeEsjTmR5dvi+CxTK691hQTQlHv0XWuP7ENTg==} @@ -4692,16 +4662,16 @@ packages: resolution: {integrity: sha512-1XmRhqQchN+pXPKEKYdpJlwESxVomJOxtEnIkbo7GAlaN2sym84fHEGDXAjLilih5GVPpcpSmFzTy8jx3LtaFg==} engines: {node: '>=18'} - '@react-native/assets-registry@0.76.5': - resolution: {integrity: sha512-MN5dasWo37MirVcKWuysRkRr4BjNc81SXwUtJYstwbn8oEkfnwR9DaqdDTo/hHOnTdhafffLIa2xOOHcjDIGEw==} + '@react-native/assets-registry@0.76.6': + resolution: {integrity: sha512-YI8HoReYiIwdFQs+k9Q9qpFTnsyYikZxgs/UVtVbhKixXDQF6F9LLvj2naOx4cfV+RGybNKxwmDl1vUok/dRFQ==} engines: {node: '>=18'} '@react-native/babel-plugin-codegen@0.74.87': resolution: {integrity: sha512-+vJYpMnENFrwtgvDfUj+CtVJRJuUnzAUYT0/Pb68Sq9RfcZ5xdcCuUgyf7JO+akW2VTBoJY427wkcxU30qrWWw==} engines: {node: '>=18'} - '@react-native/babel-plugin-codegen@0.76.5': - resolution: {integrity: sha512-xe7HSQGop4bnOLMaXt0aU+rIatMNEQbz242SDl8V9vx5oOTI0VbZV9yLy6yBc6poUlYbcboF20YVjoRsxX4yww==} + '@react-native/babel-plugin-codegen@0.76.6': + resolution: {integrity: sha512-yFC9I/aDBOBz3ZMlqKn2NY/mDUtCksUNZ7AQmBiTAeVTUP0ujEjE0hTOx5Qd+kok7A7hwZEX87HdSgjiJZfr5g==} engines: {node: '>=18'} '@react-native/babel-preset@0.74.87': @@ -4710,8 +4680,8 @@ packages: peerDependencies: '@babel/core': '*' - '@react-native/babel-preset@0.76.5': - resolution: {integrity: sha512-1Nu5Um4EogOdppBLI4pfupkteTjWfmI0hqW8ezWTg7Bezw0FtBj8yS8UYVd3wTnDFT9A5mA2VNoNUqomJnvj2A==} + '@react-native/babel-preset@0.76.6': + resolution: {integrity: sha512-ojlVWY6S/VE/nb9hIRetPMTsW9ZmGb2R3dnToEXAtQQDz41eHMHXbkw/k2h0THp6qhas25ruNvn3N5n2o+lBzg==} engines: {node: '>=18'} peerDependencies: '@babel/core': '*' @@ -4722,8 +4692,8 @@ packages: peerDependencies: '@babel/preset-env': ^7.1.6 - '@react-native/codegen@0.76.5': - resolution: {integrity: sha512-FoZ9VRQ5MpgtDAnVo1rT9nNRfjnWpE40o1GeJSDlpUMttd36bVXvsDm8W/NhX8BKTWXSX+CPQJsRcvN1UPYGKg==} + '@react-native/codegen@0.76.6': + resolution: {integrity: sha512-BABb3e5G/+hyQYEYi0AODWh2km2d8ERoASZr6Hv90pVXdUHRYR+yxCatX7vSd9rnDUYndqRTzD0hZWAucPNAKg==} engines: {node: '>=18'} peerDependencies: '@babel/preset-env': ^7.1.6 @@ -4732,8 +4702,8 @@ packages: resolution: {integrity: sha512-EgJG9lSr8x3X67dHQKQvU6EkO+3ksVlJHYIVv6U/AmW9dN80BEFxgYbSJ7icXS4wri7m4kHdgeq2PQ7/3vvrTQ==} engines: {node: '>=18'} - '@react-native/community-cli-plugin@0.76.5': - resolution: {integrity: sha512-3MKMnlU0cZOWlMhz5UG6WqACJiWUrE3XwBEumzbMmZw3Iw3h+fIsn+7kLLE5EhzqLt0hg5Y4cgYFi4kOaNgq+g==} + '@react-native/community-cli-plugin@0.76.6': + resolution: {integrity: sha512-nETlc/+U5cESVluzzgN0OcVfcoMijGBaDWzOaJhoYUodcuqnqtu75XsSEc7yzlYjwNQG+vF83mu9CQGezruNMA==} engines: {node: '>=18'} peerDependencies: '@react-native-community/cli-server-api': '*' @@ -4745,32 +4715,32 @@ packages: resolution: {integrity: sha512-MN95DJLYTv4EqJc+9JajA3AJZSBYJz2QEJ3uWlHrOky2vKrbbRVaW1ityTmaZa2OXIvNc6CZwSRSE7xCoHbXhQ==} engines: {node: '>=18'} - '@react-native/debugger-frontend@0.76.5': - resolution: {integrity: sha512-5gtsLfBaSoa9WP8ToDb/8NnDBLZjv4sybQQj7rDKytKOdsXm3Pr2y4D7x7GQQtP1ZQRqzU0X0OZrhRz9xNnOqA==} + '@react-native/debugger-frontend@0.76.6': + resolution: {integrity: sha512-kP97xMQjiANi5/lmf8MakS7d8FTJl+BqYHQMqyvNiY+eeWyKnhqW2GL2v3eEUBAuyPBgJGivuuO4RvjZujduJg==} engines: {node: '>=18'} '@react-native/dev-middleware@0.74.87': resolution: {integrity: sha512-7TmZ3hTHwooYgIHqc/z87BMe1ryrIqAUi+AF7vsD+EHCGxHFdMjSpf1BZ2SUPXuLnF2cTiTfV2RwhbPzx0tYIA==} engines: {node: '>=18'} - '@react-native/dev-middleware@0.76.5': - resolution: {integrity: sha512-f8eimsxpkvMgJia7POKoUu9uqjGF6KgkxX4zqr/a6eoR1qdEAWUd6PonSAqtag3PAqvEaJpB99gLH2ZJI1nDGg==} + '@react-native/dev-middleware@0.76.6': + resolution: {integrity: sha512-1bAyd2/X48Nzb45s5l2omM75vy764odx/UnDs4sJfFCuK+cupU4nRPgl0XWIqgdM/2+fbQ3E4QsVS/WIKTFxvQ==} engines: {node: '>=18'} '@react-native/gradle-plugin@0.74.87': resolution: {integrity: sha512-T+VX0N1qP+U9V4oAtn7FTX7pfsoVkd1ocyw9swYXgJqU2fK7hC9famW7b3s3ZiufPGPr1VPJe2TVGtSopBjL6A==} engines: {node: '>=18'} - '@react-native/gradle-plugin@0.76.5': - resolution: {integrity: sha512-7KSyD0g0KhbngITduC8OABn0MAlJfwjIdze7nA4Oe1q3R7qmAv+wQzW+UEXvPah8m1WqFjYTkQwz/4mK3XrQGw==} + '@react-native/gradle-plugin@0.76.6': + resolution: {integrity: sha512-sDzpf4eiynryoS6bpYCweGoxSmWgCSx9lzBoxIIW+S6siyGiTaffzZHWCm8mIn9UZsSPlEO37q62ggnR9Zu/OA==} engines: {node: '>=18'} '@react-native/js-polyfills@0.74.87': resolution: {integrity: sha512-M5Evdn76CuVEF0GsaXiGi95CBZ4IWubHqwXxV9vG9CC9kq0PSkoM2Pn7Lx7dgyp4vT7ccJ8a3IwHbe+5KJRnpw==} engines: {node: '>=18'} - '@react-native/js-polyfills@0.76.5': - resolution: {integrity: sha512-ggM8tcKTcaqyKQcXMIvcB0vVfqr9ZRhWVxWIdiFO1mPvJyS6n+a+lLGkgQAyO8pfH0R1qw6K9D0nqbbDo865WQ==} + '@react-native/js-polyfills@0.76.6': + resolution: {integrity: sha512-cDD7FynxWYxHkErZzAJtzPGhJ13JdOgL+R0riTh0hCovOfIUz9ItffdLQv2nx48lnvMTQ+HZXMnGOZnsFCNzQw==} engines: {node: '>=18'} '@react-native/metro-babel-transformer@0.74.87': @@ -4779,8 +4749,8 @@ packages: peerDependencies: '@babel/core': '*' - '@react-native/metro-babel-transformer@0.76.5': - resolution: {integrity: sha512-Cm9G5Sg5BDty3/MKa3vbCAJtT3YHhlEaPlQALLykju7qBS+pHZV9bE9hocfyyvc5N/osTIGWxG5YOfqTeMu1oQ==} + '@react-native/metro-babel-transformer@0.76.6': + resolution: {integrity: sha512-xSBi9jPliThu5HRSJvluqUlDOLLEmf34zY/U7RDDjEbZqC0ufPcPS7c5XsSg0GDPiXc7lgjBVesPZsKFkoIBgA==} engines: {node: '>=18'} peerDependencies: '@babel/core': '*' @@ -4788,8 +4758,8 @@ packages: '@react-native/normalize-colors@0.74.87': resolution: {integrity: sha512-Xh7Nyk/MPefkb0Itl5Z+3oOobeG9lfLb7ZOY2DKpFnoCE1TzBmib9vMNdFaLdSxLIP+Ec6icgKtdzYg8QUPYzA==} - '@react-native/normalize-colors@0.76.5': - resolution: {integrity: sha512-6QRLEok1r55gLqj+94mEWUENuU5A6wsr2OoXpyq/CgQ7THWowbHtru/kRGRr6o3AQXrVnZheR60JNgFcpNYIug==} + '@react-native/normalize-colors@0.76.6': + resolution: {integrity: sha512-1n4udXH2Cla31iA/8eLRdhFHpYUYK1NKWCn4m1Sr9L4SarWKAYuRFliK1fcLvPPALCFoFlWvn8I0ekdUOHMzDQ==} '@react-native/virtualized-lists@0.74.87': resolution: {integrity: sha512-lsGxoFMb0lyK/MiplNKJpD+A1EoEUumkLrCjH4Ht+ZlG8S0BfCxmskLZ6qXn3BiDSkLjfjI/qyZ3pnxNBvkXpQ==} @@ -4802,8 +4772,8 @@ packages: '@types/react': optional: true - '@react-native/virtualized-lists@0.76.5': - resolution: {integrity: sha512-M/fW1fTwxrHbcx0OiVOIxzG6rKC0j9cR9Csf80o77y1Xry0yrNPpAlf8D1ev3LvHsiAUiRNFlauoPtodrs2J1A==} + '@react-native/virtualized-lists@0.76.6': + resolution: {integrity: sha512-0HUWVwJbRq1BWFOu11eOWGTSmK9nMHhoMPyoI27wyWcl/nqUx7HOxMbRVq0DsTCyATSMPeF+vZ6o1REapcNWKw==} engines: {node: '>=18'} peerDependencies: '@types/react': ^18.2.6 @@ -4818,8 +4788,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-types/shared@3.26.0': - resolution: {integrity: sha512-6FuPqvhmjjlpEDLTiYx29IJCbCNWPlsyO+ZUmCUXzhUv2ttShOXfw8CmeHWHftT/b2KweAWuzqSlfeXPR76jpw==} + '@react-types/shared@3.27.0': + resolution: {integrity: sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -4855,11 +4825,11 @@ packages: '@redocly/ajv@8.11.2': resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} - '@redocly/config@0.17.1': - resolution: {integrity: sha512-CEmvaJuG7pm2ylQg53emPmtgm4nW2nxBgwXzbVEHpGas/lGnMyN8Zlkgiz6rPw0unASg6VW3wlz27SOL5XFHYQ==} + '@redocly/config@0.20.3': + resolution: {integrity: sha512-Nyyv1Bj7GgYwj/l46O0nkH1GTKWbO3Ixe7KFcn021aZipkZd+z8Vlu1BwkhqtVgivcKaClaExtWU/lDHkjBzag==} - '@redocly/openapi-core@1.27.1': - resolution: {integrity: sha512-zQ47/A+Drk2Y75/af69MD3Oad4H9LxkUDzcm7XBkyLNDKIWQrDKDnS5476oDq77+zciymNxgMVtxxVXlnGS8kw==} + '@redocly/openapi-core@1.27.2': + resolution: {integrity: sha512-qVrDc27DHpeO2NRCMeRdb4299nijKQE3BY0wrA+WUHlOLScorIi/y7JzammLk22IaTvjR9Mv9aTAdjE1aUwJnA==} engines: {node: '>=14.19.0', npm: '>=7.0.0'} '@resvg/resvg-wasm@2.4.0': @@ -4901,193 +4871,98 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.30.1': - resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} + '@rollup/rollup-android-arm-eabi@4.32.1': + resolution: {integrity: sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.31.0': - resolution: {integrity: sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.30.1': - resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} + '@rollup/rollup-android-arm64@4.32.1': + resolution: {integrity: sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==} cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.31.0': - resolution: {integrity: sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.30.1': - resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} + '@rollup/rollup-darwin-arm64@4.32.1': + resolution: {integrity: sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.31.0': - resolution: {integrity: sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.30.1': - resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} + '@rollup/rollup-darwin-x64@4.32.1': + resolution: {integrity: sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==} cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.31.0': - resolution: {integrity: sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.30.1': - resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} + '@rollup/rollup-freebsd-arm64@4.32.1': + resolution: {integrity: sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.31.0': - resolution: {integrity: sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.30.1': - resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} + '@rollup/rollup-freebsd-x64@4.32.1': + resolution: {integrity: sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.31.0': - resolution: {integrity: sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.30.1': - resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} + '@rollup/rollup-linux-arm-gnueabihf@4.32.1': + resolution: {integrity: sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.31.0': - resolution: {integrity: sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw==} + '@rollup/rollup-linux-arm-musleabihf@4.32.1': + resolution: {integrity: sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.30.1': - resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.31.0': - resolution: {integrity: sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.30.1': - resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} + '@rollup/rollup-linux-arm64-gnu@4.32.1': + resolution: {integrity: sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.31.0': - resolution: {integrity: sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA==} + '@rollup/rollup-linux-arm64-musl@4.32.1': + resolution: {integrity: sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.30.1': - resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.31.0': - resolution: {integrity: sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-loongarch64-gnu@4.30.1': - resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.32.1': + resolution: {integrity: sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.31.0': - resolution: {integrity: sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': - resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.32.1': + resolution: {integrity: sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.31.0': - resolution: {integrity: sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.30.1': - resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} + '@rollup/rollup-linux-riscv64-gnu@4.32.1': + resolution: {integrity: sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.31.0': - resolution: {integrity: sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.30.1': - resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} + '@rollup/rollup-linux-s390x-gnu@4.32.1': + resolution: {integrity: sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.31.0': - resolution: {integrity: sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ==} - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.30.1': - resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} + '@rollup/rollup-linux-x64-gnu@4.32.1': + resolution: {integrity: sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.31.0': - resolution: {integrity: sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g==} + '@rollup/rollup-linux-x64-musl@4.32.1': + resolution: {integrity: sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.30.1': - resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-linux-x64-musl@4.31.0': - resolution: {integrity: sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-win32-arm64-msvc@4.30.1': - resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} + '@rollup/rollup-win32-arm64-msvc@4.32.1': + resolution: {integrity: sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.31.0': - resolution: {integrity: sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.30.1': - resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} + '@rollup/rollup-win32-ia32-msvc@4.32.1': + resolution: {integrity: sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.31.0': - resolution: {integrity: sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.30.1': - resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.31.0': - resolution: {integrity: sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw==} + '@rollup/rollup-win32-x64-msvc@4.32.1': + resolution: {integrity: sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==} cpu: [x64] os: [win32] @@ -5125,28 +5000,28 @@ packages: '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} - '@sentry-internal/browser-utils@8.45.1': - resolution: {integrity: sha512-sZwtP3zAzDsjUS7WkMW5VGbvSl7hGKTMc8gAJbpEsrybMxllIP13zzMRwpeFF11RnnvbrZ/FtAeX58Mvj0jahA==} + '@sentry-internal/browser-utils@8.52.0': + resolution: {integrity: sha512-ojFldpRpGrgacIQMbbMZeqLYetNJJ61n+Pz29FpggaIRrbkq84ocoy4FCy+9BuLo6ywgxtUFrjOXD9pPRcZtUA==} engines: {node: '>=14.18'} - '@sentry-internal/feedback@8.45.1': - resolution: {integrity: sha512-zCKptzki4SLnG+s8je8dgnppOKFjiiO4GVBc4fh7uL8zjNPBnxW8wK4SrPfAEKVYaHUzkKc5vixwUqcpmfLLGw==} + '@sentry-internal/feedback@8.52.0': + resolution: {integrity: sha512-r62Ufg4uGlvQsQ+nRSiq9y0ieVFRlZvUaoT/zMjmPuMg29O9rRAMdPJuiCpBH4++x8KJoJ9c2HBRizn6/3uc5Q==} engines: {node: '>=14.18'} - '@sentry-internal/replay-canvas@8.45.1': - resolution: {integrity: sha512-qiPg6XwOwkiMMe/8Qf3EhXCqkSlSnWLlorYngIbdkV2klbWjd7vKnqkFJF4PnaS0g7kkZr7nh+MdzpyLyuj2Mw==} + '@sentry-internal/replay-canvas@8.52.0': + resolution: {integrity: sha512-4ES2uCUb9yEO1cbg15UBqiYU/syQYj5GviI+TvYvnPX3I8K2mK941ZRqfHh2HpFMhMxLgfX4jDqDGizNhXWdqg==} engines: {node: '>=14.18'} - '@sentry-internal/replay@8.45.1': - resolution: {integrity: sha512-cOA9CodNSR9+hmICDaGIDUvWiwxQxeMHk/esbjB8uAW8HG4CYTG3CTYTZmlmou7DuysfMd4JNuFmDFBj+YU5/A==} + '@sentry-internal/replay@8.52.0': + resolution: {integrity: sha512-b4hQPni1G2tcV5XuAPSV4RTX3vqPXO9RfUXLuTBzOTNzBHDoj8nQv0yVvcysGy5tBAuVRo5ya5A+PG/iC6FA9A==} engines: {node: '>=14.18'} '@sentry/babel-plugin-component-annotate@2.22.7': resolution: {integrity: sha512-aa7XKgZMVl6l04NY+3X7BP7yvQ/s8scn8KzQfTLrGRarziTlMGrsCOBQtCNWXOPEbtxAIHpZ9dsrAn5EJSivOQ==} engines: {node: '>= 14'} - '@sentry/browser@8.45.1': - resolution: {integrity: sha512-/KvYhQSRg8m9kotG8h9FrfXCWRlebrvdfXKjj1oE9SyZ2LmR8Ze9AcEw1qzsBsa1F1D/a5FQbUJahSoLBkaQPA==} + '@sentry/browser@8.52.0': + resolution: {integrity: sha512-7JpJ9zpInozBzy61eJf/6RPHoKUCFcoFuKd9rttkN1gyY9xkU1cQK+x1f0deiIHnF9ydftmDtXW+kGFI/+xqtw==} engines: {node: '>=14.18'} '@sentry/bundler-plugin-core@2.22.7': @@ -5199,22 +5074,22 @@ packages: engines: {node: '>= 10'} hasBin: true - '@sentry/core@8.45.1': - resolution: {integrity: sha512-1fGmkr0paZshh38mD29c4CfkRkgFoYDaAGyDLoGYfTbEph/lU8RHB2HWzN93McqNdMEhl1DRRyqIasUZoPlqSA==} + '@sentry/core@8.52.0': + resolution: {integrity: sha512-2j3B7IKmseTKFm6AyheJ+RSgXqIsx+3blFSuxpkdvsEt60Lbzva2uDkCENfBDOclioo1kvHgsyuXLfWW4A+wwA==} engines: {node: '>=14.18'} - '@sentry/nextjs@8.45.1': - resolution: {integrity: sha512-EcUuQHGAk8cheuPfjRCXacjoD5ClDsB9qssYBVIvJozNEW5C+A0eJwPj/Qd1C05tdcJ6MMdMRv0NrTHdCN1v1A==} + '@sentry/nextjs@8.52.0': + resolution: {integrity: sha512-0tPt780x0XpX0QdedXYuesJ0Caqf8zMk9zAAhA8H6NdA94eJ7ztHwCgX7tOWkzNCnAozcfiC/mMNKIrkmmV0Gg==} engines: {node: '>=14.18'} peerDependencies: next: ^13.2.0 || ^14.0 || ^15.0.0-rc.0 - '@sentry/node@8.45.1': - resolution: {integrity: sha512-xvlXifM/FSOQdLAqQBuo04SiOh7RP8rRRr+c5G/YbBtgJA867Pve0X8JZK2BJpDZ3OrGvzXV1Ubnt9ao4rBfYA==} + '@sentry/node@8.52.0': + resolution: {integrity: sha512-k+GuWbLDcqIS766zF+TvVRnfbshNy6XKRd+HRNIEaeYa9Tm4LH/ZgCxz39AhFAQGWlZUHm/G2KkCt3AvgswJEg==} engines: {node: '>=14.18'} - '@sentry/opentelemetry@8.45.1': - resolution: {integrity: sha512-khnR5TS21ksITTXmXnpniRN7brlZS5RNNQuMZ9n3MYi/L1/s9LT73skNh1gder28OV6ZxGUgrTZ+1dtKqn9tig==} + '@sentry/opentelemetry@8.52.0': + resolution: {integrity: sha512-NbGPsCZmXQW/B2wlgIjeIn9bCsNKhZGenB0DLK8ZIS/eDOI0JRBjHWMpLS9H9Q0xqgFBjyp+Gwyu3nEZcN4lXw==} engines: {node: '>=14.18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -5223,14 +5098,14 @@ packages: '@opentelemetry/sdk-trace-base': ^1.29.0 '@opentelemetry/semantic-conventions': ^1.28.0 - '@sentry/react@8.45.1': - resolution: {integrity: sha512-ooMR2Lt4YSc5CMJklBKiL/mb+uidcZMpflxUvVUbtWMif+PqTUkfPRyICv6vs7muxK9i84Rr4iCkyZ4ns4H27A==} + '@sentry/react@8.52.0': + resolution: {integrity: sha512-7tbRuTFOgKLM2fM5/TjrfDWbkqOOsxkb2QldnAUHTAcArIx77DpsurAgEx5L9lqn+UAhuw1X2IGbzk9fVV/ZDQ==} engines: {node: '>=14.18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/vercel-edge@8.45.1': - resolution: {integrity: sha512-taCKf2ESNzpYRwuaikS17YA2upqzzPbemT+If+kOafgSUrLEq2YRYRvgoruyFLmjOGO9+634+HmGo7Nyn5CGjQ==} + '@sentry/vercel-edge@8.52.0': + resolution: {integrity: sha512-6YiAUp4zJeMhVr1hx06c4Ygjzu8b9Kfv/GkmYA0sYPP+wa9shSKdqfD3ROQdWSsI7RtY4qDd22H9/acxchLmlA==} engines: {node: '>=14.18'} '@sentry/webpack-plugin@2.22.7': @@ -5285,208 +5160,217 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@smithy/abort-controller@3.1.9': - resolution: {integrity: sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==} - engines: {node: '>=16.0.0'} + '@smithy/abort-controller@4.0.1': + resolution: {integrity: sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==} + engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader-native@3.0.1': - resolution: {integrity: sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==} + '@smithy/chunked-blob-reader-native@4.0.0': + resolution: {integrity: sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==} + engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader@4.0.0': - resolution: {integrity: sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==} + '@smithy/chunked-blob-reader@5.0.0': + resolution: {integrity: sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==} + engines: {node: '>=18.0.0'} - '@smithy/config-resolver@3.0.13': - resolution: {integrity: sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==} - engines: {node: '>=16.0.0'} + '@smithy/config-resolver@4.0.1': + resolution: {integrity: sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==} + engines: {node: '>=18.0.0'} - '@smithy/core@2.5.7': - resolution: {integrity: sha512-8olpW6mKCa0v+ibCjoCzgZHQx1SQmZuW/WkrdZo73wiTprTH6qhmskT60QLFdT9DRa5mXxjz89kQPZ7ZSsoqqg==} - engines: {node: '>=16.0.0'} + '@smithy/core@3.1.2': + resolution: {integrity: sha512-htwQXkbdF13uwwDevz9BEzL5ABK+1sJpVQXywwGSH973AVOvisHNfpcB8A8761G6XgHoS2kHPqc9DqHJ2gp+/Q==} + engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@3.2.8': - resolution: {integrity: sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==} - engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@4.0.1': + resolution: {integrity: sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==} + engines: {node: '>=18.0.0'} - '@smithy/eventstream-codec@3.1.10': - resolution: {integrity: sha512-323B8YckSbUH0nMIpXn7HZsAVKHYHFUODa8gG9cHo0ySvA1fr5iWaNT+iIL0UCqUzG6QPHA3BSsBtRQou4mMqQ==} + '@smithy/eventstream-codec@4.0.1': + resolution: {integrity: sha512-Q2bCAAR6zXNVtJgifsU16ZjKGqdw/DyecKNgIgi7dlqw04fqDu0mnq+JmGphqheypVc64CYq3azSuCpAdFk2+A==} + engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-browser@3.0.14': - resolution: {integrity: sha512-kbrt0vjOIihW3V7Cqj1SXQvAI5BR8SnyQYsandva0AOR307cXAc+IhPngxIPslxTLfxwDpNu0HzCAq6g42kCPg==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-browser@4.0.1': + resolution: {integrity: sha512-HbIybmz5rhNg+zxKiyVAnvdM3vkzjE6ccrJ620iPL8IXcJEntd3hnBl+ktMwIy12Te/kyrSbUb8UCdnUT4QEdA==} + engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-config-resolver@3.0.11': - resolution: {integrity: sha512-P2pnEp4n75O+QHjyO7cbw/vsw5l93K/8EWyjNCAAybYwUmj3M+hjSQZ9P5TVdUgEG08ueMAP5R4FkuSkElZ5tQ==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-config-resolver@4.0.1': + resolution: {integrity: sha512-lSipaiq3rmHguHa3QFF4YcCM3VJOrY9oq2sow3qlhFY+nBSTF/nrO82MUQRPrxHQXA58J5G1UnU2WuJfi465BA==} + engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-node@3.0.13': - resolution: {integrity: sha512-zqy/9iwbj8Wysmvi7Lq7XFLeDgjRpTbCfwBhJa8WbrylTAHiAu6oQTwdY7iu2lxigbc9YYr9vPv5SzYny5tCXQ==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-node@4.0.1': + resolution: {integrity: sha512-o4CoOI6oYGYJ4zXo34U8X9szDe3oGjmHgsMGiZM0j4vtNoT+h80TLnkUcrLZR3+E6HIxqW+G+9WHAVfl0GXK0Q==} + engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-universal@3.0.13': - resolution: {integrity: sha512-L1Ib66+gg9uTnqp/18Gz4MDpJPKRE44geOjOQ2SVc0eiaO5l255ADziATZgjQjqumC7yPtp1XnjHlF1srcwjKw==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-universal@4.0.1': + resolution: {integrity: sha512-Z94uZp0tGJuxds3iEAZBqGU2QiaBHP4YytLUjwZWx+oUeohCsLyUm33yp4MMBmhkuPqSbQCXq5hDet6JGUgHWA==} + engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@4.1.3': - resolution: {integrity: sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==} + '@smithy/fetch-http-handler@5.0.1': + resolution: {integrity: sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==} + engines: {node: '>=18.0.0'} - '@smithy/hash-blob-browser@3.1.10': - resolution: {integrity: sha512-elwslXOoNunmfS0fh55jHggyhccobFkexLYC1ZeZ1xP2BTSrcIBaHV2b4xUQOdctrSNOpMqOZH1r2XzWTEhyfA==} + '@smithy/hash-blob-browser@4.0.1': + resolution: {integrity: sha512-rkFIrQOKZGS6i1D3gKJ8skJ0RlXqDvb1IyAphksaFOMzkn3v3I1eJ8m7OkLj0jf1McP63rcCEoLlkAn/HjcTRw==} + engines: {node: '>=18.0.0'} - '@smithy/hash-node@3.0.11': - resolution: {integrity: sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==} - engines: {node: '>=16.0.0'} + '@smithy/hash-node@4.0.1': + resolution: {integrity: sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==} + engines: {node: '>=18.0.0'} - '@smithy/hash-stream-node@3.1.10': - resolution: {integrity: sha512-olomK/jZQ93OMayW1zfTHwcbwBdhcZOHsyWyiZ9h9IXvc1mCD/VuvzbLb3Gy/qNJwI4MANPLctTp2BucV2oU/Q==} - engines: {node: '>=16.0.0'} + '@smithy/hash-stream-node@4.0.1': + resolution: {integrity: sha512-U1rAE1fxmReCIr6D2o/4ROqAQX+GffZpyMt3d7njtGDr2pUNmAKRWa49gsNVhCh2vVAuf3wXzWwNr2YN8PAXIw==} + engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@3.0.11': - resolution: {integrity: sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==} + '@smithy/invalid-dependency@4.0.1': + resolution: {integrity: sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==} + engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/is-array-buffer@3.0.0': - resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} - engines: {node: '>=16.0.0'} + '@smithy/is-array-buffer@4.0.0': + resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} + engines: {node: '>=18.0.0'} - '@smithy/md5-js@3.0.11': - resolution: {integrity: sha512-3NM0L3i2Zm4bbgG6Ymi9NBcxXhryi3uE8fIfHJZIOfZVxOkGdjdgjR9A06SFIZCfnEIWKXZdm6Yq5/aPXFFhsQ==} + '@smithy/md5-js@4.0.1': + resolution: {integrity: sha512-HLZ647L27APi6zXkZlzSFZIjpo8po45YiyjMGJZM3gyDY8n7dPGdmxIIljLm4gPt/7rRvutLTTkYJpZVfG5r+A==} + engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@3.0.13': - resolution: {integrity: sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-content-length@4.0.1': + resolution: {integrity: sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==} + engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@3.2.8': - resolution: {integrity: sha512-OEJZKVUEhMOqMs3ktrTWp7UvvluMJEvD5XgQwRePSbDg1VvBaL8pX8mwPltFn6wk1GySbcVwwyldL8S+iqnrEQ==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@4.0.3': + resolution: {integrity: sha512-YdbmWhQF5kIxZjWqPIgboVfi8i5XgiYMM7GGKFMTvBei4XjNQfNv8sukT50ITvgnWKKKpOtp0C0h7qixLgb77Q==} + engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@3.0.34': - resolution: {integrity: sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@4.0.4': + resolution: {integrity: sha512-wmxyUBGHaYUqul0wZiset4M39SMtDBOtUr2KpDuftKNN74Do9Y36Go6Eqzj9tL0mIPpr31ulB5UUtxcsCeGXsQ==} + engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@3.0.11': - resolution: {integrity: sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-serde@4.0.2': + resolution: {integrity: sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==} + engines: {node: '>=18.0.0'} - '@smithy/middleware-stack@3.0.11': - resolution: {integrity: sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@4.0.1': + resolution: {integrity: sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==} + engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@3.1.12': - resolution: {integrity: sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==} - engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@4.0.1': + resolution: {integrity: sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==} + engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@3.3.3': - resolution: {integrity: sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==} - engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@4.0.2': + resolution: {integrity: sha512-X66H9aah9hisLLSnGuzRYba6vckuFtGE+a5DcHLliI/YlqKrGoxhisD5XbX44KyoeRzoNlGr94eTsMVHFAzPOw==} + engines: {node: '>=18.0.0'} - '@smithy/property-provider@3.1.11': - resolution: {integrity: sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==} - engines: {node: '>=16.0.0'} + '@smithy/property-provider@4.0.1': + resolution: {integrity: sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==} + engines: {node: '>=18.0.0'} - '@smithy/protocol-http@4.1.8': - resolution: {integrity: sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==} - engines: {node: '>=16.0.0'} + '@smithy/protocol-http@5.0.1': + resolution: {integrity: sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==} + engines: {node: '>=18.0.0'} - '@smithy/querystring-builder@3.0.11': - resolution: {integrity: sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@4.0.1': + resolution: {integrity: sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==} + engines: {node: '>=18.0.0'} - '@smithy/querystring-parser@3.0.11': - resolution: {integrity: sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@4.0.1': + resolution: {integrity: sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==} + engines: {node: '>=18.0.0'} - '@smithy/service-error-classification@3.0.11': - resolution: {integrity: sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==} - engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@4.0.1': + resolution: {integrity: sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==} + engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@3.1.12': - resolution: {integrity: sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==} - engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@4.0.1': + resolution: {integrity: sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==} + engines: {node: '>=18.0.0'} - '@smithy/signature-v4@4.2.4': - resolution: {integrity: sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==} - engines: {node: '>=16.0.0'} + '@smithy/signature-v4@5.0.1': + resolution: {integrity: sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==} + engines: {node: '>=18.0.0'} - '@smithy/smithy-client@3.7.0': - resolution: {integrity: sha512-9wYrjAZFlqWhgVo3C4y/9kpc68jgiSsKUnsFPzr/MSiRL93+QRDafGTfhhKAb2wsr69Ru87WTiqSfQusSmWipA==} - engines: {node: '>=16.0.0'} + '@smithy/smithy-client@4.1.3': + resolution: {integrity: sha512-A2Hz85pu8BJJaYFdX8yb1yocqigyqBzn+OVaVgm+Kwi/DkN8vhN2kbDVEfADo6jXf5hPKquMLGA3UINA64UZ7A==} + engines: {node: '>=18.0.0'} - '@smithy/types@3.7.2': - resolution: {integrity: sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==} - engines: {node: '>=16.0.0'} + '@smithy/types@4.1.0': + resolution: {integrity: sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==} + engines: {node: '>=18.0.0'} - '@smithy/url-parser@3.0.11': - resolution: {integrity: sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==} + '@smithy/url-parser@4.0.1': + resolution: {integrity: sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==} + engines: {node: '>=18.0.0'} - '@smithy/util-base64@3.0.0': - resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-base64@4.0.0': + resolution: {integrity: sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==} + engines: {node: '>=18.0.0'} - '@smithy/util-body-length-browser@3.0.0': - resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + '@smithy/util-body-length-browser@4.0.0': + resolution: {integrity: sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==} + engines: {node: '>=18.0.0'} - '@smithy/util-body-length-node@3.0.0': - resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} - engines: {node: '>=16.0.0'} + '@smithy/util-body-length-node@4.0.0': + resolution: {integrity: sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==} + engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} - '@smithy/util-buffer-from@3.0.0': - resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} - engines: {node: '>=16.0.0'} + '@smithy/util-buffer-from@4.0.0': + resolution: {integrity: sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==} + engines: {node: '>=18.0.0'} - '@smithy/util-config-provider@3.0.0': - resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-config-provider@4.0.0': + resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==} + engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@3.0.34': - resolution: {integrity: sha512-FumjjF631lR521cX+svMLBj3SwSDh9VdtyynTYDAiBDEf8YPP5xORNXKQ9j0105o5+ARAGnOOP/RqSl40uXddA==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@4.0.4': + resolution: {integrity: sha512-Ej1bV5sbrIfH++KnWxjjzFNq9nyP3RIUq2c9Iqq7SmMO/idUR24sqvKH2LUQFTSPy/K7G4sB2m8n7YYlEAfZaw==} + engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@3.0.34': - resolution: {integrity: sha512-vN6aHfzW9dVVzkI0wcZoUXvfjkl4CSbM9nE//08lmUMyf00S75uuCpTrqF9uD4bD9eldIXlt53colrlwKAT8Gw==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@4.0.4': + resolution: {integrity: sha512-HE1I7gxa6yP7ZgXPCFfZSDmVmMtY7SHqzFF55gM/GPegzZKaQWZZ+nYn9C2Cc3JltCMyWe63VPR3tSFDEvuGjw==} + engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@2.1.7': - resolution: {integrity: sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==} - engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@3.0.1': + resolution: {integrity: sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==} + engines: {node: '>=18.0.0'} - '@smithy/util-hex-encoding@3.0.0': - resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-hex-encoding@4.0.0': + resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==} + engines: {node: '>=18.0.0'} - '@smithy/util-middleware@3.0.11': - resolution: {integrity: sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==} - engines: {node: '>=16.0.0'} + '@smithy/util-middleware@4.0.1': + resolution: {integrity: sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==} + engines: {node: '>=18.0.0'} - '@smithy/util-retry@3.0.11': - resolution: {integrity: sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-retry@4.0.1': + resolution: {integrity: sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==} + engines: {node: '>=18.0.0'} - '@smithy/util-stream@3.3.4': - resolution: {integrity: sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-stream@4.0.2': + resolution: {integrity: sha512-0eZ4G5fRzIoewtHtwaYyl8g2C+osYOT4KClXgfdNEDAgkbe2TYPqcnw4GAWabqkZCax2ihRGPe9LZnsPdIUIHA==} + engines: {node: '>=18.0.0'} - '@smithy/util-uri-escape@3.0.0': - resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} - engines: {node: '>=16.0.0'} + '@smithy/util-uri-escape@4.0.0': + resolution: {integrity: sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==} + engines: {node: '>=18.0.0'} '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} - '@smithy/util-utf8@3.0.0': - resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} - engines: {node: '>=16.0.0'} + '@smithy/util-utf8@4.0.0': + resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==} + engines: {node: '>=18.0.0'} - '@smithy/util-waiter@3.2.0': - resolution: {integrity: sha512-PpjSboaDUE6yl+1qlg3Si57++e84oXdWGbuFUSAciXsVfEZJJJupR2Nb0QuXHiunt2vGR+1PTizOMvnUPaG2Qg==} - engines: {node: '>=16.0.0'} + '@smithy/util-waiter@4.0.2': + resolution: {integrity: sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==} + engines: {node: '>=18.0.0'} '@storybook/addon-a11y@8.4.7': resolution: {integrity: sha512-GpUvXp6n25U1ZSv+hmDC+05BEqxWdlWjQTb/GaboRXZQeMBlze6zckpVb66spjmmtQAIISo0eZxX1+mGcVR7lA==} @@ -5604,8 +5488,8 @@ packages: '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} - '@storybook/icons@1.3.0': - resolution: {integrity: sha512-Nz/UzeYQdUZUhacrPyfkiiysSjydyjgg/p0P9HxB4p/WaJUUjMAcaoaLgy3EXx61zZJ3iD36WPuDkZs5QYrA0A==} + '@storybook/icons@1.3.2': + resolution: {integrity: sha512-t3xcbCKkPvqyef8urBM0j/nP6sKtnlRkVgC+8JTbTAZQjaTmOjes3byEgzs89p4B/K6cJsg9wLW2k3SknLtYJw==} engines: {node: '>=14.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta @@ -5676,23 +5560,33 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@t3-oss/env-core@0.11.1': - resolution: {integrity: sha512-MaxOwEoG1ntCFoKJsS7nqwgcxLW1SJw238AJwfJeaz3P/8GtkxXZsPPolsz1AdYvUTbe3XvqZ/VCdfjt+3zmKw==} + '@t3-oss/env-core@0.12.0': + resolution: {integrity: sha512-lOPj8d9nJJTt81mMuN9GMk8x5veOt7q9m11OSnCBJhwp1QrL/qR+M8Y467ULBSm9SunosryWNbmQQbgoiMgcdw==} peerDependencies: typescript: '>=5.0.0' - zod: ^3.0.0 + valibot: ^1.0.0-beta.7 || ^1.0.0 + zod: ^3.24.0 peerDependenciesMeta: typescript: optional: true + valibot: + optional: true + zod: + optional: true - '@t3-oss/env-nextjs@0.11.1': - resolution: {integrity: sha512-rx2XL9+v6wtOqLNJbD5eD8OezKlQD1BtC0WvvtHwBgK66jnF5+wGqtgkKK4Ygie1LVmoDClths2T4tdFmRvGrQ==} + '@t3-oss/env-nextjs@0.12.0': + resolution: {integrity: sha512-rFnvYk1049RnNVUPvY8iQ55AuQh1Rr+qZzQBh3t++RttCGK4COpXGNxS4+45afuQq02lu+QAOy/5955aU8hRKw==} peerDependencies: typescript: '>=5.0.0' - zod: ^3.0.0 + valibot: ^1.0.0-beta.7 || ^1.0.0 + zod: ^3.24.0 peerDependenciesMeta: typescript: optional: true + valibot: + optional: true + zod: + optional: true '@tailwindcss/forms@0.5.9': resolution: {integrity: sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==} @@ -5711,8 +5605,8 @@ packages: react: '>=16.8' react-dom: '>=16.8' - '@tanstack/react-virtual@3.11.2': - resolution: {integrity: sha512-OuFzMXPF4+xZgx8UzJha0AieuMihhhaWG0tCqpp6tDzlFwOmNBPYMuLOtMJ1Tr4pXLHmgjcWhG6RlknY2oNTdQ==} + '@tanstack/react-virtual@3.11.3': + resolution: {integrity: sha512-vCU+OTylXN3hdC8RKg68tPlBPjjxtzon7Ys46MgrSLE+JhSjSTPvoQifV6DQJeJmA8Q3KT6CphJbejupx85vFw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -5721,8 +5615,8 @@ packages: resolution: {integrity: sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==} engines: {node: '>=12'} - '@tanstack/virtual-core@3.11.2': - resolution: {integrity: sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==} + '@tanstack/virtual-core@3.11.3': + resolution: {integrity: sha512-v2mrNSnMwnPJtcVqNvV0c5roGCBqeogN8jDtgtuHCphdwBasOZ17x8UV8qpHUh+u0MLfX43c0uUHKje0s+Zb0w==} '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} @@ -5851,8 +5745,8 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/jsonwebtoken@9.0.7': - resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==} + '@types/jsonwebtoken@9.0.8': + resolution: {integrity: sha512-7fx54m60nLFUVYlxAB1xpe9CBWX2vSrk50Y6ogRJ1v5xxtba7qXTg5BgYDN5dq+yuQQ9HaVlHJyAAt1/mxryFg==} '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} @@ -5875,8 +5769,8 @@ packages: '@types/mime-types@2.1.4': resolution: {integrity: sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==} - '@types/ms@0.7.34': - resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} @@ -5887,8 +5781,8 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@18.19.70': - resolution: {integrity: sha512-RE+K0+KZoEpDUbGGctnGdkrLFwi1eYKTlIHNl2Um98mUkGsm1u2Ff6Ltd0e8DktTtC98uy7rSj+hO8t/QuLoVQ==} + '@types/node@18.19.74': + resolution: {integrity: sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==} '@types/node@22.10.2': resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==} @@ -5928,6 +5822,9 @@ packages: '@types/react@18.3.11': resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==} + '@types/react@18.3.18': + resolution: {integrity: sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==} + '@types/react@19.0.1': resolution: {integrity: sha512-YW6614BDhqbpR5KtUYzTA+zlA7nayzJRA9ljz9CQoxthR0sDisYZLuvSMsil36t4EH/uAt8T52Xb4sVw17G+SQ==} @@ -5967,8 +5864,8 @@ packages: '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@types/ws@8.5.13': - resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} + '@types/ws@8.5.14': + resolution: {integrity: sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==} '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -6027,8 +5924,8 @@ packages: resolution: {integrity: sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.19.1': - resolution: {integrity: sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==} + '@typescript-eslint/scope-manager@8.22.0': + resolution: {integrity: sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/type-utils@7.18.0': @@ -6060,8 +5957,8 @@ packages: resolution: {integrity: sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.19.1': - resolution: {integrity: sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==} + '@typescript-eslint/types@8.22.0': + resolution: {integrity: sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@5.62.0': @@ -6088,8 +5985,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/typescript-estree@8.19.1': - resolution: {integrity: sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==} + '@typescript-eslint/typescript-estree@8.22.0': + resolution: {integrity: sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' @@ -6113,8 +6010,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' - '@typescript-eslint/utils@8.19.1': - resolution: {integrity: sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==} + '@typescript-eslint/utils@8.22.0': + resolution: {integrity: sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -6132,12 +6029,12 @@ packages: resolution: {integrity: sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.19.1': - resolution: {integrity: sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==} + '@typescript-eslint/visitor-keys@8.22.0': + resolution: {integrity: sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@ungap/structured-clone@1.2.1': - resolution: {integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==} + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} '@urql/core@5.1.0': resolution: {integrity: sha512-yC3sw8yqjbX45GbXxfiBY8GLYCiyW/hLBbQF9l3TJrv4ro00Y0ChkKaD9I2KntRxAVm9IYBqh0awX8fwWAe/Yw==} @@ -6219,12 +6116,36 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@vitest/coverage-v8@2.1.8': + resolution: {integrity: sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==} + peerDependencies: + '@vitest/browser': 2.1.8 + vitest: 2.1.8 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/coverage-v8@3.0.4': + resolution: {integrity: sha512-f0twgRCHgbs24Dp8cLWagzcObXMcuKtAwgxjJV/nnysPAJJk1JiKu/W0gIehZLmkljhJXU/E0/dmuQzsA/4jhA==} + peerDependencies: + '@vitest/browser': 3.0.4 + vitest: 3.0.4 + peerDependenciesMeta: + '@vitest/browser': + optional: true + '@vitest/expect@2.0.5': resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} '@vitest/expect@2.1.8': resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==} + '@vitest/expect@3.0.4': + resolution: {integrity: sha512-Nm5kJmYw6P2BxhJPkO3eKKhGYKRsnqJqf+r0yOGRKpEP+bSCBDsjXgiu1/5QFrnPMEgzfC38ZEjvCFgaNBC0Eg==} + + '@vitest/expect@3.0.5': + resolution: {integrity: sha512-nNIOqupgZ4v5jWuQx2DSlHLEs7Q4Oh/7AYwNyE+k0UQzG7tSmjPXShUikn1mpNGzYEN2jJbTvLejwShMitovBA==} + '@vitest/mocker@2.1.8': resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} peerDependencies: @@ -6236,30 +6157,82 @@ packages: vite: optional: true + '@vitest/mocker@3.0.4': + resolution: {integrity: sha512-gEef35vKafJlfQbnyOXZ0Gcr9IBUsMTyTLXsEQwuyYAerpHqvXhzdBnDFuHLpFqth3F7b6BaFr4qV/Cs1ULx5A==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/mocker@3.0.5': + resolution: {integrity: sha512-CLPNBFBIE7x6aEGbIjaQAX03ZZlBMaWwAjBdMkIf/cAn6xzLTiM3zYqO/WAbieEjsAZir6tO71mzeHZoodThvw==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@2.0.5': resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} '@vitest/pretty-format@2.1.8': resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==} + '@vitest/pretty-format@3.0.4': + resolution: {integrity: sha512-ts0fba+dEhK2aC9PFuZ9LTpULHpY/nd6jhAQ5IMU7Gaj7crPCTdCFfgvXxruRBLFS+MLraicCuFXxISEq8C93g==} + + '@vitest/pretty-format@3.0.5': + resolution: {integrity: sha512-CjUtdmpOcm4RVtB+up8r2vVDLR16Mgm/bYdkGFe3Yj/scRfCpbSi2W/BDSDcFK7ohw8UXvjMbOp9H4fByd/cOA==} + '@vitest/runner@2.1.8': resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} + '@vitest/runner@3.0.4': + resolution: {integrity: sha512-dKHzTQ7n9sExAcWH/0sh1elVgwc7OJ2lMOBrAm73J7AH6Pf9T12Zh3lNE1TETZaqrWFXtLlx3NVrLRb5hCK+iw==} + + '@vitest/runner@3.0.5': + resolution: {integrity: sha512-BAiZFityFexZQi2yN4OX3OkJC6scwRo8EhRB0Z5HIGGgd2q+Nq29LgHU/+ovCtd0fOfXj5ZI6pwdlUmC5bpi8A==} + '@vitest/snapshot@2.1.8': resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} + '@vitest/snapshot@3.0.4': + resolution: {integrity: sha512-+p5knMLwIk7lTQkM3NonZ9zBewzVp9EVkVpvNta0/PlFWpiqLaRcF4+33L1it3uRUCh0BGLOaXPPGEjNKfWb4w==} + + '@vitest/snapshot@3.0.5': + resolution: {integrity: sha512-GJPZYcd7v8QNUJ7vRvLDmRwl+a1fGg4T/54lZXe+UOGy47F9yUfE18hRCtXL5aHN/AONu29NGzIXSVFh9K0feA==} + '@vitest/spy@2.0.5': resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} '@vitest/spy@2.1.8': resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} + '@vitest/spy@3.0.4': + resolution: {integrity: sha512-sXIMF0oauYyUy2hN49VFTYodzEAu744MmGcPR3ZBsPM20G+1/cSW/n1U+3Yu/zHxX2bIDe1oJASOkml+osTU6Q==} + + '@vitest/spy@3.0.5': + resolution: {integrity: sha512-5fOzHj0WbUNqPK6blI/8VzZdkBlQLnT25knX0r4dbZI9qoZDf3qAdjoMmDcLG5A83W6oUUFJgUd0EYBc2P5xqg==} + '@vitest/utils@2.0.5': resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} '@vitest/utils@2.1.8': resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} + '@vitest/utils@3.0.4': + resolution: {integrity: sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==} + + '@vitest/utils@3.0.5': + resolution: {integrity: sha512-N9AX0NUoUtVwKwy21JtwzaqR5L5R5A99GAbrHfCCXK1lp593i/3AZAXhSP43wRQuxYsflrdzEfXZFo1reR1Nkg==} + '@volar/language-core@2.4.11': resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==} @@ -6401,8 +6374,8 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - ai@4.0.18: - resolution: {integrity: sha512-BTWzalLNE1LQphEka5xzJXDs5v4xXy1Uzr7dAVk+C/CnO3WNpuMBgrCymwUv0VrWaWc8xMQuh+OqsT7P7JyekQ==} + ai@4.1.17: + resolution: {integrity: sha512-5SW15tXDuxE/wlEOjRKxLxTOUIGD4C9bIee+FCFvXHTTAZhHiQjViC2s7RtMUW+hbFtGya302jUHY1Pe8A/YuQ==} engines: {node: '>=18'} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc @@ -6459,8 +6432,8 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - algoliasearch@5.19.0: - resolution: {integrity: sha512-zrLtGhC63z3sVLDDKGW+SlCRN9eJHFTgdEmoAOpsVh6wgGL1GgTTDou7tpCBjevzgIvi3AIyDAQO3Xjbg5eqZg==} + algoliasearch@5.20.0: + resolution: {integrity: sha512-groO71Fvi5SWpxjI9Ia+chy0QBwT61mg6yxJV27f5YFf+Mw+STT75K6SHySpP8Co5LsCrtsbCH5dJZSRtkSKaQ==} engines: {node: '>= 14.0.0'} anser@1.4.10: @@ -6643,6 +6616,10 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + async-limiter@1.0.1: resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} @@ -6667,8 +6644,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - aws-crt@1.24.0: - resolution: {integrity: sha512-23X764sRXf9P7luqwxbY47cuQd1pj2ZAQdI08Ih/YgXKz0DGptRXA2TZfj+mW9NJsZellqCXIYznnqEwqrlbtQ==} + aws-crt@1.25.3: + resolution: {integrity: sha512-MWV2Yy08xxAZqMJiFE1ZwSrVNq2Hy54SK4ooUx2Ts6DlDzdAizSfgoEKkh7ifyo7j5RVXu3924zAPjC8FBLiSg==} axe-core@4.10.2: resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} @@ -6737,8 +6714,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - babel-preset-expo@12.0.5: - resolution: {integrity: sha512-rEFjN1CoMYEWSRpE+Hvw+zv+nLbDXyRM8vGAoYJtFPJovHupX2VRWPVaqtHlnMTrzsGFQDf4WfQJrjAQ9e2hAQ==} + babel-preset-expo@12.0.6: + resolution: {integrity: sha512-az3H7gDVo0wxNBAFES8h5vLLWE8NPGkD9g5P962hDEOqZUdyPacb9MOzicypeLmcq9zQWr6E3iVtEHoNagCTTQ==} peerDependencies: babel-plugin-react-compiler: ^19.0.0-beta-9ee70a1-20241017 react-compiler-runtime: ^19.0.0-beta-8a03594-20241020 @@ -6985,8 +6962,8 @@ packages: camelize@1.0.1: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} - caniuse-lite@1.0.30001692: - resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==} + caniuse-lite@1.0.30001696: + resolution: {integrity: sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -7080,8 +7057,8 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} - chromatic@11.22.2: - resolution: {integrity: sha512-Z7+9hD1yp1fUm34XX1wojIco0lQlXOVYhzDSE8v1ZU6qLD2r4N6UHKD+N+XY1Jj+gpsDFWYMTpSnDfcHZf5mhg==} + chromatic@11.25.1: + resolution: {integrity: sha512-D0NdcGOSy84hqgNnSY7FM4TzB77RymRTowjm4hb1CV4wbk1djKTV4SJbbYVCzHFD+n/NOg/wtZ9Y7sjiRdy8dA==} hasBin: true peerDependencies: '@chromatic-com/cypress': ^0.*.* || ^1.0.0 @@ -7319,8 +7296,8 @@ packages: resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} engines: {node: '>= 0.10.0'} - consola@3.3.3: - resolution: {integrity: sha512-Qil5KwghMzlqd51UXM0b6fyaGHtOC22scxrwrz4A2882LyUMwQjnvaedN1HAeXzphspQ6CpHkzMAWxBTUruDLg==} + consola@3.4.0: + resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} engines: {node: ^14.18.0 || >=16.10.0} console-browserify@1.2.0: @@ -7529,8 +7506,8 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} - decimal.js@10.4.3: - resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + decimal.js@10.5.0: + resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} decko@1.2.0: resolution: {integrity: sha512-m8FnyHXV1QX+S1cl+KPFDIl6NMkxtKsy6+U/aYyjrOqWMuwAwYWu7ePqrsUHtDR5Y8Yk2pi/KIDSgF+vT4cPOQ==} @@ -7734,8 +7711,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.80: - resolution: {integrity: sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==} + electron-to-chromium@1.5.88: + resolution: {integrity: sha512-K3C2qf1o+bGzbilTDCTBhTQcMS9KW60yTAaTeeXsfvQuTDDwlokLam/AdqlqcSy9u4UainDgsHV23ksXAOgamw==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -7843,10 +7820,6 @@ packages: es-module-lexer@1.6.0: resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -8218,52 +8191,52 @@ packages: resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} engines: {node: '>=12.0.0'} - expo-asset@11.0.1: - resolution: {integrity: sha512-WatvD7JVC89EsllXFYcS/rji3ajVzE2B/USo0TqedsETixwyVCQfrrvCdCPQyuKghrxVNEj8bQ/Qbea/RZLYjg==} + expo-asset@11.0.2: + resolution: {integrity: sha512-We3Td5WsNsNQyXoheLnuwic6JCOt/pqXqIIyWaZ3z/PeHrA+SwoQdI18MjDhkudLK08tbIVyDSUW8IJHXa04eg==} peerDependencies: expo: '*' react: '*' react-native: '*' - expo-constants@17.0.3: - resolution: {integrity: sha512-lnbcX2sAu8SucHXEXxSkhiEpqH+jGrf+TF+MO6sHWIESjwOUVVYlT8qYdjR9xbxWmqFtrI4KV44FkeJf2DaFjQ==} + expo-constants@17.0.5: + resolution: {integrity: sha512-6SHXh32jCB+vrp2TRDNkoGoM421eOBPZIXX9ixI0hKKz71tIjD+LMr/P+rGUd/ks312MP3WK3j5vcYYPkCD8tQ==} peerDependencies: expo: '*' react-native: '*' - expo-file-system@18.0.6: - resolution: {integrity: sha512-gGEwIJCXV3/wpIJ/wRyhmieLOSAY7HeFFjb+wEfHs04aE63JYR+rXXV4b7rBpEh1ZgNV9U91zfet/iQG7J8HBQ==} + expo-file-system@18.0.7: + resolution: {integrity: sha512-6PpbQfogMXdzOsJzlJayy5qf40IfIHhudtAOzr32RlRYL4Hkmk3YcR9jG0PWQ0rklJfAhbAdP63yOcN+wDgzaA==} peerDependencies: expo: '*' react-native: '*' - expo-font@13.0.2: - resolution: {integrity: sha512-H9FaXM7ZW5+EfV38w80BgJG3H17kB7CuVXwHoiszIYyoPfWz9bWesFe4QwNZjTq3pzKes28sSd8irFuflIrSIA==} + expo-font@13.0.3: + resolution: {integrity: sha512-9IdYz+A+b3KvuCYP7DUUXF4VMZjPU+IsvAnLSVJ2TfP6zUD2JjZFx3jeo/cxWRkYk/aLj5+53Te7elTAScNl4Q==} peerDependencies: expo: '*' react: '*' - expo-keep-awake@14.0.1: - resolution: {integrity: sha512-c5mGCAIk2YM+Vsdy90BlEJ4ZX+KG5Au9EkJUIxXWlpnuKmDAJ3N+5nEZ7EUO1ZTheqoSBeAo4jJ8rTWPU+JXdw==} + expo-keep-awake@14.0.2: + resolution: {integrity: sha512-71XAMnoWjKZrN8J7Q3+u0l9Ytp4OfhNAYz8BCWF1/9aFUw09J3I7Z5DuI3MUsVMa/KWi+XhG+eDUFP8cVA19Uw==} peerDependencies: expo: '*' react: '*' - expo-modules-autolinking@2.0.4: - resolution: {integrity: sha512-e0p+19NhmD50U7s7BV7kWIypWmTNC9n/VlJKlXS05hM/zX7pe6JKmXyb+BFnXJq3SLBalLCUY0tu2gEUF3XeVg==} + expo-modules-autolinking@2.0.7: + resolution: {integrity: sha512-rkGc6a/90AC3q8wSy4V+iIpq6Fd0KXmQICKrvfmSWwrMgJmLfwP4QTrvLYPYOOMjFwNJcTaohcH8vzW/wYKrMg==} hasBin: true - expo-modules-core@2.1.1: - resolution: {integrity: sha512-yQzYCLR2mre4BNMXuqkeJ0oSNgmGEMI6BcmIzeNZbC2NFEjiaDpKvlV9bclYCtyVhUEVNbJcEPYMr6c1Y4eR4w==} + expo-modules-core@2.2.0: + resolution: {integrity: sha512-mOFEHIe6jZ7G5pYUVSQ2Ghs3CUr9Uz6DOh4JI+4PsTf0gmEvMmMEOrxirS89jRWQjXPJ7QaGBK0CJrZlj/Sdeg==} - expo-status-bar@2.0.0: - resolution: {integrity: sha512-vxxdpvpNDMTEc5uTiIrbTvySKKUsOACmfl8OZuUdjNle05oGqwtq3v5YObwym/njSByjoyuZX8UpXBZnxvarwQ==} + expo-status-bar@2.0.1: + resolution: {integrity: sha512-AkIPX7jWHRPp83UBZ1iXtVvyr0g+DgBVvIXTtlmPtmUsm8Vq9Bb5IGj86PW8osuFlgoTVAg7HI/+Ok7yEYwiRg==} peerDependencies: react: '*' react-native: '*' - expo@52.0.18: - resolution: {integrity: sha512-z+qdUbH0d5JRknE3VrY0s5k+3j5JpsLx4vXRwV4To8Xm5uf3d642FQ2HbuPWFAAhtSKFQsxQAh3iuAUGAWDBhg==} + expo@52.0.28: + resolution: {integrity: sha512-0O/JEYYCFszJ85frislm79YmlrQA5ghAQXV4dqcQcsy9FqftdicD4p/ehT36yiuGIhaKC6fn25LEaJ9JR2ei7g==} hasBin: true peerDependencies: '@expo/dom-webview': '*' @@ -8318,8 +8291,8 @@ packages: fast-shallow-equal@1.0.0: resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} - fast-uri@3.0.5: - resolution: {integrity: sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==} + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} fast-write-atomic@0.2.1: resolution: {integrity: sha512-WvJe06IfNYlr+6cO3uQkdKdy3Cb1LlCJSF8zRs2eT8yuhdbSlR9nIt+TgQ92RUxiRrQm+/S7RARnMfCs5iuAjw==} @@ -8350,8 +8323,8 @@ packages: fbjs@3.0.5: resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} - fdir@6.4.2: - resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -8426,8 +8399,8 @@ packages: flow-enums-runtime@0.0.6: resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==} - flow-parser@0.258.1: - resolution: {integrity: sha512-Y8CrO98EcXVCiYE4s5z0LTMbeYjKyd3MAEUJqxA7B8yGRlmdrG5UDqq4pVrUAfAu2tMFgpQESvBhBu9Xg1tpow==} + flow-parser@0.259.1: + resolution: {integrity: sha512-xiXLmMH2Z7OmdE9Q+MjljUMr/rbemFqZIRxaeZieVScG4HzQrKKhNcCYZbWTGpoN7ZPi7z8ClQbeVPq6t5AszQ==} engines: {node: '>=0.4.0'} follow-redirects@1.15.9: @@ -8442,8 +8415,9 @@ packages: fontfaceobserver@2.3.0: resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==} - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.4: + resolution: {integrity: sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==} + engines: {node: '>= 0.4'} foreach@2.0.6: resolution: {integrity: sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==} @@ -8620,8 +8594,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} get-user-locale@2.3.2: resolution: {integrity: sha512-O2GWvQkhnbDoWFUJfaBlDIKUEdND8ATpBXD6KXcbhxlfktyD/d8w6mkzM/IlQEqGZAMz/PW6j6Hv53BiigKLUQ==} @@ -8694,8 +8668,8 @@ packages: peerDependencies: csstype: ^3.0.10 - google-auth-library@9.15.0: - resolution: {integrity: sha512-7ccSEJFDFO7exFbO6NRyC+xH8/mZ1GZGG2xxx9iHxZWcjUjJpjWxIMw3cofAKcueZ6DATiukmmprD7yavQHOyQ==} + google-auth-library@9.15.1: + resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==} engines: {node: '>=14'} googleapis-common@7.2.0: @@ -8849,6 +8823,9 @@ packages: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-to-text@9.0.5: resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} engines: {node: '>=14'} @@ -8993,8 +8970,8 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - intl-messageformat@10.7.11: - resolution: {integrity: sha512-IB2N1tmI24k2EFH3PWjU7ivJsnWyLwOWOva0jnXFa29WzB6fb0JZ5EMQGu+XN5lDtjHYFo0/UooP67zBwUg7rQ==} + intl-messageformat@10.7.14: + resolution: {integrity: sha512-mMGnE4E1otdEutV5vLUdCxRJygHB5ozUBxsPB5qhitewssrS/qGruq9bmvIRkkGsNeK5ZWLfYRld18UHGTIifQ==} invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} @@ -9037,8 +9014,8 @@ packages: is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - is-async-function@2.1.0: - resolution: {integrity: sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==} + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} is-bigint@1.1.0: @@ -9297,6 +9274,18 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} @@ -9529,8 +9518,8 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - langfuse-core@3.32.0: - resolution: {integrity: sha512-8wWnPtmhMBBgVicLR9SjQ1O29fgvTJyRDE5kaF8b908TvE0PXZmxs068GGhLCCdTMUGpU6WXqKRHTZfXAuIzXA==} + langfuse-core@3.33.1: + resolution: {integrity: sha512-RW+pDwYVSpJTfht4i7IfbNm5HoMAytZ1+LBjjMsdlsrWlZm23siWRWOGS/xcDk5kDNWa0qHIAkjC/nnDa8dObQ==} engines: {node: '>=18'} langfuse-vercel@3.31.3: @@ -9539,8 +9528,8 @@ packages: peerDependencies: ai: '>=3.2.44' - langfuse@3.32.0: - resolution: {integrity: sha512-Qto8+CW8ZulNaZ/gn4JgVe5v/yeKbvMyvQ/s2DXO5bP5o1PmqE6YOCHQ3Uep6W94B8kpXUgOHg7ZS5Wg8Xljsw==} + langfuse@3.33.1: + resolution: {integrity: sha512-FIxZZpB3236u+iNsAYx2ikGtVDE6Ozvq+A79addNIeEZ+4yWHIFpQSz+Spqss5SeL5BUZaZ5hfX7FK6v859OLQ==} engines: {node: '>=18'} language-subtag-registry@0.3.23: @@ -9717,6 +9706,7 @@ packages: lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} @@ -9726,6 +9716,7 @@ packages: lodash.isequal@4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. lodash.isinteger@4.0.4: resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} @@ -9789,8 +9780,8 @@ packages: lottie-web@5.12.2: resolution: {integrity: sha512-uvhvYPC8kGPjXT3MyKMrL3JitEAmDMp30lVkuq/590Mw9ok6pWcFCwXJveo0t5uqYw1UREQHofD+jVpdjBv8wg==} - loupe@3.1.2: - resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -9836,6 +9827,9 @@ packages: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} @@ -9844,6 +9838,10 @@ packages: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -9934,8 +9932,8 @@ packages: mdast-util-mdx-expression@2.0.1: resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} - mdast-util-mdx-jsx@3.1.3: - resolution: {integrity: sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==} + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} mdast-util-mdx@3.0.0: resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} @@ -10116,8 +10114,8 @@ packages: micromark-extension-gfm-strikethrough@2.1.0: resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - micromark-extension-gfm-table@2.1.0: - resolution: {integrity: sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==} + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} micromark-extension-gfm-tagfilter@2.0.0: resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} @@ -10197,8 +10195,8 @@ packages: micromark-util-sanitize-uri@2.0.1: resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - micromark-util-subtokenize@2.0.3: - resolution: {integrity: sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==} + micromark-util-subtokenize@2.0.4: + resolution: {integrity: sha512-N6hXjrin2GTJDe3MVjf5FuXpm12PGm80BrUAeub9XFXca8JZbP+oIwY4LJSVwFUCL1IPm/WwSVUN7goFHmSGGQ==} micromark-util-symbol@2.0.1: resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} @@ -10352,8 +10350,8 @@ packages: engines: {node: '>=10'} hasBin: true - mlly@1.7.3: - resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==} + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} mobx-react-lite@4.1.0: resolution: {integrity: sha512-QEP10dpHHBeQNv1pks3WnHRCem2Zp636lq54M2nKO2Sarr13pL4u6diQXf65yzXUn0mkk18SyIDCm9UOJYTi1w==} @@ -10390,11 +10388,11 @@ packages: moo@0.5.2: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} - motion-dom@11.16.4: - resolution: {integrity: sha512-2wuCie206pCiP2K23uvwJeci4pMFfyQKpWI0Vy6HrCTDzDCer4TsYtT7IVnuGbDeoIV37UuZiUr6SZMHEc1Vww==} + motion-dom@11.18.1: + resolution: {integrity: sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==} - motion-utils@11.16.0: - resolution: {integrity: sha512-ngdWPjg31rD4WGXFi0eZ00DQQqKKu04QExyv/ymlC+3k+WIgYVFbt6gS5JsFPbJODTF/r8XiE/X+SsoT9c0ocw==} + motion-utils@11.18.1: + resolution: {integrity: sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==} mqtt-packet@6.10.0: resolution: {integrity: sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==} @@ -11015,6 +11013,9 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathe@2.0.2: + resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==} + pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} @@ -11087,8 +11088,8 @@ packages: resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} engines: {node: '>=10'} - pkg-types@1.3.0: - resolution: {integrity: sha512-kS7yWjVFCkIw9hqdJBoMxDdzEngmkr5FXeWZZfQ6GoYacjVnsW6l2CcYW/0ThD0vF4LPJgVYnrg4d0uuhwYQbg==} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} playwright-core@1.49.1: resolution: {integrity: sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==} @@ -11199,10 +11200,6 @@ packages: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.1: - resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} - engines: {node: ^10 || ^12 || >=14} - postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -11222,8 +11219,8 @@ packages: posthog-js@1.200.2: resolution: {integrity: sha512-hDdnzn/FWz+lR0qoYn8TJ7UAVzJSH48ceM2rYXrrZZa8EqBKaUKLf1LWK505/s3QVjK972mbF8wjF+pRDSlwOg==} - posthog-node@4.3.2: - resolution: {integrity: sha512-vy8Mt9IEfniUgqQ1rOCQ31CBO1VNqDGd3ZtHlWR9/YfU6RiuK+9pUXPb4h6HTGzQmjL8NFnjd8K8NMXSX8S6MQ==} + posthog-node@4.4.1: + resolution: {integrity: sha512-o9G9sSvwWITrfSJgIUrPLJd//AYPGJNu5D+pSLxqiBvhUeicc/i639FvU0DPr1OsHiLDE2zHNMmLpa0mw4kBCg==} engines: {node: '>=15.0.0'} preact-render-to-string@5.2.6: @@ -11238,8 +11235,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-plugin-packagejson@2.5.6: - resolution: {integrity: sha512-TY7KiLtyt6Tlf53BEbXUWkN0+TRdHKgIMmtXtDCyHH6yWnZ50Lwq6Vb6lyjapZrhDTXooC4EtlY5iLe1sCgi5w==} + prettier-plugin-packagejson@2.5.8: + resolution: {integrity: sha512-BaGOF63I0IJZoudxpuQe17naV93BRtK8b3byWktkJReKEMX9CC4qdGUzThPDVO/AUhPzlqDiAXbp18U6X8wLKA==} peerDependencies: prettier: '>= 1.16.0' peerDependenciesMeta: @@ -11437,8 +11434,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - qs@6.13.1: - resolution: {integrity: sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} querystring-es3@0.2.1: @@ -11530,8 +11527,8 @@ packages: peerDependencies: typescript: '>= 4.3.x' - react-docgen@7.1.0: - resolution: {integrity: sha512-APPU8HB2uZnpl6Vt/+0AFoVYgSRtfiP6FLrZgPPTDmqSb2R4qZRbgd0A3VzIFxDt5e+Fozjx79WjLWnF69DK8g==} + react-docgen@7.1.1: + resolution: {integrity: sha512-hlSJDQ2synMPKFZOsKo9Hi8WWZTC7POR8EmWvTSjow+VDgKzkmjQvFm2fk0tmRw+f0vTOIYKlarR0iL4996pdg==} engines: {node: '>=16.14.0'} react-dom@18.3.1: @@ -11618,8 +11615,8 @@ packages: '@types/react': optional: true - react-native@0.76.5: - resolution: {integrity: sha512-op2p2kB+lqMF1D7AdX4+wvaR0OPFbvWYs+VBE7bwsb99Cn9xISrLRLAgFflZedQsa5HvnOGrULhtnmItbIKVVw==} + react-native@0.76.6: + resolution: {integrity: sha512-AsRi+ud6v6ADH7ZtSOY42kRB4nbM0KtSu450pGO4pDudl4AEK/AF96ai88snb2/VJJSGGa/49QyJVFXxz/qoFg==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -11744,9 +11741,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - readdirp@4.0.2: - resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} - engines: {node: '>= 14.16.0'} + readdirp@4.1.1: + resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} + engines: {node: '>= 14.18.0'} readline@1.3.0: resolution: {integrity: sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==} @@ -11892,8 +11889,8 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - require-in-the-middle@7.4.0: - resolution: {integrity: sha512-X34iHADNbNDfr6OTStIAHWSAvvKQRYgLO6duASaVf7J2VA3lvmNYboAHOuLC2huav1IwgZJtyEcJCKVzFxOSMQ==} + require-in-the-middle@7.5.0: + resolution: {integrity: sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==} engines: {node: '>=8.6.0'} require-main-filename@2.0.0: @@ -12001,13 +11998,8 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true - rollup@4.30.1: - resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - rollup@4.31.0: - resolution: {integrity: sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw==} + rollup@4.32.1: + resolution: {integrity: sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -12026,9 +12018,6 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rusha@0.8.14: - resolution: {integrity: sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==} - rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} @@ -12304,8 +12293,8 @@ packages: sort-object-keys@1.1.3: resolution: {integrity: sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==} - sort-package-json@2.12.0: - resolution: {integrity: sha512-/HrPQAeeLaa+vbAH/znjuhwUluuiM/zL5XX9kop8UpDgjtyWKt43hGDk2vd/TBdDpzIyzIHVUgmYofzYrAQjew==} + sort-package-json@2.14.0: + resolution: {integrity: sha512-xBRdmMjFB/KW3l51mP31dhlaiFmqkHLfWTfZAno8prb/wbDxwBPWFpxB16GZbiPbYr3wL41H8Kx22QIDWRe8WQ==} hasBin: true source-map-js@1.2.1: @@ -12353,8 +12342,8 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-license-ids@3.0.20: - resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} + spdx-license-ids@3.0.21: + resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} split2@3.2.2: resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} @@ -12578,8 +12567,8 @@ packages: stylis@4.3.2: resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} - stylis@4.3.4: - resolution: {integrity: sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==} + stylis@4.3.5: + resolution: {integrity: sha512-K7npNOKGRYuhAFFzkzMGfxFDpN6gDwf8hcMiE+uveTVbBgm93HrNP3ZDUpKqzZ4pG7TP6fmb+EMAQPjq9FqqvA==} sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} @@ -12644,6 +12633,9 @@ packages: tailwind-merge@2.5.5: resolution: {integrity: sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==} + tailwind-merge@3.0.1: + resolution: {integrity: sha512-AvzE8FmSoXC7nC+oU5GlQJbip2UO7tmOhOfQyOmPhrStOGXHU08j8mZEHZ4BmCqY5dWTCo4ClWkNyRNx1wpT0g==} + tailwindcss@3.4.16: resolution: {integrity: sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw==} engines: {node: '>=14.0.0'} @@ -12718,6 +12710,10 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + test-exclude@7.0.1: + resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} + engines: {node: '>=18'} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -12777,15 +12773,19 @@ packages: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + tinyspy@3.0.2: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} - tldts-core@6.1.71: - resolution: {integrity: sha512-LRbChn2YRpic1KxY+ldL1pGXN/oVvKfCVufwfVzEQdFYNo39uF7AJa/WXdo+gYO7PTvdfkCPCed6Hkvz/kR7jg==} + tldts-core@6.1.75: + resolution: {integrity: sha512-AOvV5YYIAFFBfransBzSTyztkc3IMfz5Eq3YluaRiEu55nn43Fzaufx70UqEKYr8BoLCach4q8g/bg6e5+/aFw==} - tldts@6.1.71: - resolution: {integrity: sha512-LQIHmHnuzfZgZWAf2HzL83TIIrD8NhhI0DVxqo9/FdOd4ilec+NTNZOlDZf7EwrTNoutccbsHjvWHYXLAtvxjw==} + tldts@6.1.75: + resolution: {integrity: sha512-+lFzEXhpl7JXgWYaXcB6DqTYXbUArvrWAE/5ioq/X3CdWLbDjpPP4XTrQBmEJ91y3xbe4Fkw7Lxv4P3GWeJaNg==} hasBin: true tmp@0.0.33: @@ -13067,10 +13067,6 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} - undici@6.21.0: - resolution: {integrity: sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==} - engines: {node: '>=18.17'} - undici@6.21.1: resolution: {integrity: sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==} engines: {node: '>=18.17'} @@ -13282,6 +13278,16 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true + vite-node@3.0.4: + resolution: {integrity: sha512-7JZKEzcYV2Nx3u6rlvN8qdo3QV7Fxyt6hx+CCKz9fbWxdX5IvUOmTWEAxMrWxaiSf7CKGLJQ5rFu8prb/jBjOA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite-node@3.0.5: + resolution: {integrity: sha512-02JEJl7SbtwSDJdYS537nU6l+ktdvcREfLksk/NDAqtdKWGqHl+joXzEubHROmS3E6pip+Xgu2tFezMu75jH7A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + vite-plugin-dts@4.3.0: resolution: {integrity: sha512-LkBJh9IbLwL6/rxh0C1/bOurDrIEmRE7joC+jFdOEEciAFPbpEKOLSAr5nNh5R7CJ45cMbksTrFfy52szzC5eA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -13407,6 +13413,62 @@ packages: jsdom: optional: true + vitest@3.0.4: + resolution: {integrity: sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.0.4 + '@vitest/ui': 3.0.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vitest@3.0.5: + resolution: {integrity: sha512-4dof+HvqONw9bvsYxtkfUp2uHsTN9bV2CZIi1pWgoFpL1Lld8LA1ka9q/ONSsoScAKG7NVGf2stJTI7XRkXb2Q==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.0.5 + '@vitest/ui': 3.0.5 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vlq@1.0.1: resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} @@ -13694,8 +13756,8 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yjs@13.6.21: - resolution: {integrity: sha512-/fzzyeCAfr3Qwx1D71zvumm64x+Q5MEFel6EhWlA1IBFxWPb7tei4J2a8CJyjpYHfVrRij5q3RJTK9W2Iqjouw==} + yjs@13.6.23: + resolution: {integrity: sha512-ExtnT5WIOVpkL56bhLeisG/N5c4fmzKn4k0ROVfJa5TY2QHbH7F0Wu2T5ZhR7ErsFWQEFafyrnSI8TPKVF9Few==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} yn@3.1.1: @@ -13748,176 +13810,176 @@ snapshots: '@adobe/css-tools@4.4.1': {} - '@ai-sdk/azure@1.0.10(zod@3.24.1)': + '@ai-sdk/azure@1.1.9(zod@3.24.1)': dependencies: - '@ai-sdk/openai': 1.0.8(zod@3.24.1) - '@ai-sdk/provider': 1.0.2 - '@ai-sdk/provider-utils': 2.0.4(zod@3.24.1) + '@ai-sdk/openai': 1.1.9(zod@3.24.1) + '@ai-sdk/provider': 1.0.7 + '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1) zod: 3.24.1 - '@ai-sdk/openai@1.0.8(zod@3.24.1)': + '@ai-sdk/openai@1.1.9(zod@3.24.1)': dependencies: - '@ai-sdk/provider': 1.0.2 - '@ai-sdk/provider-utils': 2.0.4(zod@3.24.1) + '@ai-sdk/provider': 1.0.7 + '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1) zod: 3.24.1 - '@ai-sdk/provider-utils@2.0.4(zod@3.24.1)': + '@ai-sdk/provider-utils@2.1.6(zod@3.24.1)': dependencies: - '@ai-sdk/provider': 1.0.2 + '@ai-sdk/provider': 1.0.7 eventsource-parser: 3.0.0 nanoid: 3.3.8 secure-json-parse: 2.7.0 optionalDependencies: zod: 3.24.1 - '@ai-sdk/provider@1.0.2': + '@ai-sdk/provider@1.0.7': dependencies: json-schema: 0.4.0 - '@ai-sdk/react@1.0.6(react@19.0.0)(zod@3.24.1)': + '@ai-sdk/react@1.1.8(react@19.0.0)(zod@3.24.1)': dependencies: - '@ai-sdk/provider-utils': 2.0.4(zod@3.24.1) - '@ai-sdk/ui-utils': 1.0.5(zod@3.24.1) + '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1) + '@ai-sdk/ui-utils': 1.1.8(zod@3.24.1) swr: 2.3.0(react@19.0.0) throttleit: 2.1.0 optionalDependencies: react: 19.0.0 zod: 3.24.1 - '@ai-sdk/ui-utils@1.0.5(zod@3.24.1)': + '@ai-sdk/ui-utils@1.1.8(zod@3.24.1)': dependencies: - '@ai-sdk/provider': 1.0.2 - '@ai-sdk/provider-utils': 2.0.4(zod@3.24.1) + '@ai-sdk/provider': 1.0.7 + '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1) zod-to-json-schema: 3.24.1(zod@3.24.1) optionalDependencies: zod: 3.24.1 - '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.17.3)': + '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)': dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.17.3) - '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0) + '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0) transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - search-insights - '@algolia/autocomplete-core@1.17.8(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.17.3)': + '@algolia/autocomplete-core@1.17.8(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)': dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.17.8(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.17.3) - '@algolia/autocomplete-shared': 1.17.8(@algolia/client-search@5.19.0)(algoliasearch@5.19.0) + '@algolia/autocomplete-plugin-algolia-insights': 1.17.8(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.8(@algolia/client-search@5.20.0)(algoliasearch@5.20.0) transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - search-insights - '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.17.3)': + '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)': dependencies: - '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0) search-insights: 2.17.3 transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - '@algolia/autocomplete-plugin-algolia-insights@1.17.8(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.17.3)': + '@algolia/autocomplete-plugin-algolia-insights@1.17.8(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3)': dependencies: - '@algolia/autocomplete-shared': 1.17.8(@algolia/client-search@5.19.0)(algoliasearch@5.19.0) + '@algolia/autocomplete-shared': 1.17.8(@algolia/client-search@5.20.0)(algoliasearch@5.20.0) search-insights: 2.17.3 transitivePeerDependencies: - '@algolia/client-search' - algoliasearch - '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)': + '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)': dependencies: - '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0) - '@algolia/client-search': 5.19.0 - algoliasearch: 5.19.0 + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0) + '@algolia/client-search': 5.20.0 + algoliasearch: 5.20.0 - '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)': + '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)': dependencies: - '@algolia/client-search': 5.19.0 - algoliasearch: 5.19.0 + '@algolia/client-search': 5.20.0 + algoliasearch: 5.20.0 - '@algolia/autocomplete-shared@1.17.8(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)': + '@algolia/autocomplete-shared@1.17.8(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)': dependencies: - '@algolia/client-search': 5.19.0 - algoliasearch: 5.19.0 + '@algolia/client-search': 5.20.0 + algoliasearch: 5.20.0 - '@algolia/client-abtesting@5.19.0': + '@algolia/client-abtesting@5.20.0': dependencies: - '@algolia/client-common': 5.19.0 - '@algolia/requester-browser-xhr': 5.19.0 - '@algolia/requester-fetch': 5.19.0 - '@algolia/requester-node-http': 5.19.0 + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 - '@algolia/client-analytics@5.19.0': + '@algolia/client-analytics@5.20.0': dependencies: - '@algolia/client-common': 5.19.0 - '@algolia/requester-browser-xhr': 5.19.0 - '@algolia/requester-fetch': 5.19.0 - '@algolia/requester-node-http': 5.19.0 + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 - '@algolia/client-common@5.19.0': {} + '@algolia/client-common@5.20.0': {} - '@algolia/client-insights@5.19.0': + '@algolia/client-insights@5.20.0': dependencies: - '@algolia/client-common': 5.19.0 - '@algolia/requester-browser-xhr': 5.19.0 - '@algolia/requester-fetch': 5.19.0 - '@algolia/requester-node-http': 5.19.0 + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 - '@algolia/client-personalization@5.19.0': + '@algolia/client-personalization@5.20.0': dependencies: - '@algolia/client-common': 5.19.0 - '@algolia/requester-browser-xhr': 5.19.0 - '@algolia/requester-fetch': 5.19.0 - '@algolia/requester-node-http': 5.19.0 + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 - '@algolia/client-query-suggestions@5.19.0': + '@algolia/client-query-suggestions@5.20.0': dependencies: - '@algolia/client-common': 5.19.0 - '@algolia/requester-browser-xhr': 5.19.0 - '@algolia/requester-fetch': 5.19.0 - '@algolia/requester-node-http': 5.19.0 + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 - '@algolia/client-search@5.19.0': + '@algolia/client-search@5.20.0': dependencies: - '@algolia/client-common': 5.19.0 - '@algolia/requester-browser-xhr': 5.19.0 - '@algolia/requester-fetch': 5.19.0 - '@algolia/requester-node-http': 5.19.0 + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 - '@algolia/ingestion@1.19.0': + '@algolia/ingestion@1.20.0': dependencies: - '@algolia/client-common': 5.19.0 - '@algolia/requester-browser-xhr': 5.19.0 - '@algolia/requester-fetch': 5.19.0 - '@algolia/requester-node-http': 5.19.0 + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 - '@algolia/monitoring@1.19.0': + '@algolia/monitoring@1.20.0': dependencies: - '@algolia/client-common': 5.19.0 - '@algolia/requester-browser-xhr': 5.19.0 - '@algolia/requester-fetch': 5.19.0 - '@algolia/requester-node-http': 5.19.0 + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 - '@algolia/recommend@5.19.0': + '@algolia/recommend@5.20.0': dependencies: - '@algolia/client-common': 5.19.0 - '@algolia/requester-browser-xhr': 5.19.0 - '@algolia/requester-fetch': 5.19.0 - '@algolia/requester-node-http': 5.19.0 + '@algolia/client-common': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 - '@algolia/requester-browser-xhr@5.19.0': + '@algolia/requester-browser-xhr@5.20.0': dependencies: - '@algolia/client-common': 5.19.0 + '@algolia/client-common': 5.20.0 - '@algolia/requester-fetch@5.19.0': + '@algolia/requester-fetch@5.20.0': dependencies: - '@algolia/client-common': 5.19.0 + '@algolia/client-common': 5.20.0 - '@algolia/requester-node-http@5.19.0': + '@algolia/requester-node-http@5.20.0': dependencies: - '@algolia/client-common': 5.19.0 + '@algolia/client-common': 5.20.0 '@alloc/quick-lru@5.2.0': {} @@ -13928,31 +13990,31 @@ snapshots: '@antfu/ni@0.21.4': {} - '@asamuzakjp/css-color@2.8.2': + '@asamuzakjp/css-color@2.8.3': dependencies: '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) '@csstools/css-tokenizer': 3.0.3 - lru-cache: 11.0.2 + lru-cache: 10.4.3 '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.709.0 + '@aws-sdk/types': 3.734.0 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.709.0 + '@aws-sdk/types': 3.734.0 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.709.0 + '@aws-sdk/types': 3.734.0 '@aws-sdk/util-locate-window': 3.723.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -13962,7 +14024,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.709.0 + '@aws-sdk/types': 3.734.0 '@aws-sdk/util-locate-window': 3.723.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -13970,7 +14032,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.709.0 + '@aws-sdk/types': 3.734.0 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -13979,506 +14041,457 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.709.0 + '@aws-sdk/types': 3.734.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.712.0(aws-crt@1.24.0)': + '@aws-sdk/client-s3@3.741.0(aws-crt@1.25.3)': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0) - '@aws-sdk/client-sts': 3.712.0(aws-crt@1.24.0) - '@aws-sdk/core': 3.709.0 - '@aws-sdk/credential-provider-node': 3.712.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0))(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0) - '@aws-sdk/middleware-bucket-endpoint': 3.709.0 - '@aws-sdk/middleware-expect-continue': 3.709.0 - '@aws-sdk/middleware-flexible-checksums': 3.709.0 - '@aws-sdk/middleware-host-header': 3.709.0 - '@aws-sdk/middleware-location-constraint': 3.709.0 - '@aws-sdk/middleware-logger': 3.709.0 - '@aws-sdk/middleware-recursion-detection': 3.709.0 - '@aws-sdk/middleware-sdk-s3': 3.709.0 - '@aws-sdk/middleware-ssec': 3.709.0 - '@aws-sdk/middleware-user-agent': 3.709.0 - '@aws-sdk/region-config-resolver': 3.709.0 - '@aws-sdk/signature-v4-multi-region': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@aws-sdk/util-endpoints': 3.709.0 - '@aws-sdk/util-user-agent-browser': 3.709.0 - '@aws-sdk/util-user-agent-node': 3.712.0(aws-crt@1.24.0) - '@aws-sdk/xml-builder': 3.709.0 - '@smithy/config-resolver': 3.0.13 - '@smithy/core': 2.5.7 - '@smithy/eventstream-serde-browser': 3.0.14 - '@smithy/eventstream-serde-config-resolver': 3.0.11 - '@smithy/eventstream-serde-node': 3.0.13 - '@smithy/fetch-http-handler': 4.1.3 - '@smithy/hash-blob-browser': 3.1.10 - '@smithy/hash-node': 3.0.11 - '@smithy/hash-stream-node': 3.1.10 - '@smithy/invalid-dependency': 3.0.11 - '@smithy/md5-js': 3.0.11 - '@smithy/middleware-content-length': 3.0.13 - '@smithy/middleware-endpoint': 3.2.8 - '@smithy/middleware-retry': 3.0.34 - '@smithy/middleware-serde': 3.0.11 - '@smithy/middleware-stack': 3.0.11 - '@smithy/node-config-provider': 3.1.12 - '@smithy/node-http-handler': 3.3.3 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.34 - '@smithy/util-defaults-mode-node': 3.0.34 - '@smithy/util-endpoints': 2.1.7 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-retry': 3.0.11 - '@smithy/util-stream': 3.3.4 - '@smithy/util-utf8': 3.0.0 - '@smithy/util-waiter': 3.2.0 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/credential-provider-node': 3.741.0(aws-crt@1.25.3) + '@aws-sdk/middleware-bucket-endpoint': 3.734.0 + '@aws-sdk/middleware-expect-continue': 3.734.0 + '@aws-sdk/middleware-flexible-checksums': 3.735.0 + '@aws-sdk/middleware-host-header': 3.734.0 + '@aws-sdk/middleware-location-constraint': 3.734.0 + '@aws-sdk/middleware-logger': 3.734.0 + '@aws-sdk/middleware-recursion-detection': 3.734.0 + '@aws-sdk/middleware-sdk-s3': 3.740.0 + '@aws-sdk/middleware-ssec': 3.734.0 + '@aws-sdk/middleware-user-agent': 3.734.0 + '@aws-sdk/region-config-resolver': 3.734.0 + '@aws-sdk/signature-v4-multi-region': 3.740.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-endpoints': 3.734.0 + '@aws-sdk/util-user-agent-browser': 3.734.0 + '@aws-sdk/util-user-agent-node': 3.734.0(aws-crt@1.25.3) + '@aws-sdk/xml-builder': 3.734.0 + '@smithy/config-resolver': 4.0.1 + '@smithy/core': 3.1.2 + '@smithy/eventstream-serde-browser': 4.0.1 + '@smithy/eventstream-serde-config-resolver': 4.0.1 + '@smithy/eventstream-serde-node': 4.0.1 + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/hash-blob-browser': 4.0.1 + '@smithy/hash-node': 4.0.1 + '@smithy/hash-stream-node': 4.0.1 + '@smithy/invalid-dependency': 4.0.1 + '@smithy/md5-js': 4.0.1 + '@smithy/middleware-content-length': 4.0.1 + '@smithy/middleware-endpoint': 4.0.3 + '@smithy/middleware-retry': 4.0.4 + '@smithy/middleware-serde': 4.0.2 + '@smithy/middleware-stack': 4.0.1 + '@smithy/node-config-provider': 4.0.1 + '@smithy/node-http-handler': 4.0.2 + '@smithy/protocol-http': 5.0.1 + '@smithy/smithy-client': 4.1.3 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.4 + '@smithy/util-defaults-mode-node': 4.0.4 + '@smithy/util-endpoints': 3.0.1 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-retry': 4.0.1 + '@smithy/util-stream': 4.0.2 + '@smithy/util-utf8': 4.0.0 + '@smithy/util-waiter': 4.0.2 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0)': + '@aws-sdk/client-sso@3.734.0(aws-crt@1.25.3)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.712.0(aws-crt@1.24.0) - '@aws-sdk/core': 3.709.0 - '@aws-sdk/credential-provider-node': 3.712.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0))(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0) - '@aws-sdk/middleware-host-header': 3.709.0 - '@aws-sdk/middleware-logger': 3.709.0 - '@aws-sdk/middleware-recursion-detection': 3.709.0 - '@aws-sdk/middleware-user-agent': 3.709.0 - '@aws-sdk/region-config-resolver': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@aws-sdk/util-endpoints': 3.709.0 - '@aws-sdk/util-user-agent-browser': 3.709.0 - '@aws-sdk/util-user-agent-node': 3.712.0(aws-crt@1.24.0) - '@smithy/config-resolver': 3.0.13 - '@smithy/core': 2.5.7 - '@smithy/fetch-http-handler': 4.1.3 - '@smithy/hash-node': 3.0.11 - '@smithy/invalid-dependency': 3.0.11 - '@smithy/middleware-content-length': 3.0.13 - '@smithy/middleware-endpoint': 3.2.8 - '@smithy/middleware-retry': 3.0.34 - '@smithy/middleware-serde': 3.0.11 - '@smithy/middleware-stack': 3.0.11 - '@smithy/node-config-provider': 3.1.12 - '@smithy/node-http-handler': 3.3.3 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.34 - '@smithy/util-defaults-mode-node': 3.0.34 - '@smithy/util-endpoints': 2.1.7 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-retry': 3.0.11 - '@smithy/util-utf8': 3.0.0 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/middleware-host-header': 3.734.0 + '@aws-sdk/middleware-logger': 3.734.0 + '@aws-sdk/middleware-recursion-detection': 3.734.0 + '@aws-sdk/middleware-user-agent': 3.734.0 + '@aws-sdk/region-config-resolver': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-endpoints': 3.734.0 + '@aws-sdk/util-user-agent-browser': 3.734.0 + '@aws-sdk/util-user-agent-node': 3.734.0(aws-crt@1.25.3) + '@smithy/config-resolver': 4.0.1 + '@smithy/core': 3.1.2 + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/hash-node': 4.0.1 + '@smithy/invalid-dependency': 4.0.1 + '@smithy/middleware-content-length': 4.0.1 + '@smithy/middleware-endpoint': 4.0.3 + '@smithy/middleware-retry': 4.0.4 + '@smithy/middleware-serde': 4.0.2 + '@smithy/middleware-stack': 4.0.1 + '@smithy/node-config-provider': 4.0.1 + '@smithy/node-http-handler': 4.0.2 + '@smithy/protocol-http': 5.0.1 + '@smithy/smithy-client': 4.1.3 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.4 + '@smithy/util-defaults-mode-node': 4.0.4 + '@smithy/util-endpoints': 3.0.1 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-retry': 4.0.1 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.712.0(aws-crt@1.24.0)': + '@aws-sdk/core@3.734.0': dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.709.0 - '@aws-sdk/middleware-host-header': 3.709.0 - '@aws-sdk/middleware-logger': 3.709.0 - '@aws-sdk/middleware-recursion-detection': 3.709.0 - '@aws-sdk/middleware-user-agent': 3.709.0 - '@aws-sdk/region-config-resolver': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@aws-sdk/util-endpoints': 3.709.0 - '@aws-sdk/util-user-agent-browser': 3.709.0 - '@aws-sdk/util-user-agent-node': 3.712.0(aws-crt@1.24.0) - '@smithy/config-resolver': 3.0.13 - '@smithy/core': 2.5.7 - '@smithy/fetch-http-handler': 4.1.3 - '@smithy/hash-node': 3.0.11 - '@smithy/invalid-dependency': 3.0.11 - '@smithy/middleware-content-length': 3.0.13 - '@smithy/middleware-endpoint': 3.2.8 - '@smithy/middleware-retry': 3.0.34 - '@smithy/middleware-serde': 3.0.11 - '@smithy/middleware-stack': 3.0.11 - '@smithy/node-config-provider': 3.1.12 - '@smithy/node-http-handler': 3.3.3 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.34 - '@smithy/util-defaults-mode-node': 3.0.34 - '@smithy/util-endpoints': 2.1.7 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-retry': 3.0.11 - '@smithy/util-utf8': 3.0.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0)': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0) - '@aws-sdk/core': 3.709.0 - '@aws-sdk/credential-provider-node': 3.712.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0))(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0) - '@aws-sdk/middleware-host-header': 3.709.0 - '@aws-sdk/middleware-logger': 3.709.0 - '@aws-sdk/middleware-recursion-detection': 3.709.0 - '@aws-sdk/middleware-user-agent': 3.709.0 - '@aws-sdk/region-config-resolver': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@aws-sdk/util-endpoints': 3.709.0 - '@aws-sdk/util-user-agent-browser': 3.709.0 - '@aws-sdk/util-user-agent-node': 3.712.0(aws-crt@1.24.0) - '@smithy/config-resolver': 3.0.13 - '@smithy/core': 2.5.7 - '@smithy/fetch-http-handler': 4.1.3 - '@smithy/hash-node': 3.0.11 - '@smithy/invalid-dependency': 3.0.11 - '@smithy/middleware-content-length': 3.0.13 - '@smithy/middleware-endpoint': 3.2.8 - '@smithy/middleware-retry': 3.0.34 - '@smithy/middleware-serde': 3.0.11 - '@smithy/middleware-stack': 3.0.11 - '@smithy/node-config-provider': 3.1.12 - '@smithy/node-http-handler': 3.3.3 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.34 - '@smithy/util-defaults-mode-node': 3.0.34 - '@smithy/util-endpoints': 2.1.7 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-retry': 3.0.11 - '@smithy/util-utf8': 3.0.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/core@3.709.0': - dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/core': 2.5.7 - '@smithy/node-config-provider': 3.1.12 - '@smithy/property-provider': 3.1.11 - '@smithy/protocol-http': 4.1.8 - '@smithy/signature-v4': 4.2.4 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 - '@smithy/util-middleware': 3.0.11 + '@aws-sdk/types': 3.734.0 + '@smithy/core': 3.1.2 + '@smithy/node-config-provider': 4.0.1 + '@smithy/property-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/signature-v4': 5.0.1 + '@smithy/smithy-client': 4.1.3 + '@smithy/types': 4.1.0 + '@smithy/util-middleware': 4.0.1 fast-xml-parser: 4.4.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.709.0': + '@aws-sdk/credential-provider-env@3.734.0': dependencies: - '@aws-sdk/core': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@smithy/property-provider': 3.1.11 - '@smithy/types': 3.7.2 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@smithy/property-provider': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.709.0': + '@aws-sdk/credential-provider-http@3.734.0': dependencies: - '@aws-sdk/core': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@smithy/fetch-http-handler': 4.1.3 - '@smithy/node-http-handler': 3.3.3 - '@smithy/property-provider': 3.1.11 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 - '@smithy/util-stream': 3.3.4 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/node-http-handler': 4.0.2 + '@smithy/property-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/smithy-client': 4.1.3 + '@smithy/types': 4.1.0 + '@smithy/util-stream': 4.0.2 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.712.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0))(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0)': + '@aws-sdk/credential-provider-ini@3.741.0(aws-crt@1.25.3)': dependencies: - '@aws-sdk/client-sts': 3.712.0(aws-crt@1.24.0) - '@aws-sdk/core': 3.709.0 - '@aws-sdk/credential-provider-env': 3.709.0 - '@aws-sdk/credential-provider-http': 3.709.0 - '@aws-sdk/credential-provider-process': 3.709.0 - '@aws-sdk/credential-provider-sso': 3.712.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0))(aws-crt@1.24.0) - '@aws-sdk/credential-provider-web-identity': 3.709.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0)) - '@aws-sdk/types': 3.709.0 - '@smithy/credential-provider-imds': 3.2.8 - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/credential-provider-env': 3.734.0 + '@aws-sdk/credential-provider-http': 3.734.0 + '@aws-sdk/credential-provider-process': 3.734.0 + '@aws-sdk/credential-provider-sso': 3.734.0(aws-crt@1.25.3) + '@aws-sdk/credential-provider-web-identity': 3.734.0(aws-crt@1.25.3) + '@aws-sdk/nested-clients': 3.734.0(aws-crt@1.25.3) + '@aws-sdk/types': 3.734.0 + '@smithy/credential-provider-imds': 4.0.1 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-node@3.712.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0))(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0)': + '@aws-sdk/credential-provider-node@3.741.0(aws-crt@1.25.3)': dependencies: - '@aws-sdk/credential-provider-env': 3.709.0 - '@aws-sdk/credential-provider-http': 3.709.0 - '@aws-sdk/credential-provider-ini': 3.712.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0))(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0) - '@aws-sdk/credential-provider-process': 3.709.0 - '@aws-sdk/credential-provider-sso': 3.712.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0))(aws-crt@1.24.0) - '@aws-sdk/credential-provider-web-identity': 3.709.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0)) - '@aws-sdk/types': 3.709.0 - '@smithy/credential-provider-imds': 3.2.8 - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/credential-provider-env': 3.734.0 + '@aws-sdk/credential-provider-http': 3.734.0 + '@aws-sdk/credential-provider-ini': 3.741.0(aws-crt@1.25.3) + '@aws-sdk/credential-provider-process': 3.734.0 + '@aws-sdk/credential-provider-sso': 3.734.0(aws-crt@1.25.3) + '@aws-sdk/credential-provider-web-identity': 3.734.0(aws-crt@1.25.3) + '@aws-sdk/types': 3.734.0 + '@smithy/credential-provider-imds': 4.0.1 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-process@3.709.0': + '@aws-sdk/credential-provider-process@3.734.0': dependencies: - '@aws-sdk/core': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.712.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0))(aws-crt@1.24.0)': + '@aws-sdk/credential-provider-sso@3.734.0(aws-crt@1.25.3)': dependencies: - '@aws-sdk/client-sso': 3.712.0(aws-crt@1.24.0) - '@aws-sdk/core': 3.709.0 - '@aws-sdk/token-providers': 3.709.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0)) - '@aws-sdk/types': 3.709.0 - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/client-sso': 3.734.0(aws-crt@1.25.3) + '@aws-sdk/core': 3.734.0 + '@aws-sdk/token-providers': 3.734.0(aws-crt@1.25.3) + '@aws-sdk/types': 3.734.0 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-web-identity@3.709.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))': + '@aws-sdk/credential-provider-web-identity@3.734.0(aws-crt@1.25.3)': dependencies: - '@aws-sdk/client-sts': 3.712.0(aws-crt@1.24.0) - '@aws-sdk/core': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@smithy/property-provider': 3.1.11 - '@smithy/types': 3.7.2 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/nested-clients': 3.734.0(aws-crt@1.25.3) + '@aws-sdk/types': 3.734.0 + '@smithy/property-provider': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/middleware-bucket-endpoint@3.734.0': + dependencies: + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-arn-parser': 3.723.0 + '@smithy/node-config-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-config-provider': 4.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-bucket-endpoint@3.709.0': + '@aws-sdk/middleware-expect-continue@3.734.0': dependencies: - '@aws-sdk/types': 3.709.0 - '@aws-sdk/util-arn-parser': 3.693.0 - '@smithy/node-config-provider': 3.1.12 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - '@smithy/util-config-provider': 3.0.0 + '@aws-sdk/types': 3.734.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.709.0': - dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - tslib: 2.8.1 - - '@aws-sdk/middleware-flexible-checksums@3.709.0': + '@aws-sdk/middleware-flexible-checksums@3.735.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@smithy/is-array-buffer': 3.0.0 - '@smithy/node-config-provider': 3.1.12 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-stream': 3.3.4 - '@smithy/util-utf8': 3.0.0 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@smithy/is-array-buffer': 4.0.0 + '@smithy/node-config-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-stream': 4.0.2 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.709.0': + '@aws-sdk/middleware-host-header@3.734.0': dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.734.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.709.0': + '@aws-sdk/middleware-location-constraint@3.734.0': dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.734.0 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.709.0': + '@aws-sdk/middleware-logger@3.734.0': dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.734.0 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.709.0': + '@aws-sdk/middleware-recursion-detection@3.734.0': dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.734.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.709.0': + '@aws-sdk/middleware-sdk-s3@3.740.0': dependencies: - '@aws-sdk/core': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@aws-sdk/util-arn-parser': 3.693.0 - '@smithy/core': 2.5.7 - '@smithy/node-config-provider': 3.1.12 - '@smithy/protocol-http': 4.1.8 - '@smithy/signature-v4': 4.2.4 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 - '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-stream': 3.3.4 - '@smithy/util-utf8': 3.0.0 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-arn-parser': 3.723.0 + '@smithy/core': 3.1.2 + '@smithy/node-config-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/signature-v4': 5.0.1 + '@smithy/smithy-client': 4.1.3 + '@smithy/types': 4.1.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-stream': 4.0.2 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.709.0': + '@aws-sdk/middleware-ssec@3.734.0': dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.734.0 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.709.0': + '@aws-sdk/middleware-user-agent@3.734.0': dependencies: - '@aws-sdk/core': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@aws-sdk/util-endpoints': 3.709.0 - '@smithy/core': 2.5.7 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-endpoints': 3.734.0 + '@smithy/core': 3.1.2 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/region-config-resolver@3.709.0': + '@aws-sdk/nested-clients@3.734.0(aws-crt@1.25.3)': dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/node-config-provider': 3.1.12 - '@smithy/types': 3.7.2 - '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.11 - tslib: 2.8.1 - - '@aws-sdk/s3-presigned-post@3.712.0(aws-crt@1.24.0)': - dependencies: - '@aws-sdk/client-s3': 3.712.0(aws-crt@1.24.0) - '@aws-sdk/types': 3.709.0 - '@aws-sdk/util-format-url': 3.709.0 - '@smithy/middleware-endpoint': 3.2.8 - '@smithy/signature-v4': 4.2.4 - '@smithy/types': 3.7.2 - '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-utf8': 3.0.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.734.0 + '@aws-sdk/middleware-host-header': 3.734.0 + '@aws-sdk/middleware-logger': 3.734.0 + '@aws-sdk/middleware-recursion-detection': 3.734.0 + '@aws-sdk/middleware-user-agent': 3.734.0 + '@aws-sdk/region-config-resolver': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-endpoints': 3.734.0 + '@aws-sdk/util-user-agent-browser': 3.734.0 + '@aws-sdk/util-user-agent-node': 3.734.0(aws-crt@1.25.3) + '@smithy/config-resolver': 4.0.1 + '@smithy/core': 3.1.2 + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/hash-node': 4.0.1 + '@smithy/invalid-dependency': 4.0.1 + '@smithy/middleware-content-length': 4.0.1 + '@smithy/middleware-endpoint': 4.0.3 + '@smithy/middleware-retry': 4.0.4 + '@smithy/middleware-serde': 4.0.2 + '@smithy/middleware-stack': 4.0.1 + '@smithy/node-config-provider': 4.0.1 + '@smithy/node-http-handler': 4.0.2 + '@smithy/protocol-http': 5.0.1 + '@smithy/smithy-client': 4.1.3 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.4 + '@smithy/util-defaults-mode-node': 4.0.4 + '@smithy/util-endpoints': 3.0.1 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-retry': 4.0.1 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/s3-request-presigner@3.712.0': + '@aws-sdk/region-config-resolver@3.734.0': dependencies: - '@aws-sdk/signature-v4-multi-region': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@aws-sdk/util-format-url': 3.709.0 - '@smithy/middleware-endpoint': 3.2.8 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.734.0 + '@smithy/node-config-provider': 4.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.1 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.709.0': + '@aws-sdk/s3-presigned-post@3.741.0(aws-crt@1.25.3)': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@smithy/protocol-http': 4.1.8 - '@smithy/signature-v4': 4.2.4 - '@smithy/types': 3.7.2 + '@aws-sdk/client-s3': 3.741.0(aws-crt@1.25.3) + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-format-url': 3.734.0 + '@smithy/middleware-endpoint': 4.0.3 + '@smithy/signature-v4': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/s3-request-presigner@3.741.0': + dependencies: + '@aws-sdk/signature-v4-multi-region': 3.740.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-format-url': 3.734.0 + '@smithy/middleware-endpoint': 4.0.3 + '@smithy/protocol-http': 5.0.1 + '@smithy/smithy-client': 4.1.3 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.709.0(@aws-sdk/client-sso-oidc@3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0))': + '@aws-sdk/signature-v4-multi-region@3.740.0': dependencies: - '@aws-sdk/client-sso-oidc': 3.712.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0))(aws-crt@1.24.0) - '@aws-sdk/types': 3.709.0 - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/middleware-sdk-s3': 3.740.0 + '@aws-sdk/types': 3.734.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/signature-v4': 5.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/types@3.709.0': + '@aws-sdk/token-providers@3.734.0(aws-crt@1.25.3)': dependencies: - '@smithy/types': 3.7.2 + '@aws-sdk/nested-clients': 3.734.0(aws-crt@1.25.3) + '@aws-sdk/types': 3.734.0 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.734.0': + dependencies: + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@aws-sdk/util-arn-parser@3.693.0': + '@aws-sdk/util-arn-parser@3.723.0': dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.709.0': + '@aws-sdk/util-endpoints@3.734.0': dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/types': 3.7.2 - '@smithy/util-endpoints': 2.1.7 + '@aws-sdk/types': 3.734.0 + '@smithy/types': 4.1.0 + '@smithy/util-endpoints': 3.0.1 tslib: 2.8.1 - '@aws-sdk/util-format-url@3.709.0': + '@aws-sdk/util-format-url@3.734.0': dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/querystring-builder': 3.0.11 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.734.0 + '@smithy/querystring-builder': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 '@aws-sdk/util-locate-window@3.723.0': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.709.0': + '@aws-sdk/util-user-agent-browser@3.734.0': dependencies: - '@aws-sdk/types': 3.709.0 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.734.0 + '@smithy/types': 4.1.0 bowser: 2.11.0 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.712.0(aws-crt@1.24.0)': + '@aws-sdk/util-user-agent-node@3.734.0(aws-crt@1.25.3)': dependencies: - '@aws-sdk/middleware-user-agent': 3.709.0 - '@aws-sdk/types': 3.709.0 - '@smithy/node-config-provider': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/middleware-user-agent': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@smithy/node-config-provider': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 optionalDependencies: - aws-crt: 1.24.0 + aws-crt: 1.25.3 '@aws-sdk/util-utf8-browser@3.259.0': dependencies: tslib: 2.8.1 - '@aws-sdk/xml-builder@3.709.0': + '@aws-sdk/xml-builder@3.734.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 '@azure/abort-controller@2.1.2': @@ -14549,7 +14562,7 @@ snapshots: fast-xml-parser: 4.5.1 tslib: 2.8.1 - '@azure/identity@4.5.0': + '@azure/identity@4.6.0': dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.9.0 @@ -14558,7 +14571,7 @@ snapshots: '@azure/core-tracing': 1.2.0 '@azure/core-util': 1.11.0 '@azure/logger': 1.1.4 - '@azure/msal-browser': 3.28.0 + '@azure/msal-browser': 4.0.2 '@azure/msal-node': 2.16.2 events: 3.3.0 jws: 4.0.0 @@ -14575,7 +14588,7 @@ snapshots: '@azure/microsoft-playwright-testing@1.0.0-beta.6(@playwright/test@1.49.1)': dependencies: '@azure/core-rest-pipeline': 1.18.2 - '@azure/identity': 4.5.0 + '@azure/identity': 4.6.0 '@azure/logger': 1.1.4 '@azure/storage-blob': 12.26.0 '@playwright/test': 1.49.1 @@ -14583,12 +14596,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/msal-browser@3.28.0': + '@azure/msal-browser@4.0.2': dependencies: - '@azure/msal-common': 14.16.0 + '@azure/msal-common': 15.0.2 '@azure/msal-common@14.16.0': {} + '@azure/msal-common@15.0.2': {} + '@azure/msal-node@2.16.2': dependencies: '@azure/msal-common': 14.16.0 @@ -14623,22 +14638,20 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.3': {} - '@babel/compat-data@7.26.5': {} '@babel/core@7.26.0': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.3 - '@babel/helper-compilation-targets': 7.25.9 + '@babel/generator': 7.26.5 + '@babel/helper-compilation-targets': 7.26.5 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.3 + '@babel/helpers': 7.26.7 + '@babel/parser': 7.26.7 '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 convert-source-map: 2.0.0 debug: 4.4.0 gensync: 1.0.0-beta.2 @@ -14647,7 +14660,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/eslint-parser@7.25.9(@babel/core@7.26.0)(eslint@8.57.0)': + '@babel/eslint-parser@7.26.5(@babel/core@7.26.0)(eslint@8.57.0)': dependencies: '@babel/core': 7.26.0 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 @@ -14655,33 +14668,17 @@ snapshots: eslint-visitor-keys: 2.1.0 semver: 6.3.1 - '@babel/generator@7.26.3': - dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 3.1.0 - '@babel/generator@7.26.5': dependencies: - '@babel/parser': 7.26.5 - '@babel/types': 7.26.5 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.25.9': dependencies: - '@babel/types': 7.26.3 - - '@babel/helper-compilation-targets@7.25.9': - dependencies: - '@babel/compat-data': 7.26.3 - '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.4 - lru-cache: 5.1.1 - semver: 6.3.1 + '@babel/types': 7.26.7 '@babel/helper-compilation-targets@7.26.5': dependencies: @@ -14697,9 +14694,9 @@ snapshots: '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-member-expression-to-functions': 7.25.9 '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.7 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -14714,8 +14711,8 @@ snapshots: '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 debug: 4.4.0 lodash.debounce: 4.0.8 resolve: 1.22.10 @@ -14724,19 +14721,19 @@ snapshots: '@babel/helper-environment-visitor@7.24.7': dependencies: - '@babel/types': 7.26.5 + '@babel/types': 7.26.7 '@babel/helper-member-expression-to-functions@7.25.9': dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.5 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.25.9': dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color @@ -14745,15 +14742,13 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-module-imports': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.7 transitivePeerDependencies: - supports-color '@babel/helper-optimise-call-expression@7.25.9': dependencies: - '@babel/types': 7.26.5 - - '@babel/helper-plugin-utils@7.25.9': {} + '@babel/types': 7.26.7 '@babel/helper-plugin-utils@7.26.5': {} @@ -14762,16 +14757,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-wrap-function': 7.25.9 - '@babel/traverse': 7.26.4 - transitivePeerDependencies: - - supports-color - - '@babel/helper-replace-supers@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.7 transitivePeerDependencies: - supports-color @@ -14780,14 +14766,14 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-member-expression-to-functions': 7.25.9 '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/traverse': 7.26.5 + '@babel/traverse': 7.26.7 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.25.9': dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.5 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color @@ -14800,15 +14786,15 @@ snapshots: '@babel/helper-wrap-function@7.25.9': dependencies: '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.5 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color - '@babel/helpers@7.26.0': + '@babel/helpers@7.26.7': dependencies: '@babel/template': 7.25.9 - '@babel/types': 7.26.3 + '@babel/types': 7.26.7 '@babel/highlight@7.25.9': dependencies: @@ -14817,19 +14803,15 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/parser@7.26.3': + '@babel/parser@7.26.7': dependencies: - '@babel/types': 7.26.3 - - '@babel/parser@7.26.5': - dependencies: - '@babel/types': 7.26.5 + '@babel/types': 7.26.7 '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.26.5 - '@babel/traverse': 7.26.5 + '@babel/traverse': 7.26.7 transitivePeerDependencies: - supports-color @@ -14856,7 +14838,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.26.5 - '@babel/traverse': 7.26.5 + '@babel/traverse': 7.26.7 transitivePeerDependencies: - supports-color @@ -14864,7 +14846,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.0) transitivePeerDependencies: @@ -14874,7 +14856,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color @@ -14882,7 +14864,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-decorators': 7.25.9(@babel/core@7.26.0) transitivePeerDependencies: - supports-color @@ -14890,45 +14872,45 @@ snapshots: '@babel/plugin-proposal-export-default-from@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.0) '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.0) '@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.0) '@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.26.0)': dependencies: - '@babel/compat-data': 7.26.3 + '@babel/compat-data': 7.26.5 '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0) '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) '@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.0) '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0) transitivePeerDependencies: @@ -14941,42 +14923,42 @@ snapshots: '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-decorators@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-export-default-from@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0)': dependencies: @@ -14986,67 +14968,67 @@ snapshots: '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)': dependencies: @@ -15057,14 +15039,14 @@ snapshots: '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.7 transitivePeerDependencies: - supports-color @@ -15072,7 +15054,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) transitivePeerDependencies: - supports-color @@ -15085,13 +15067,13 @@ snapshots: '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color @@ -15107,10 +15089,10 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) - '@babel/traverse': 7.26.4 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) + '@babel/traverse': 7.26.7 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -15118,13 +15100,13 @@ snapshots: '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/template': 7.25.9 '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0)': dependencies: @@ -15156,18 +15138,18 @@ snapshots: '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-flow-strip-types@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-flow-strip-types@7.26.5(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-flow': 7.26.0(@babel/core@7.26.0) '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color @@ -15175,9 +15157,9 @@ snapshots: '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 + '@babel/traverse': 7.26.7 transitivePeerDependencies: - supports-color @@ -15189,12 +15171,12 @@ snapshots: '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0)': dependencies: @@ -15213,7 +15195,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color @@ -15223,7 +15205,7 @@ snapshots: '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.5 + '@babel/traverse': 7.26.7 transitivePeerDependencies: - supports-color @@ -15239,18 +15221,13 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-nullish-coalescing-operator@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-nullish-coalescing-operator@7.26.6(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 @@ -15259,13 +15236,13 @@ snapshots: '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.0)': @@ -15279,12 +15256,12 @@ snapshots: '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color @@ -15292,13 +15269,13 @@ snapshots: '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color @@ -15307,7 +15284,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 transitivePeerDependencies: - supports-color @@ -15319,7 +15296,7 @@ snapshots: '@babel/plugin-transform-react-display-name@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-react-jsx-development@7.25.9(@babel/core@7.26.0)': dependencies: @@ -15331,21 +15308,21 @@ snapshots: '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) - '@babel/types': 7.26.3 + '@babel/types': 7.26.7 transitivePeerDependencies: - supports-color @@ -15353,12 +15330,12 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 regenerator-transform: 0.15.2 '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.0)': @@ -15376,7 +15353,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.0) babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0) babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.0) @@ -15387,12 +15364,12 @@ snapshots: '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 transitivePeerDependencies: - supports-color @@ -15400,24 +15377,24 @@ snapshots: '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.0)': + '@babel/plugin-transform-typeof-symbol@7.26.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-typescript@7.26.3(@babel/core@7.26.0)': + '@babel/plugin-transform-typescript@7.26.7(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) transitivePeerDependencies: @@ -15438,7 +15415,7 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.0)': dependencies: @@ -15446,7 +15423,7 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) '@babel/helper-plugin-utils': 7.26.5 - '@babel/preset-env@7.26.0(@babel/core@7.26.0)': + '@babel/preset-env@7.26.7(@babel/core@7.26.0)': dependencies: '@babel/compat-data': 7.26.5 '@babel/core': 7.26.0 @@ -15507,7 +15484,7 @@ snapshots: '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-typeof-symbol': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-typeof-symbol': 7.26.7(@babel/core@7.26.0) '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0) @@ -15524,21 +15501,21 @@ snapshots: '@babel/preset-flow@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-validator-option': 7.25.9 - '@babel/plugin-transform-flow-strip-types': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-flow-strip-types': 7.26.5(@babel/core@7.26.0) '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-plugin-utils': 7.26.5 - '@babel/types': 7.26.5 + '@babel/types': 7.26.7 esutils: 2.0.3 '@babel/preset-react@7.26.3(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-validator-option': 7.25.9 '@babel/plugin-transform-react-display-name': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) @@ -15550,11 +15527,11 @@ snapshots: '@babel/preset-typescript@7.26.0(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@babel/helper-validator-option': 7.25.9 '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) - '@babel/plugin-transform-typescript': 7.26.3(@babel/core@7.26.0) + '@babel/plugin-transform-typescript': 7.26.7(@babel/core@7.26.0) transitivePeerDependencies: - supports-color @@ -15567,49 +15544,36 @@ snapshots: pirates: 4.0.6 source-map-support: 0.5.21 - '@babel/runtime@7.26.0': + '@babel/runtime@7.26.7': dependencies: regenerator-runtime: 0.14.1 '@babel/template@7.25.9': dependencies: '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 - '@babel/traverse@7.26.4': - dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.3 - '@babel/parser': 7.26.3 - '@babel/template': 7.25.9 - '@babel/types': 7.26.3 - debug: 4.4.0 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - '@babel/traverse@7.26.5': + '@babel/traverse@7.26.7': dependencies: '@babel/code-frame': 7.26.2 '@babel/generator': 7.26.5 - '@babel/parser': 7.26.5 + '@babel/parser': 7.26.7 '@babel/template': 7.25.9 - '@babel/types': 7.26.5 + '@babel/types': 7.26.7 debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.26.3': + '@babel/types@7.26.7': dependencies: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@babel/types@7.26.5': - dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 + '@bcoe/v8-coverage@0.2.3': {} + + '@bcoe/v8-coverage@1.0.2': {} '@calcom/embed-core@1.5.1': {} @@ -15635,7 +15599,7 @@ snapshots: react-is: 18.3.1 react-shallow-renderer: 16.15.0(react@19.0.0) - '@changesets/apply-release-plan@7.0.7': + '@changesets/apply-release-plan@7.0.8': dependencies: '@changesets/config': 3.0.5 '@changesets/get-version-range-type': 0.4.0 @@ -15666,7 +15630,7 @@ snapshots: '@changesets/cli@2.27.10': dependencies: - '@changesets/apply-release-plan': 7.0.7 + '@changesets/apply-release-plan': 7.0.8 '@changesets/assemble-release-plan': 6.0.5 '@changesets/changelog-git': 0.2.0 '@changesets/config': 3.0.5 @@ -15779,7 +15743,7 @@ snapshots: '@chromatic-com/storybook@3.2.2(react@19.0.0)(storybook@8.4.7(prettier@3.4.2))': dependencies: - chromatic: 11.22.2 + chromatic: 11.25.1 filesize: 10.1.6 jsonfile: 6.1.0 react-confetti: 6.1.0(react@19.0.0) @@ -15852,14 +15816,14 @@ snapshots: '@docsearch/css@3.8.0': {} - '@docsearch/css@3.8.2': {} + '@docsearch/css@3.8.3': {} - '@docsearch/react@3.8.0(@algolia/client-search@5.19.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)': + '@docsearch/react@3.8.0(@algolia/client-search@5.20.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(search-insights@2.17.3)': dependencies: - '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0)(search-insights@2.17.3) - '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.19.0)(algoliasearch@5.19.0) + '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0)(search-insights@2.17.3) + '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.20.0)(algoliasearch@5.20.0) '@docsearch/css': 3.8.0 - algoliasearch: 5.19.0 + algoliasearch: 5.20.0 optionalDependencies: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) @@ -16197,26 +16161,26 @@ snapshots: dependencies: uuid: 8.3.2 - '@expo/cli@0.22.5(encoding@0.1.13)': + '@expo/cli@0.22.11(encoding@0.1.13)': dependencies: '@0no-co/graphql.web': 1.0.13 - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 '@expo/code-signing-certificates': 0.0.5 - '@expo/config': 10.0.7 - '@expo/config-plugins': 9.0.13 + '@expo/config': 10.0.8 + '@expo/config-plugins': 9.0.14 '@expo/devcert': 1.1.4 - '@expo/env': 0.4.0 - '@expo/image-utils': 0.6.3 - '@expo/json-file': 9.0.0 - '@expo/metro-config': 0.19.7 - '@expo/osascript': 2.1.4 - '@expo/package-manager': 1.7.0 - '@expo/plist': 0.2.0 - '@expo/prebuild-config': 8.0.24 + '@expo/env': 0.4.1 + '@expo/image-utils': 0.6.4 + '@expo/json-file': 9.0.1 + '@expo/metro-config': 0.19.9 + '@expo/osascript': 2.1.5 + '@expo/package-manager': 1.7.1 + '@expo/plist': 0.2.1 + '@expo/prebuild-config': 8.0.25 '@expo/rudder-sdk-node': 1.1.1(encoding@0.1.13) '@expo/spawn-async': 1.7.2 '@expo/xcpretty': 4.3.2 - '@react-native/dev-middleware': 0.76.5 + '@react-native/dev-middleware': 0.76.6 '@urql/core': 5.1.0 '@urql/exchange-retry': 1.3.0(@urql/core@5.1.0) accepts: 1.3.8 @@ -16266,7 +16230,7 @@ snapshots: temp-dir: 2.0.0 tempy: 0.7.1 terminal-link: 2.1.1 - undici: 6.21.0 + undici: 6.21.1 unique-string: 2.0.0 wrap-ansi: 7.0.0 ws: 8.18.0 @@ -16282,11 +16246,11 @@ snapshots: node-forge: 1.3.1 nullthrows: 1.1.1 - '@expo/config-plugins@9.0.13': + '@expo/config-plugins@9.0.14': dependencies: - '@expo/config-types': 52.0.2 - '@expo/json-file': 9.0.0 - '@expo/plist': 0.2.0 + '@expo/config-types': 52.0.3 + '@expo/json-file': 9.0.1 + '@expo/plist': 0.2.1 '@expo/sdk-runtime-versions': 1.0.0 chalk: 4.1.2 debug: 4.4.0 @@ -16301,14 +16265,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/config-types@52.0.2': {} + '@expo/config-types@52.0.3': {} - '@expo/config@10.0.7': + '@expo/config@10.0.8': dependencies: '@babel/code-frame': 7.10.4 - '@expo/config-plugins': 9.0.13 - '@expo/config-types': 52.0.2 - '@expo/json-file': 9.0.0 + '@expo/config-plugins': 9.0.14 + '@expo/config-types': 52.0.3 + '@expo/json-file': 9.0.1 deepmerge: 4.3.1 getenv: 1.0.0 glob: 10.4.5 @@ -16338,7 +16302,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/env@0.4.0': + '@expo/env@0.4.1': dependencies: chalk: 4.1.2 debug: 4.4.0 @@ -16348,7 +16312,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/fingerprint@0.11.3': + '@expo/fingerprint@0.11.7': dependencies: '@expo/spawn-async': 1.7.2 arg: 5.0.2 @@ -16363,7 +16327,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/image-utils@0.6.3': + '@expo/image-utils@0.6.4': dependencies: '@expo/spawn-async': 1.7.2 chalk: 4.1.2 @@ -16376,21 +16340,21 @@ snapshots: temp-dir: 2.0.0 unique-string: 2.0.0 - '@expo/json-file@9.0.0': + '@expo/json-file@9.0.1': dependencies: '@babel/code-frame': 7.10.4 json5: 2.2.3 write-file-atomic: 2.4.3 - '@expo/metro-config@0.19.7': + '@expo/metro-config@0.19.9': dependencies: '@babel/core': 7.26.0 - '@babel/generator': 7.26.3 - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 - '@expo/config': 10.0.7 - '@expo/env': 0.4.0 - '@expo/json-file': 9.0.0 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 + '@expo/config': 10.0.8 + '@expo/env': 0.4.1 + '@expo/json-file': 9.0.1 '@expo/spawn-async': 1.7.2 chalk: 4.1.2 debug: 4.4.0 @@ -16405,14 +16369,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/osascript@2.1.4': + '@expo/osascript@2.1.5': dependencies: '@expo/spawn-async': 1.7.2 exec-async: 2.2.0 - '@expo/package-manager@1.7.0': + '@expo/package-manager@1.7.1': dependencies: - '@expo/json-file': 9.0.0 + '@expo/json-file': 9.0.1 '@expo/spawn-async': 1.7.2 ansi-regex: 5.0.1 chalk: 4.1.2 @@ -16425,20 +16389,20 @@ snapshots: split: 1.0.1 sudo-prompt: 9.1.1 - '@expo/plist@0.2.0': + '@expo/plist@0.2.1': dependencies: '@xmldom/xmldom': 0.7.13 base64-js: 1.5.1 xmlbuilder: 14.0.0 - '@expo/prebuild-config@8.0.24': + '@expo/prebuild-config@8.0.25': dependencies: - '@expo/config': 10.0.7 - '@expo/config-plugins': 9.0.13 - '@expo/config-types': 52.0.2 - '@expo/image-utils': 0.6.3 - '@expo/json-file': 9.0.0 - '@react-native/normalize-colors': 0.76.5 + '@expo/config': 10.0.8 + '@expo/config-plugins': 9.0.14 + '@expo/config-types': 52.0.3 + '@expo/image-utils': 0.6.4 + '@expo/json-file': 9.0.1 + '@react-native/normalize-colors': 0.76.6 debug: 4.4.0 fs-extra: 9.1.0 resolve-from: 5.0.0 @@ -16505,14 +16469,14 @@ snapshots: dependencies: '@formatjs/fast-memoize': 2.2.6 '@formatjs/intl-localematcher': 0.5.10 - decimal.js: 10.4.3 + decimal.js: 10.5.0 tslib: 2.8.1 '@formatjs/fast-memoize@2.2.6': dependencies: tslib: 2.8.1 - '@formatjs/icu-messageformat-parser@2.9.8': + '@formatjs/icu-messageformat-parser@2.11.0': dependencies: '@formatjs/ecma402-abstract': 2.3.2 '@formatjs/icu-skeleton-parser': 1.8.12 @@ -16538,9 +16502,9 @@ snapshots: '@headlessui/react@2.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@floating-ui/react': 0.26.28(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@react-aria/focus': 3.19.0(react@19.0.0) - '@react-aria/interactions': 3.22.5(react@19.0.0) - '@tanstack/react-virtual': 3.11.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@react-aria/focus': 3.19.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@react-aria/interactions': 3.23.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@tanstack/react-virtual': 3.11.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) @@ -16554,7 +16518,7 @@ snapshots: '@httptoolkit/websocket-stream@6.0.1': dependencies: - '@types/ws': 8.5.13 + '@types/ws': 8.5.14 duplexify: 3.7.1 inherits: 2.0.4 isomorphic-ws: 4.0.1(ws@8.18.0) @@ -16868,7 +16832,7 @@ snapshots: '@lexical/utils': 0.21.0 lexical: 0.21.0 - '@lexical/react@0.21.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(yjs@13.6.21)': + '@lexical/react@0.21.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(yjs@13.6.23)': dependencies: '@lexical/clipboard': 0.21.0 '@lexical/code': 0.21.0 @@ -16887,7 +16851,7 @@ snapshots: '@lexical/table': 0.21.0 '@lexical/text': 0.21.0 '@lexical/utils': 0.21.0 - '@lexical/yjs': 0.21.0(yjs@13.6.21) + '@lexical/yjs': 0.21.0(yjs@13.6.23) lexical: 0.21.0 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) @@ -16923,23 +16887,23 @@ snapshots: '@lexical/table': 0.21.0 lexical: 0.21.0 - '@lexical/yjs@0.21.0(yjs@13.6.21)': + '@lexical/yjs@0.21.0(yjs@13.6.23)': dependencies: '@lexical/offset': 0.21.0 '@lexical/selection': 0.21.0 lexical: 0.21.0 - yjs: 13.6.21 + yjs: 13.6.23 '@manypkg/find-root@1.1.0': dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 '@manypkg/get-packages@1.1.3': dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -17097,7 +17061,7 @@ snapshots: dependencies: eslint-scope: 5.1.1 - '@noble/hashes@1.7.0': {} + '@noble/hashes@1.7.1': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -17129,7 +17093,7 @@ snapshots: '@opentelemetry/api@1.9.0': {} - '@opentelemetry/context-async-hooks@1.30.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -17138,7 +17102,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.28.0 - '@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.28.0 @@ -17146,7 +17110,7 @@ snapshots: '@opentelemetry/instrumentation-amqplib@0.45.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 transitivePeerDependencies: @@ -17155,7 +17119,7 @@ snapshots: '@opentelemetry/instrumentation-connect@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 '@types/connect': 3.4.36 @@ -17172,7 +17136,7 @@ snapshots: '@opentelemetry/instrumentation-express@0.46.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 transitivePeerDependencies: @@ -17181,7 +17145,7 @@ snapshots: '@opentelemetry/instrumentation-fastify@0.43.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 transitivePeerDependencies: @@ -17190,7 +17154,7 @@ snapshots: '@opentelemetry/instrumentation-fs@0.18.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -17212,7 +17176,7 @@ snapshots: '@opentelemetry/instrumentation-hapi@0.44.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 transitivePeerDependencies: @@ -17257,7 +17221,7 @@ snapshots: '@opentelemetry/instrumentation-koa@0.46.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 transitivePeerDependencies: @@ -17281,7 +17245,7 @@ snapshots: '@opentelemetry/instrumentation-mongoose@0.45.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 transitivePeerDependencies: @@ -17316,7 +17280,7 @@ snapshots: '@opentelemetry/instrumentation-pg@0.49.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) @@ -17346,7 +17310,7 @@ snapshots: '@opentelemetry/instrumentation-undici@0.9.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -17357,7 +17321,7 @@ snapshots: '@opentelemetry/api-logs': 0.52.1 '@types/shimmer': 1.2.0 import-in-the-middle: 1.12.0 - require-in-the-middle: 7.4.0 + require-in-the-middle: 7.5.0 semver: 7.6.3 shimmer: 1.2.1 transitivePeerDependencies: @@ -17369,7 +17333,7 @@ snapshots: '@opentelemetry/api-logs': 0.56.0 '@types/shimmer': 1.2.0 import-in-the-middle: 1.12.0 - require-in-the-middle: 7.4.0 + require-in-the-middle: 7.5.0 semver: 7.6.3 shimmer: 1.2.1 transitivePeerDependencies: @@ -17383,10 +17347,10 @@ snapshots: '@opentelemetry/core': 1.29.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 - '@opentelemetry/resources@1.30.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 '@opentelemetry/sdk-logs@0.56.0(@opentelemetry/api@1.9.0)': @@ -17396,17 +17360,17 @@ snapshots: '@opentelemetry/core': 1.29.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.29.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics@1.30.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 '@opentelemetry/semantic-conventions@1.27.0': {} @@ -17416,7 +17380,7 @@ snapshots: '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@otplib/core@12.0.1': {} @@ -17445,7 +17409,7 @@ snapshots: '@paralleldrive/cuid2@2.2.2': dependencies: - '@noble/hashes': 1.7.0 + '@noble/hashes': 1.7.1 '@pkgjs/parseargs@0.11.0': optional: true @@ -17588,11 +17552,11 @@ snapshots: dependencies: '@prisma/debug': 6.0.1 - '@prisma/instrumentation@5.19.1': + '@prisma/instrumentation@5.22.0': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -17651,536 +17615,539 @@ snapshots: '@radix-ui/primitive@1.1.1': {} - '@radix-ui/react-accordion@1.2.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-accordion@1.2.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collapsible': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-collapsible': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-arrow@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-arrow@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-checkbox@1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-checkbox@1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-collapsible@1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-collapsible@1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-collection@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-collection@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-compose-refs@1.1.1(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-compose-refs@1.1.1(@types/react@18.3.18)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-context@1.1.1(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-context@1.1.1(@types/react@18.3.18)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-dialog@1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-dialog@1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) aria-hidden: 1.2.4 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.0(@types/react@18.3.11)(react@19.0.0) + react-remove-scroll: 2.6.0(@types/react@18.3.18)(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-direction@1.1.0(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-direction@1.1.0(@types/react@18.3.18)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-dismissable-layer@1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-dismissable-layer@1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-dropdown-menu@2.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-dropdown-menu@2.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-menu': 2.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-menu': 2.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-focus-guards@1.1.1(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-focus-guards@1.1.1(@types/react@18.3.18)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-id@1.1.0(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-id@1.1.0(@types/react@18.3.18)(react@19.0.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-label@2.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-label@2.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-menu@2.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-menu@2.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@19.0.0) aria-hidden: 1.2.4 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.0(@types/react@18.3.11)(react@19.0.0) + react-remove-scroll: 2.6.0(@types/react@18.3.18)(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-popover@1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-popover@1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) aria-hidden: 1.2.4 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.0(@types/react@18.3.11)(react@19.0.0) + react-remove-scroll: 2.6.0(@types/react@18.3.18)(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-popper@1.2.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-popper@1.2.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-arrow': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-rect': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-arrow': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-rect': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.18)(react@19.0.0) '@radix-ui/rect': 1.1.0 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-portal@1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-portal@1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-radio-group@1.2.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-radio-group@1.2.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-roving-focus@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-roving-focus@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-select@2.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-select@2.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/number': 1.1.0 '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) aria-hidden: 1.2.4 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.0(@types/react@18.3.11)(react@19.0.0) + react-remove-scroll: 2.6.0(@types/react@18.3.18)(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-separator@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-separator@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-slider@1.2.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-slider@1.2.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/number': 1.1.0 '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-slot@1.1.1(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-slot@1.1.1(@types/react@18.3.18)(react@19.0.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-switch@1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-switch@1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-tabs@1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-tabs@1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-toggle-group@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-toggle-group@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-toggle': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-toggle': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-toggle@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-toggle@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-tooltip@1.1.5(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-tooltip@1.1.5(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.1(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) - '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.18)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.18)(react@19.0.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.18)(react@19.0.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.18)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-use-previous@1.1.0(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-use-previous@1.1.0(@types/react@18.3.18)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-use-rect@1.1.0(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-use-rect@1.1.0(@types/react@18.3.18)(react@19.0.0)': dependencies: '@radix-ui/rect': 1.1.0 react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-use-size@1.1.0(@types/react@18.3.11)(react@19.0.0)': + '@radix-ui/react-use-size@1.1.0(@types/react@18.3.18)(react@19.0.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.11)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.18)(react@19.0.0) react: 19.0.0 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 - '@types/react-dom': 19.0.2(@types/react@18.3.11) + '@types/react': 18.3.18 + '@types/react-dom': 19.0.2(@types/react@18.3.18) '@radix-ui/rect@1.1.0': {} - '@react-aria/focus@3.19.0(react@19.0.0)': + '@react-aria/focus@3.19.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@react-aria/interactions': 3.22.5(react@19.0.0) - '@react-aria/utils': 3.26.0(react@19.0.0) - '@react-types/shared': 3.26.0(react@19.0.0) + '@react-aria/interactions': 3.23.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@react-aria/utils': 3.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@react-types/shared': 3.27.0(react@19.0.0) '@swc/helpers': 0.5.15 clsx: 2.1.1 react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) - '@react-aria/interactions@3.22.5(react@19.0.0)': + '@react-aria/interactions@3.23.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@react-aria/ssr': 3.9.7(react@19.0.0) - '@react-aria/utils': 3.26.0(react@19.0.0) - '@react-types/shared': 3.26.0(react@19.0.0) + '@react-aria/utils': 3.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@react-types/shared': 3.27.0(react@19.0.0) '@swc/helpers': 0.5.15 react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) '@react-aria/ssr@3.9.7(react@19.0.0)': dependencies: '@swc/helpers': 0.5.15 react: 19.0.0 - '@react-aria/utils@3.26.0(react@19.0.0)': + '@react-aria/utils@3.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@react-aria/ssr': 3.9.7(react@19.0.0) '@react-stately/utils': 3.10.5(react@19.0.0) - '@react-types/shared': 3.26.0(react@19.0.0) + '@react-types/shared': 3.27.0(react@19.0.0) '@swc/helpers': 0.5.15 clsx: 2.1.1 react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) '@react-email/body@0.0.11(react@19.0.0)': dependencies: @@ -18294,10 +18261,15 @@ snapshots: dependencies: react: 19.0.0 - '@react-native-async-storage/async-storage@2.1.0(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1))': + '@react-native-async-storage/async-storage@2.1.0(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1))': dependencies: merge-options: 3.0.4 - react-native: 0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1) + react-native: 0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1) + + '@react-native-async-storage/async-storage@2.1.0(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))': + dependencies: + merge-options: 3.0.4 + react-native: 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1) '@react-native-community/cli-clean@13.6.9(encoding@0.1.13)': dependencies: @@ -18448,23 +18420,23 @@ snapshots: '@react-native/assets-registry@0.74.87': {} - '@react-native/assets-registry@0.76.5': {} + '@react-native/assets-registry@0.76.6': {} - '@react-native/babel-plugin-codegen@0.74.87(@babel/preset-env@7.26.0(@babel/core@7.26.0))': + '@react-native/babel-plugin-codegen@0.74.87(@babel/preset-env@7.26.7(@babel/core@7.26.0))': dependencies: - '@react-native/codegen': 0.74.87(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + '@react-native/codegen': 0.74.87(@babel/preset-env@7.26.7(@babel/core@7.26.0)) transitivePeerDependencies: - '@babel/preset-env' - supports-color - '@react-native/babel-plugin-codegen@0.76.5(@babel/preset-env@7.26.0(@babel/core@7.26.0))': + '@react-native/babel-plugin-codegen@0.76.6(@babel/preset-env@7.26.7(@babel/core@7.26.0))': dependencies: - '@react-native/codegen': 0.76.5(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + '@react-native/codegen': 0.76.6(@babel/preset-env@7.26.7(@babel/core@7.26.0)) transitivePeerDependencies: - '@babel/preset-env' - supports-color - '@react-native/babel-preset@0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))': + '@react-native/babel-preset@0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))': dependencies: '@babel/core': 7.26.0 '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.26.0) @@ -18487,7 +18459,7 @@ snapshots: '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-flow-strip-types': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-flow-strip-types': 7.26.5(@babel/core@7.26.0) '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) @@ -18503,17 +18475,17 @@ snapshots: '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-typescript': 7.26.3(@babel/core@7.26.0) + '@babel/plugin-transform-typescript': 7.26.7(@babel/core@7.26.0) '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0) '@babel/template': 7.25.9 - '@react-native/babel-plugin-codegen': 0.74.87(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + '@react-native/babel-plugin-codegen': 0.74.87(@babel/preset-env@7.26.7(@babel/core@7.26.0)) babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.26.0) react-refresh: 0.14.2 transitivePeerDependencies: - '@babel/preset-env' - supports-color - '@react-native/babel-preset@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))': + '@react-native/babel-preset@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))': dependencies: '@babel/core': 7.26.0 '@babel/plugin-proposal-export-default-from': 7.25.9(@babel/core@7.26.0) @@ -18529,14 +18501,14 @@ snapshots: '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-flow-strip-types': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-flow-strip-types': 7.26.5(@babel/core@7.26.0) '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.26.6(@babel/core@7.26.0) '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.0) @@ -18553,10 +18525,10 @@ snapshots: '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-typescript': 7.26.3(@babel/core@7.26.0) + '@babel/plugin-transform-typescript': 7.26.7(@babel/core@7.26.0) '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0) '@babel/template': 7.25.9 - '@react-native/babel-plugin-codegen': 0.76.5(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + '@react-native/babel-plugin-codegen': 0.76.6(@babel/preset-env@7.26.7(@babel/core@7.26.0)) babel-plugin-syntax-hermes-parser: 0.25.1 babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.26.0) react-refresh: 0.14.2 @@ -18564,39 +18536,39 @@ snapshots: - '@babel/preset-env' - supports-color - '@react-native/codegen@0.74.87(@babel/preset-env@7.26.0(@babel/core@7.26.0))': + '@react-native/codegen@0.74.87(@babel/preset-env@7.26.7(@babel/core@7.26.0))': dependencies: - '@babel/parser': 7.26.3 - '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + '@babel/parser': 7.26.7 + '@babel/preset-env': 7.26.7(@babel/core@7.26.0) glob: 7.2.3 hermes-parser: 0.19.1 invariant: 2.2.4 - jscodeshift: 0.14.0(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + jscodeshift: 0.14.0(@babel/preset-env@7.26.7(@babel/core@7.26.0)) mkdirp: 0.5.6 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - '@react-native/codegen@0.76.5(@babel/preset-env@7.26.0(@babel/core@7.26.0))': + '@react-native/codegen@0.76.6(@babel/preset-env@7.26.7(@babel/core@7.26.0))': dependencies: - '@babel/parser': 7.26.3 - '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + '@babel/parser': 7.26.7 + '@babel/preset-env': 7.26.7(@babel/core@7.26.0) glob: 7.2.3 hermes-parser: 0.23.1 invariant: 2.2.4 - jscodeshift: 0.14.0(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + jscodeshift: 0.14.0(@babel/preset-env@7.26.7(@babel/core@7.26.0)) mkdirp: 0.5.6 nullthrows: 1.1.1 yargs: 17.7.2 transitivePeerDependencies: - supports-color - '@react-native/community-cli-plugin@0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)': dependencies: '@react-native-community/cli-server-api': 13.6.9(encoding@0.1.13) '@react-native-community/cli-tools': 13.6.9(encoding@0.1.13) '@react-native/dev-middleware': 0.74.87(encoding@0.1.13) - '@react-native/metro-babel-transformer': 0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + '@react-native/metro-babel-transformer': 0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0)) chalk: 4.1.2 execa: 5.1.1 metro: 0.80.12 @@ -18613,10 +18585,10 @@ snapshots: - supports-color - utf-8-validate - '@react-native/community-cli-plugin@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(encoding@0.1.13)': + '@react-native/community-cli-plugin@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(encoding@0.1.13)': dependencies: - '@react-native/dev-middleware': 0.76.5 - '@react-native/metro-babel-transformer': 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + '@react-native/dev-middleware': 0.76.6 + '@react-native/metro-babel-transformer': 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0)) chalk: 4.1.2 execa: 5.1.1 invariant: 2.2.4 @@ -18638,7 +18610,7 @@ snapshots: '@react-native/debugger-frontend@0.74.87': {} - '@react-native/debugger-frontend@0.76.5': {} + '@react-native/debugger-frontend@0.76.6': {} '@react-native/dev-middleware@0.74.87(encoding@0.1.13)': dependencies: @@ -18661,10 +18633,10 @@ snapshots: - supports-color - utf-8-validate - '@react-native/dev-middleware@0.76.5': + '@react-native/dev-middleware@0.76.6': dependencies: '@isaacs/ttlcache': 1.4.1 - '@react-native/debugger-frontend': 0.76.5 + '@react-native/debugger-frontend': 0.76.6 chrome-launcher: 0.15.2 chromium-edge-launcher: 0.2.0 connect: 3.7.0 @@ -18681,26 +18653,26 @@ snapshots: '@react-native/gradle-plugin@0.74.87': {} - '@react-native/gradle-plugin@0.76.5': {} + '@react-native/gradle-plugin@0.76.6': {} '@react-native/js-polyfills@0.74.87': {} - '@react-native/js-polyfills@0.76.5': {} + '@react-native/js-polyfills@0.76.6': {} - '@react-native/metro-babel-transformer@0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))': + '@react-native/metro-babel-transformer@0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))': dependencies: '@babel/core': 7.26.0 - '@react-native/babel-preset': 0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + '@react-native/babel-preset': 0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0)) hermes-parser: 0.19.1 nullthrows: 1.1.1 transitivePeerDependencies: - '@babel/preset-env' - supports-color - '@react-native/metro-babel-transformer@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))': + '@react-native/metro-babel-transformer@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))': dependencies: '@babel/core': 7.26.0 - '@react-native/babel-preset': 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + '@react-native/babel-preset': 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0)) hermes-parser: 0.23.1 nullthrows: 1.1.1 transitivePeerDependencies: @@ -18709,32 +18681,32 @@ snapshots: '@react-native/normalize-colors@0.74.87': {} - '@react-native/normalize-colors@0.76.5': {} + '@react-native/normalize-colors@0.76.6': {} - '@react-native/virtualized-lists@0.74.87(@types/react@18.3.11)(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': + '@react-native/virtualized-lists@0.74.87(@types/react@18.3.11)(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 18.3.1 - react-native: 0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1) + react-native: 0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1) optionalDependencies: '@types/react': 18.3.11 - '@react-native/virtualized-lists@0.76.5(@types/react@19.0.1)(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': + '@react-native/virtualized-lists@0.76.6(@types/react@18.3.18)(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 18.3.1 - react-native: 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1) + react-native: 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1) optionalDependencies: - '@types/react': 19.0.1 + '@types/react': 18.3.18 '@react-stately/utils@3.10.5(react@19.0.0)': dependencies: '@swc/helpers': 0.5.15 react: 19.0.0 - '@react-types/shared@3.26.0(react@19.0.0)': + '@react-types/shared@3.27.0(react@19.0.0)': dependencies: react: 19.0.0 @@ -18771,12 +18743,12 @@ snapshots: require-from-string: 2.0.2 uri-js-replace: 1.0.1 - '@redocly/config@0.17.1': {} + '@redocly/config@0.20.3': {} - '@redocly/openapi-core@1.27.1(encoding@0.1.13)': + '@redocly/openapi-core@1.27.2(encoding@0.1.13)': dependencies: '@redocly/ajv': 8.11.2 - '@redocly/config': 0.17.1 + '@redocly/config': 0.20.3 colorette: 1.4.0 https-proxy-agent: 7.0.6 js-levenshtein: 1.1.6 @@ -18793,7 +18765,7 @@ snapshots: '@rnx-kit/chromium-edge-launcher@1.0.0': dependencies: - '@types/node': 18.19.70 + '@types/node': 18.19.74 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -18807,20 +18779,20 @@ snapshots: '@rollup/pluginutils': 5.1.4(rollup@3.29.5) commondir: 1.0.1 estree-walker: 2.0.2 - fdir: 6.4.2(picomatch@4.0.2) + fdir: 6.4.3(picomatch@4.0.2) is-reference: 1.2.1 magic-string: 0.30.17 picomatch: 4.0.2 optionalDependencies: rollup: 3.29.5 - '@rollup/plugin-inject@5.0.5(rollup@4.31.0)': + '@rollup/plugin-inject@5.0.5(rollup@4.32.1)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.31.0) + '@rollup/pluginutils': 5.1.4(rollup@4.32.1) estree-walker: 2.0.2 magic-string: 0.30.17 optionalDependencies: - rollup: 4.31.0 + rollup: 4.32.1 '@rollup/pluginutils@4.2.1': dependencies: @@ -18835,126 +18807,69 @@ snapshots: optionalDependencies: rollup: 3.29.5 - '@rollup/pluginutils@5.1.4(rollup@4.31.0)': + '@rollup/pluginutils@5.1.4(rollup@4.32.1)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.31.0 + rollup: 4.32.1 - '@rollup/rollup-android-arm-eabi@4.30.1': + '@rollup/rollup-android-arm-eabi@4.32.1': optional: true - '@rollup/rollup-android-arm-eabi@4.31.0': + '@rollup/rollup-android-arm64@4.32.1': optional: true - '@rollup/rollup-android-arm64@4.30.1': + '@rollup/rollup-darwin-arm64@4.32.1': optional: true - '@rollup/rollup-android-arm64@4.31.0': + '@rollup/rollup-darwin-x64@4.32.1': optional: true - '@rollup/rollup-darwin-arm64@4.30.1': + '@rollup/rollup-freebsd-arm64@4.32.1': optional: true - '@rollup/rollup-darwin-arm64@4.31.0': + '@rollup/rollup-freebsd-x64@4.32.1': optional: true - '@rollup/rollup-darwin-x64@4.30.1': + '@rollup/rollup-linux-arm-gnueabihf@4.32.1': optional: true - '@rollup/rollup-darwin-x64@4.31.0': + '@rollup/rollup-linux-arm-musleabihf@4.32.1': optional: true - '@rollup/rollup-freebsd-arm64@4.30.1': + '@rollup/rollup-linux-arm64-gnu@4.32.1': optional: true - '@rollup/rollup-freebsd-arm64@4.31.0': + '@rollup/rollup-linux-arm64-musl@4.32.1': optional: true - '@rollup/rollup-freebsd-x64@4.30.1': + '@rollup/rollup-linux-loongarch64-gnu@4.32.1': optional: true - '@rollup/rollup-freebsd-x64@4.31.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.32.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.30.1': + '@rollup/rollup-linux-riscv64-gnu@4.32.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.31.0': + '@rollup/rollup-linux-s390x-gnu@4.32.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.30.1': + '@rollup/rollup-linux-x64-gnu@4.32.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.31.0': + '@rollup/rollup-linux-x64-musl@4.32.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.30.1': + '@rollup/rollup-win32-arm64-msvc@4.32.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.31.0': + '@rollup/rollup-win32-ia32-msvc@4.32.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.30.1': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.31.0': - optional: true - - '@rollup/rollup-linux-loongarch64-gnu@4.30.1': - optional: true - - '@rollup/rollup-linux-loongarch64-gnu@4.31.0': - optional: true - - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': - optional: true - - '@rollup/rollup-linux-powerpc64le-gnu@4.31.0': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.30.1': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.31.0': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.30.1': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.31.0': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.30.1': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.31.0': - optional: true - - '@rollup/rollup-linux-x64-musl@4.30.1': - optional: true - - '@rollup/rollup-linux-x64-musl@4.31.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.30.1': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.31.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.30.1': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.31.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.30.1': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.31.0': + '@rollup/rollup-win32-x64-msvc@4.32.1': optional: true '@rtsao/scc@1.1.0': {} @@ -19005,33 +18920,33 @@ snapshots: domhandler: 5.0.3 selderee: 0.11.0 - '@sentry-internal/browser-utils@8.45.1': + '@sentry-internal/browser-utils@8.52.0': dependencies: - '@sentry/core': 8.45.1 + '@sentry/core': 8.52.0 - '@sentry-internal/feedback@8.45.1': + '@sentry-internal/feedback@8.52.0': dependencies: - '@sentry/core': 8.45.1 + '@sentry/core': 8.52.0 - '@sentry-internal/replay-canvas@8.45.1': + '@sentry-internal/replay-canvas@8.52.0': dependencies: - '@sentry-internal/replay': 8.45.1 - '@sentry/core': 8.45.1 + '@sentry-internal/replay': 8.52.0 + '@sentry/core': 8.52.0 - '@sentry-internal/replay@8.45.1': + '@sentry-internal/replay@8.52.0': dependencies: - '@sentry-internal/browser-utils': 8.45.1 - '@sentry/core': 8.45.1 + '@sentry-internal/browser-utils': 8.52.0 + '@sentry/core': 8.52.0 '@sentry/babel-plugin-component-annotate@2.22.7': {} - '@sentry/browser@8.45.1': + '@sentry/browser@8.52.0': dependencies: - '@sentry-internal/browser-utils': 8.45.1 - '@sentry-internal/feedback': 8.45.1 - '@sentry-internal/replay': 8.45.1 - '@sentry-internal/replay-canvas': 8.45.1 - '@sentry/core': 8.45.1 + '@sentry-internal/browser-utils': 8.52.0 + '@sentry-internal/feedback': 8.52.0 + '@sentry-internal/replay': 8.52.0 + '@sentry-internal/replay-canvas': 8.52.0 + '@sentry/core': 8.52.0 '@sentry/bundler-plugin-core@2.22.7(encoding@0.1.13)': dependencies: @@ -19087,19 +19002,19 @@ snapshots: - encoding - supports-color - '@sentry/core@8.45.1': {} + '@sentry/core@8.52.0': {} - '@sentry/nextjs@8.45.1(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(webpack@5.97.1)': + '@sentry/nextjs@8.52.0(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(webpack@5.97.1)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.28.0 '@rollup/plugin-commonjs': 28.0.1(rollup@3.29.5) - '@sentry-internal/browser-utils': 8.45.1 - '@sentry/core': 8.45.1 - '@sentry/node': 8.45.1 - '@sentry/opentelemetry': 8.45.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0) - '@sentry/react': 8.45.1(react@19.0.0) - '@sentry/vercel-edge': 8.45.1 + '@sentry-internal/browser-utils': 8.52.0 + '@sentry/core': 8.52.0 + '@sentry/node': 8.52.0 + '@sentry/opentelemetry': 8.52.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0) + '@sentry/react': 8.52.0(react@19.0.0) + '@sentry/vercel-edge': 8.52.0 '@sentry/webpack-plugin': 2.22.7(encoding@0.1.13)(webpack@5.97.1) chalk: 3.0.0 next: 15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -19115,11 +19030,11 @@ snapshots: - supports-color - webpack - '@sentry/node@8.45.1': + '@sentry/node@8.52.0': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 1.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-amqplib': 0.45.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-connect': 0.42.0(@opentelemetry/api@1.9.0) @@ -19145,36 +19060,36 @@ snapshots: '@opentelemetry/instrumentation-redis-4': 0.45.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-tedious': 0.17.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-undici': 0.9.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 - '@prisma/instrumentation': 5.19.1 - '@sentry/core': 8.45.1 - '@sentry/opentelemetry': 8.45.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0) + '@prisma/instrumentation': 5.22.0 + '@sentry/core': 8.52.0 + '@sentry/opentelemetry': 8.52.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0) import-in-the-middle: 1.12.0 transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.45.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0)': + '@sentry/opentelemetry@8.52.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 - '@sentry/core': 8.45.1 + '@sentry/core': 8.52.0 - '@sentry/react@8.45.1(react@19.0.0)': + '@sentry/react@8.52.0(react@19.0.0)': dependencies: - '@sentry/browser': 8.45.1 - '@sentry/core': 8.45.1 + '@sentry/browser': 8.52.0 + '@sentry/core': 8.52.0 hoist-non-react-statics: 3.3.2 react: 19.0.0 - '@sentry/vercel-edge@8.45.1': + '@sentry/vercel-edge@8.52.0': dependencies: '@opentelemetry/api': 1.9.0 - '@sentry/core': 8.45.1 + '@sentry/core': 8.52.0 '@sentry/webpack-plugin@2.22.7(encoding@0.1.13)(webpack@5.97.1)': dependencies: @@ -19245,250 +19160,250 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@smithy/abort-controller@3.1.9': + '@smithy/abort-controller@4.0.1': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/chunked-blob-reader-native@3.0.1': + '@smithy/chunked-blob-reader-native@4.0.0': dependencies: - '@smithy/util-base64': 3.0.0 + '@smithy/util-base64': 4.0.0 tslib: 2.8.1 - '@smithy/chunked-blob-reader@4.0.0': + '@smithy/chunked-blob-reader@5.0.0': dependencies: tslib: 2.8.1 - '@smithy/config-resolver@3.0.13': + '@smithy/config-resolver@4.0.1': dependencies: - '@smithy/node-config-provider': 3.1.12 - '@smithy/types': 3.7.2 - '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.11 + '@smithy/node-config-provider': 4.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.1 tslib: 2.8.1 - '@smithy/core@2.5.7': + '@smithy/core@3.1.2': dependencies: - '@smithy/middleware-serde': 3.0.11 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-stream': 3.3.4 - '@smithy/util-utf8': 3.0.0 + '@smithy/middleware-serde': 4.0.2 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-stream': 4.0.2 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@smithy/credential-provider-imds@3.2.8': + '@smithy/credential-provider-imds@4.0.1': dependencies: - '@smithy/node-config-provider': 3.1.12 - '@smithy/property-provider': 3.1.11 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 + '@smithy/node-config-provider': 4.0.1 + '@smithy/property-provider': 4.0.1 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 tslib: 2.8.1 - '@smithy/eventstream-codec@3.1.10': + '@smithy/eventstream-codec@4.0.1': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 3.7.2 - '@smithy/util-hex-encoding': 3.0.0 + '@smithy/types': 4.1.0 + '@smithy/util-hex-encoding': 4.0.0 tslib: 2.8.1 - '@smithy/eventstream-serde-browser@3.0.14': + '@smithy/eventstream-serde-browser@4.0.1': dependencies: - '@smithy/eventstream-serde-universal': 3.0.13 - '@smithy/types': 3.7.2 + '@smithy/eventstream-serde-universal': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/eventstream-serde-config-resolver@3.0.11': + '@smithy/eventstream-serde-config-resolver@4.0.1': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/eventstream-serde-node@3.0.13': + '@smithy/eventstream-serde-node@4.0.1': dependencies: - '@smithy/eventstream-serde-universal': 3.0.13 - '@smithy/types': 3.7.2 + '@smithy/eventstream-serde-universal': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/eventstream-serde-universal@3.0.13': + '@smithy/eventstream-serde-universal@4.0.1': dependencies: - '@smithy/eventstream-codec': 3.1.10 - '@smithy/types': 3.7.2 + '@smithy/eventstream-codec': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/fetch-http-handler@4.1.3': + '@smithy/fetch-http-handler@5.0.1': dependencies: - '@smithy/protocol-http': 4.1.8 - '@smithy/querystring-builder': 3.0.11 - '@smithy/types': 3.7.2 - '@smithy/util-base64': 3.0.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/querystring-builder': 4.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-base64': 4.0.0 tslib: 2.8.1 - '@smithy/hash-blob-browser@3.1.10': + '@smithy/hash-blob-browser@4.0.1': dependencies: - '@smithy/chunked-blob-reader': 4.0.0 - '@smithy/chunked-blob-reader-native': 3.0.1 - '@smithy/types': 3.7.2 + '@smithy/chunked-blob-reader': 5.0.0 + '@smithy/chunked-blob-reader-native': 4.0.0 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/hash-node@3.0.11': + '@smithy/hash-node@4.0.1': dependencies: - '@smithy/types': 3.7.2 - '@smithy/util-buffer-from': 3.0.0 - '@smithy/util-utf8': 3.0.0 + '@smithy/types': 4.1.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@smithy/hash-stream-node@3.1.10': + '@smithy/hash-stream-node@4.0.1': dependencies: - '@smithy/types': 3.7.2 - '@smithy/util-utf8': 3.0.0 + '@smithy/types': 4.1.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@smithy/invalid-dependency@3.0.11': + '@smithy/invalid-dependency@4.0.1': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - '@smithy/is-array-buffer@3.0.0': + '@smithy/is-array-buffer@4.0.0': dependencies: tslib: 2.8.1 - '@smithy/md5-js@3.0.11': + '@smithy/md5-js@4.0.1': dependencies: - '@smithy/types': 3.7.2 - '@smithy/util-utf8': 3.0.0 + '@smithy/types': 4.1.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@smithy/middleware-content-length@3.0.13': + '@smithy/middleware-content-length@4.0.1': dependencies: - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/middleware-endpoint@3.2.8': + '@smithy/middleware-endpoint@4.0.3': dependencies: - '@smithy/core': 2.5.7 - '@smithy/middleware-serde': 3.0.11 - '@smithy/node-config-provider': 3.1.12 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 - '@smithy/util-middleware': 3.0.11 + '@smithy/core': 3.1.2 + '@smithy/middleware-serde': 4.0.2 + '@smithy/node-config-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 + '@smithy/util-middleware': 4.0.1 tslib: 2.8.1 - '@smithy/middleware-retry@3.0.34': + '@smithy/middleware-retry@4.0.4': dependencies: - '@smithy/node-config-provider': 3.1.12 - '@smithy/protocol-http': 4.1.8 - '@smithy/service-error-classification': 3.0.11 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-retry': 3.0.11 + '@smithy/node-config-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/service-error-classification': 4.0.1 + '@smithy/smithy-client': 4.1.3 + '@smithy/types': 4.1.0 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-retry': 4.0.1 tslib: 2.8.1 uuid: 9.0.1 - '@smithy/middleware-serde@3.0.11': + '@smithy/middleware-serde@4.0.2': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/middleware-stack@3.0.11': + '@smithy/middleware-stack@4.0.1': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/node-config-provider@3.1.12': + '@smithy/node-config-provider@4.0.1': dependencies: - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/node-http-handler@3.3.3': + '@smithy/node-http-handler@4.0.2': dependencies: - '@smithy/abort-controller': 3.1.9 - '@smithy/protocol-http': 4.1.8 - '@smithy/querystring-builder': 3.0.11 - '@smithy/types': 3.7.2 + '@smithy/abort-controller': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/querystring-builder': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/property-provider@3.1.11': + '@smithy/property-provider@4.0.1': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/protocol-http@4.1.8': + '@smithy/protocol-http@5.0.1': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/querystring-builder@3.0.11': + '@smithy/querystring-builder@4.0.1': dependencies: - '@smithy/types': 3.7.2 - '@smithy/util-uri-escape': 3.0.0 + '@smithy/types': 4.1.0 + '@smithy/util-uri-escape': 4.0.0 tslib: 2.8.1 - '@smithy/querystring-parser@3.0.11': + '@smithy/querystring-parser@4.0.1': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/service-error-classification@3.0.11': + '@smithy/service-error-classification@4.0.1': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 - '@smithy/shared-ini-file-loader@3.1.12': + '@smithy/shared-ini-file-loader@4.0.1': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/signature-v4@4.2.4': + '@smithy/signature-v4@5.0.1': dependencies: - '@smithy/is-array-buffer': 3.0.0 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-uri-escape': 3.0.0 - '@smithy/util-utf8': 3.0.0 + '@smithy/is-array-buffer': 4.0.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-uri-escape': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@smithy/smithy-client@3.7.0': + '@smithy/smithy-client@4.1.3': dependencies: - '@smithy/core': 2.5.7 - '@smithy/middleware-endpoint': 3.2.8 - '@smithy/middleware-stack': 3.0.11 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - '@smithy/util-stream': 3.3.4 + '@smithy/core': 3.1.2 + '@smithy/middleware-endpoint': 4.0.3 + '@smithy/middleware-stack': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-stream': 4.0.2 tslib: 2.8.1 - '@smithy/types@3.7.2': + '@smithy/types@4.1.0': dependencies: tslib: 2.8.1 - '@smithy/url-parser@3.0.11': + '@smithy/url-parser@4.0.1': dependencies: - '@smithy/querystring-parser': 3.0.11 - '@smithy/types': 3.7.2 + '@smithy/querystring-parser': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/util-base64@3.0.0': + '@smithy/util-base64@4.0.0': dependencies: - '@smithy/util-buffer-from': 3.0.0 - '@smithy/util-utf8': 3.0.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@smithy/util-body-length-browser@3.0.0': + '@smithy/util-body-length-browser@4.0.0': dependencies: tslib: 2.8.1 - '@smithy/util-body-length-node@3.0.0': + '@smithy/util-body-length-node@4.0.0': dependencies: tslib: 2.8.1 @@ -19497,66 +19412,66 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 - '@smithy/util-buffer-from@3.0.0': + '@smithy/util-buffer-from@4.0.0': dependencies: - '@smithy/is-array-buffer': 3.0.0 + '@smithy/is-array-buffer': 4.0.0 tslib: 2.8.1 - '@smithy/util-config-provider@3.0.0': + '@smithy/util-config-provider@4.0.0': dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@3.0.34': + '@smithy/util-defaults-mode-browser@4.0.4': dependencies: - '@smithy/property-provider': 3.1.11 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 + '@smithy/property-provider': 4.0.1 + '@smithy/smithy-client': 4.1.3 + '@smithy/types': 4.1.0 bowser: 2.11.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@3.0.34': + '@smithy/util-defaults-mode-node@4.0.4': dependencies: - '@smithy/config-resolver': 3.0.13 - '@smithy/credential-provider-imds': 3.2.8 - '@smithy/node-config-provider': 3.1.12 - '@smithy/property-provider': 3.1.11 - '@smithy/smithy-client': 3.7.0 - '@smithy/types': 3.7.2 + '@smithy/config-resolver': 4.0.1 + '@smithy/credential-provider-imds': 4.0.1 + '@smithy/node-config-provider': 4.0.1 + '@smithy/property-provider': 4.0.1 + '@smithy/smithy-client': 4.1.3 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/util-endpoints@2.1.7': + '@smithy/util-endpoints@3.0.1': dependencies: - '@smithy/node-config-provider': 3.1.12 - '@smithy/types': 3.7.2 + '@smithy/node-config-provider': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/util-hex-encoding@3.0.0': + '@smithy/util-hex-encoding@4.0.0': dependencies: tslib: 2.8.1 - '@smithy/util-middleware@3.0.11': + '@smithy/util-middleware@4.0.1': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/util-retry@3.0.11': + '@smithy/util-retry@4.0.1': dependencies: - '@smithy/service-error-classification': 3.0.11 - '@smithy/types': 3.7.2 + '@smithy/service-error-classification': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 - '@smithy/util-stream@3.3.4': + '@smithy/util-stream@4.0.2': dependencies: - '@smithy/fetch-http-handler': 4.1.3 - '@smithy/node-http-handler': 3.3.3 - '@smithy/types': 3.7.2 - '@smithy/util-base64': 3.0.0 - '@smithy/util-buffer-from': 3.0.0 - '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-utf8': 3.0.0 + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/node-http-handler': 4.0.2 + '@smithy/types': 4.1.0 + '@smithy/util-base64': 4.0.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@smithy/util-uri-escape@3.0.0': + '@smithy/util-uri-escape@4.0.0': dependencies: tslib: 2.8.1 @@ -19565,15 +19480,15 @@ snapshots: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 - '@smithy/util-utf8@3.0.0': + '@smithy/util-utf8@4.0.0': dependencies: - '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-buffer-from': 4.0.0 tslib: 2.8.1 - '@smithy/util-waiter@3.2.0': + '@smithy/util-waiter@4.0.2': dependencies: - '@smithy/abort-controller': 3.1.9 - '@smithy/types': 3.7.2 + '@smithy/abort-controller': 4.0.1 + '@smithy/types': 4.1.0 tslib: 2.8.1 '@storybook/addon-a11y@8.4.7(storybook@8.4.7(prettier@3.4.2))': @@ -19688,7 +19603,7 @@ snapshots: '@storybook/blocks@8.4.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.4.7(prettier@3.4.2))': dependencies: '@storybook/csf': 0.1.13 - '@storybook/icons': 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/icons': 1.3.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) storybook: 8.4.7(prettier@3.4.2) ts-dedent: 2.2.0 optionalDependencies: @@ -19698,7 +19613,7 @@ snapshots: '@storybook/blocks@8.4.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.4.7(prettier@3.4.2))': dependencies: '@storybook/csf': 0.1.13 - '@storybook/icons': 1.3.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@storybook/icons': 1.3.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) storybook: 8.4.7(prettier@3.4.2) ts-dedent: 2.2.0 optionalDependencies: @@ -19748,12 +19663,12 @@ snapshots: '@storybook/global@5.0.0': {} - '@storybook/icons@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@storybook/icons@1.3.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@storybook/icons@1.3.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@storybook/icons@1.3.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) @@ -19784,16 +19699,16 @@ snapshots: react-dom: 19.0.0(react@19.0.0) storybook: 8.4.7(prettier@3.4.2) - '@storybook/react-vite@8.4.7(@storybook/test@8.4.7(storybook@8.4.7(prettier@3.4.2)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.31.0)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))': + '@storybook/react-vite@8.4.7(@storybook/test@8.4.7(storybook@8.4.7(prettier@3.4.2)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.32.1)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.4.2(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) - '@rollup/pluginutils': 5.1.4(rollup@4.31.0) + '@rollup/pluginutils': 5.1.4(rollup@4.32.1) '@storybook/builder-vite': 8.4.7(storybook@8.4.7(prettier@3.4.2))(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) '@storybook/react': 8.4.7(@storybook/test@8.4.7(storybook@8.4.7(prettier@3.4.2)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.4.7(prettier@3.4.2))(typescript@5.7.2) find-up: 5.0.0 magic-string: 0.30.17 react: 19.0.0 - react-docgen: 7.1.0 + react-docgen: 7.1.1 react-dom: 19.0.0(react@19.0.0) resolve: 1.22.10 storybook: 8.4.7(prettier@3.4.2) @@ -19844,18 +19759,17 @@ snapshots: dependencies: tslib: 2.8.1 - '@t3-oss/env-core@0.11.1(typescript@5.7.2)(zod@3.24.1)': - dependencies: - zod: 3.24.1 + '@t3-oss/env-core@0.12.0(typescript@5.7.2)(zod@3.24.1)': optionalDependencies: typescript: 5.7.2 + zod: 3.24.1 - '@t3-oss/env-nextjs@0.11.1(typescript@5.7.2)(zod@3.24.1)': + '@t3-oss/env-nextjs@0.12.0(typescript@5.7.2)(zod@3.24.1)': dependencies: - '@t3-oss/env-core': 0.11.1(typescript@5.7.2)(zod@3.24.1) - zod: 3.24.1 + '@t3-oss/env-core': 0.12.0(typescript@5.7.2)(zod@3.24.1) optionalDependencies: typescript: 5.7.2 + zod: 3.24.1 '@tailwindcss/forms@0.5.9(tailwindcss@3.4.16(ts-node@10.9.2(@types/node@22.10.2)(typescript@5.7.2)))': dependencies: @@ -19876,20 +19790,20 @@ snapshots: react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - '@tanstack/react-virtual@3.11.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@tanstack/react-virtual@3.11.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: - '@tanstack/virtual-core': 3.11.2 + '@tanstack/virtual-core': 3.11.3 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) '@tanstack/table-core@8.20.5': {} - '@tanstack/virtual-core@3.11.2': {} + '@tanstack/virtual-core@3.11.3': {} '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.26.2 - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 '@types/aria-query': 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -19913,10 +19827,10 @@ snapshots: '@trivago/prettier-plugin-sort-imports@5.2.0(prettier@3.4.2)': dependencies: - '@babel/generator': 7.26.3 - '@babel/parser': 7.26.3 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.7 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 javascript-natural-sort: 0.7.1 lodash: 4.17.21 prettier: 3.4.2 @@ -19941,24 +19855,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.26.3 + '@babel/types': 7.26.7 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.26.3 + '@babel/types': 7.26.7 '@types/bcryptjs@2.4.6': {} @@ -19972,11 +19886,11 @@ snapshots: '@types/debug@4.1.12': dependencies: - '@types/ms': 0.7.34 + '@types/ms': 2.1.0 '@types/debug@4.1.8': dependencies: - '@types/ms': 0.7.34 + '@types/ms': 2.1.0 '@types/diff-match-patch@1.0.36': {} @@ -20030,8 +19944,9 @@ snapshots: '@types/json5@0.0.29': {} - '@types/jsonwebtoken@9.0.7': + '@types/jsonwebtoken@9.0.8': dependencies: + '@types/ms': 2.1.0 '@types/node': 22.10.2 '@types/linkify-it@5.0.0': {} @@ -20053,7 +19968,7 @@ snapshots: '@types/mime-types@2.1.4': {} - '@types/ms@0.7.34': {} + '@types/ms@2.1.0': {} '@types/mysql@2.15.26': dependencies: @@ -20065,7 +19980,7 @@ snapshots: '@types/node@12.20.55': {} - '@types/node@18.19.70': + '@types/node@18.19.74': dependencies: undici-types: 5.26.5 @@ -20101,9 +20016,9 @@ snapshots: dependencies: '@types/node': 22.10.2 - '@types/react-dom@19.0.2(@types/react@18.3.11)': + '@types/react-dom@19.0.2(@types/react@18.3.18)': dependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 optional: true '@types/react-dom@19.0.2(@types/react@19.0.1)': @@ -20119,6 +20034,11 @@ snapshots: '@types/prop-types': 15.7.14 csstype: 3.1.3 + '@types/react@18.3.18': + dependencies: + '@types/prop-types': 15.7.14 + csstype: 3.1.3 + '@types/react@19.0.1': dependencies: csstype: 3.1.3 @@ -20150,7 +20070,7 @@ snapshots: '@types/uuid@9.0.8': {} - '@types/ws@8.5.13': + '@types/ws@8.5.14': dependencies: '@types/node': 22.10.2 @@ -20239,10 +20159,10 @@ snapshots: '@typescript-eslint/types': 8.18.0 '@typescript-eslint/visitor-keys': 8.18.0 - '@typescript-eslint/scope-manager@8.19.1': + '@typescript-eslint/scope-manager@8.22.0': dependencies: - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/types': 8.22.0 + '@typescript-eslint/visitor-keys': 8.22.0 '@typescript-eslint/type-utils@7.18.0(eslint@8.57.0)(typescript@5.7.2)': dependencies: @@ -20273,7 +20193,7 @@ snapshots: '@typescript-eslint/types@8.18.0': {} - '@typescript-eslint/types@8.19.1': {} + '@typescript-eslint/types@8.22.0': {} '@typescript-eslint/typescript-estree@5.62.0(typescript@5.7.2)': dependencies: @@ -20318,10 +20238,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.19.1(typescript@5.7.2)': + '@typescript-eslint/typescript-estree@8.22.0(typescript@5.7.2)': dependencies: - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/visitor-keys': 8.19.1 + '@typescript-eslint/types': 8.22.0 + '@typescript-eslint/visitor-keys': 8.22.0 debug: 4.4.0 fast-glob: 3.3.2 is-glob: 4.0.3 @@ -20369,12 +20289,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.19.1(eslint@8.57.0)(typescript@5.7.2)': + '@typescript-eslint/utils@8.22.0(eslint@8.57.0)(typescript@5.7.2)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.0) - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.22.0 + '@typescript-eslint/types': 8.22.0 + '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.7.2) eslint: 8.57.0 typescript: 5.7.2 transitivePeerDependencies: @@ -20395,12 +20315,12 @@ snapshots: '@typescript-eslint/types': 8.18.0 eslint-visitor-keys: 4.2.0 - '@typescript-eslint/visitor-keys@8.19.1': + '@typescript-eslint/visitor-keys@8.22.0': dependencies: - '@typescript-eslint/types': 8.19.1 + '@typescript-eslint/types': 8.22.0 eslint-visitor-keys: 4.2.0 - '@ungap/structured-clone@1.2.1': {} + '@ungap/structured-clone@1.3.0': {} '@urql/core@5.1.0': dependencies: @@ -20414,9 +20334,9 @@ snapshots: '@urql/core': 5.1.0 wonka: 6.3.4 - '@vercel/functions@1.5.2(@aws-sdk/credential-provider-web-identity@3.709.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0)))': + '@vercel/functions@1.5.2(@aws-sdk/credential-provider-web-identity@3.734.0)': optionalDependencies: - '@aws-sdk/credential-provider-web-identity': 3.709.0(@aws-sdk/client-sts@3.712.0(aws-crt@1.24.0)) + '@aws-sdk/credential-provider-web-identity': 3.734.0(aws-crt@1.25.3) '@vercel/og@0.6.4': dependencies: @@ -20424,25 +20344,25 @@ snapshots: satori: 0.12.0 yoga-wasm-web: 0.3.3 - '@vercel/otel@1.10.0(@opentelemetry/api-logs@0.56.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.0(@opentelemetry/api@1.9.0))': + '@vercel/otel@1.10.0(@opentelemetry/api-logs@0.56.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.56.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.56.0 '@opentelemetry/instrumentation': 0.56.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-logs': 0.56.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 1.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) '@vercel/speed-insights@1.1.0(next@15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)': optionalDependencies: next: 15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 - '@vercel/style-guide@6.0.0(@next/eslint-plugin-next@15.1.0)(eslint@8.57.0)(prettier@3.4.2)(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0))': + '@vercel/style-guide@6.0.0(@next/eslint-plugin-next@15.1.0)(eslint@8.57.0)(prettier@3.4.2)(typescript@5.7.2)(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))': dependencies: '@babel/core': 7.26.0 - '@babel/eslint-parser': 7.25.9(@babel/core@7.26.0)(eslint@8.57.0) + '@babel/eslint-parser': 7.26.5(@babel/core@7.26.0)(eslint@8.57.0) '@rushstack/eslint-patch': 1.10.5 '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2) '@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.7.2) @@ -20459,8 +20379,8 @@ snapshots: eslint-plugin-testing-library: 6.5.0(eslint@8.57.0)(typescript@5.7.2) eslint-plugin-tsdoc: 0.2.17 eslint-plugin-unicorn: 51.0.1(eslint@8.57.0) - eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)) - prettier-plugin-packagejson: 2.5.6(prettier@3.4.2) + eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2)(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + prettier-plugin-packagejson: 2.5.8(prettier@3.4.2) optionalDependencies: '@next/eslint-plugin-next': 15.1.0 eslint: 8.57.0 @@ -20484,6 +20404,42 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/coverage-v8@2.1.8(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 0.2.3 + debug: 4.4.0 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magic-string: 0.30.17 + magicast: 0.3.5 + std-env: 3.8.0 + test-exclude: 7.0.1 + tinyrainbow: 1.2.0 + vitest: 2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0) + transitivePeerDependencies: + - supports-color + + '@vitest/coverage-v8@3.0.4(vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 1.0.2 + debug: 4.4.0 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magic-string: 0.30.17 + magicast: 0.3.5 + std-env: 3.8.0 + test-exclude: 7.0.1 + tinyrainbow: 2.0.0 + vitest: 3.0.4(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - supports-color + '@vitest/expect@2.0.5': dependencies: '@vitest/spy': 2.0.5 @@ -20498,6 +20454,20 @@ snapshots: chai: 5.1.2 tinyrainbow: 1.2.0 + '@vitest/expect@3.0.4': + dependencies: + '@vitest/spy': 3.0.4 + '@vitest/utils': 3.0.4 + chai: 5.1.2 + tinyrainbow: 2.0.0 + + '@vitest/expect@3.0.5': + dependencies: + '@vitest/spy': 3.0.5 + '@vitest/utils': 3.0.5 + chai: 5.1.2 + tinyrainbow: 2.0.0 + '@vitest/mocker@2.1.8(vite@5.4.14(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0))': dependencies: '@vitest/spy': 2.1.8 @@ -20506,6 +20476,22 @@ snapshots: optionalDependencies: vite: 5.4.14(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0) + '@vitest/mocker@3.0.4(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))': + dependencies: + '@vitest/spy': 3.0.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + + '@vitest/mocker@3.0.5(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))': + dependencies: + '@vitest/spy': 3.0.5 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + '@vitest/pretty-format@2.0.5': dependencies: tinyrainbow: 1.2.0 @@ -20514,17 +20500,47 @@ snapshots: dependencies: tinyrainbow: 1.2.0 + '@vitest/pretty-format@3.0.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/pretty-format@3.0.5': + dependencies: + tinyrainbow: 2.0.0 + '@vitest/runner@2.1.8': dependencies: '@vitest/utils': 2.1.8 pathe: 1.1.2 + '@vitest/runner@3.0.4': + dependencies: + '@vitest/utils': 3.0.4 + pathe: 2.0.2 + + '@vitest/runner@3.0.5': + dependencies: + '@vitest/utils': 3.0.5 + pathe: 2.0.2 + '@vitest/snapshot@2.1.8': dependencies: '@vitest/pretty-format': 2.1.8 magic-string: 0.30.17 pathe: 1.1.2 + '@vitest/snapshot@3.0.4': + dependencies: + '@vitest/pretty-format': 3.0.4 + magic-string: 0.30.17 + pathe: 2.0.2 + + '@vitest/snapshot@3.0.5': + dependencies: + '@vitest/pretty-format': 3.0.5 + magic-string: 0.30.17 + pathe: 2.0.2 + '@vitest/spy@2.0.5': dependencies: tinyspy: 3.0.2 @@ -20533,19 +20549,39 @@ snapshots: dependencies: tinyspy: 3.0.2 + '@vitest/spy@3.0.4': + dependencies: + tinyspy: 3.0.2 + + '@vitest/spy@3.0.5': + dependencies: + tinyspy: 3.0.2 + '@vitest/utils@2.0.5': dependencies: '@vitest/pretty-format': 2.0.5 estree-walker: 3.0.3 - loupe: 3.1.2 + loupe: 3.1.3 tinyrainbow: 1.2.0 '@vitest/utils@2.1.8': dependencies: '@vitest/pretty-format': 2.1.8 - loupe: 3.1.2 + loupe: 3.1.3 tinyrainbow: 1.2.0 + '@vitest/utils@3.0.4': + dependencies: + '@vitest/pretty-format': 3.0.4 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + + '@vitest/utils@3.0.5': + dependencies: + '@vitest/pretty-format': 3.0.5 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + '@volar/language-core@2.4.11': dependencies: '@volar/source-map': 2.4.11 @@ -20560,7 +20596,7 @@ snapshots: '@vue/compiler-core@3.5.13': dependencies: - '@babel/parser': 7.26.3 + '@babel/parser': 7.26.7 '@vue/shared': 3.5.13 entities: 4.5.0 estree-walker: 2.0.2 @@ -20719,15 +20755,14 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ai@4.0.18(react@19.0.0)(zod@3.24.1): + ai@4.1.17(react@19.0.0)(zod@3.24.1): dependencies: - '@ai-sdk/provider': 1.0.2 - '@ai-sdk/provider-utils': 2.0.4(zod@3.24.1) - '@ai-sdk/react': 1.0.6(react@19.0.0)(zod@3.24.1) - '@ai-sdk/ui-utils': 1.0.5(zod@3.24.1) + '@ai-sdk/provider': 1.0.7 + '@ai-sdk/provider-utils': 2.1.6(zod@3.24.1) + '@ai-sdk/react': 1.1.8(react@19.0.0)(zod@3.24.1) + '@ai-sdk/ui-utils': 1.1.8(zod@3.24.1) '@opentelemetry/api': 1.9.0 jsondiffpatch: 0.6.0 - zod-to-json-schema: 3.24.1(zod@3.24.1) optionalDependencies: react: 19.0.0 zod: 3.24.1 @@ -20777,25 +20812,25 @@ snapshots: ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.0.5 + fast-uri: 3.0.6 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch@5.19.0: + algoliasearch@5.20.0: dependencies: - '@algolia/client-abtesting': 5.19.0 - '@algolia/client-analytics': 5.19.0 - '@algolia/client-common': 5.19.0 - '@algolia/client-insights': 5.19.0 - '@algolia/client-personalization': 5.19.0 - '@algolia/client-query-suggestions': 5.19.0 - '@algolia/client-search': 5.19.0 - '@algolia/ingestion': 1.19.0 - '@algolia/monitoring': 1.19.0 - '@algolia/recommend': 5.19.0 - '@algolia/requester-browser-xhr': 5.19.0 - '@algolia/requester-fetch': 5.19.0 - '@algolia/requester-node-http': 5.19.0 + '@algolia/client-abtesting': 5.20.0 + '@algolia/client-analytics': 5.20.0 + '@algolia/client-common': 5.20.0 + '@algolia/client-insights': 5.20.0 + '@algolia/client-personalization': 5.20.0 + '@algolia/client-query-suggestions': 5.20.0 + '@algolia/client-search': 5.20.0 + '@algolia/ingestion': 1.20.0 + '@algolia/monitoring': 1.20.0 + '@algolia/recommend': 5.20.0 + '@algolia/requester-browser-xhr': 5.20.0 + '@algolia/requester-fetch': 5.20.0 + '@algolia/requester-node-http': 5.20.0 anser@1.4.10: {} @@ -20918,7 +20953,7 @@ snapshots: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.9 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 get-intrinsic: 1.2.7 is-string: 1.1.1 @@ -20939,7 +20974,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 es-shim-unscopables: 1.0.2 array.prototype.findlastindex@1.2.5: @@ -20948,7 +20983,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 es-shim-unscopables: 1.0.2 array.prototype.flat@1.3.3: @@ -21017,6 +21052,8 @@ snapshots: astring@1.9.0: {} + async-function@1.0.0: {} + async-limiter@1.0.1: {} async@3.2.6: {} @@ -21028,28 +21065,18 @@ snapshots: autoprefixer@10.4.20(postcss@8.4.49): dependencies: browserslist: 4.24.4 - caniuse-lite: 1.0.30001692 + caniuse-lite: 1.0.30001696 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 postcss: 8.4.49 postcss-value-parser: 4.2.0 - autoprefixer@10.4.20(postcss@8.5.1): - dependencies: - browserslist: 4.24.4 - caniuse-lite: 1.0.30001692 - fraction.js: 4.3.7 - normalize-range: 0.1.2 - picocolors: 1.1.1 - postcss: 8.5.1 - postcss-value-parser: 4.2.0 - available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - aws-crt@1.24.0: + aws-crt@1.25.3: dependencies: '@aws-sdk/util-utf8-browser': 3.259.0 '@httptoolkit/websocket-stream': 6.0.1 @@ -21095,7 +21122,7 @@ snapshots: babel-plugin-istanbul@6.1.1: dependencies: - '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-plugin-utils': 7.26.5 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.1 @@ -21106,13 +21133,13 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: '@babel/template': 7.25.9 - '@babel/types': 7.26.5 + '@babel/types': 7.26.7 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.6 babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.0): dependencies: - '@babel/compat-data': 7.26.3 + '@babel/compat-data': 7.26.5 '@babel/core': 7.26.0 '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) semver: 6.3.1 @@ -21173,7 +21200,7 @@ snapshots: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.0) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.0) - babel-preset-expo@12.0.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0)): + babel-preset-expo@12.0.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0)): dependencies: '@babel/plugin-proposal-decorators': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.26.0) @@ -21181,7 +21208,7 @@ snapshots: '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) '@babel/preset-react': 7.26.3(@babel/core@7.26.0) '@babel/preset-typescript': 7.26.0(@babel/core@7.26.0) - '@react-native/babel-preset': 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0)) + '@react-native/babel-preset': 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0)) babel-plugin-react-native-web: 0.19.13 react-refresh: 0.14.2 transitivePeerDependencies: @@ -21241,7 +21268,7 @@ snapshots: dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 - chalk: 5.0.1 + chalk: 5.4.1 cli-boxes: 3.0.0 string-width: 5.1.2 type-fest: 2.19.0 @@ -21332,8 +21359,8 @@ snapshots: browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001692 - electron-to-chromium: 1.5.80 + caniuse-lite: 1.0.30001696 + electron-to-chromium: 1.5.88 node-releases: 2.0.19 update-browserslist-db: 1.1.2(browserslist@4.24.4) @@ -21443,7 +21470,7 @@ snapshots: camelize@1.0.1: {} - caniuse-lite@1.0.30001692: {} + caniuse-lite@1.0.30001696: {} ccount@2.0.1: {} @@ -21457,7 +21484,7 @@ snapshots: assertion-error: 2.0.1 check-error: 2.1.1 deep-eql: 5.0.2 - loupe: 3.1.2 + loupe: 3.1.3 pathval: 2.0.0 chalk-template@0.4.0: @@ -21555,11 +21582,11 @@ snapshots: chokidar@4.0.3: dependencies: - readdirp: 4.0.2 + readdirp: 4.1.1 chownr@2.0.0: {} - chromatic@11.22.2: {} + chromatic@11.25.1: {} chrome-launcher@0.15.2: dependencies: @@ -21668,11 +21695,11 @@ snapshots: cluster-key-slot@1.1.2: {} - cmdk@1.0.4(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + cmdk@1.0.4(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - '@radix-ui/react-dialog': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@18.3.11)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.11))(@types/react@18.3.11)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-dialog': 1.1.3(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.18)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.2(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) use-sync-external-store: 1.4.0(react@19.0.0) @@ -21808,7 +21835,7 @@ snapshots: transitivePeerDependencies: - supports-color - consola@3.3.3: {} + consola@3.4.0: {} console-browserify@1.2.0: {} @@ -21960,7 +21987,7 @@ snapshots: cssstyle@4.2.1: dependencies: - '@asamuzakjp/css-color': 2.8.2 + '@asamuzakjp/css-color': 2.8.3 rrweb-cssom: 0.8.0 csstype@3.1.3: {} @@ -22018,7 +22045,7 @@ snapshots: decamelize@1.2.0: {} - decimal.js@10.4.3: {} + decimal.js@10.5.0: {} decko@1.2.0: {} @@ -22208,7 +22235,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.80: {} + electron-to-chromium@1.5.88: {} elliptic@6.6.1: dependencies: @@ -22324,7 +22351,7 @@ snapshots: data-view-byte-offset: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 es-set-tostringtag: 2.1.0 es-to-primitive: 1.3.0 function.prototype.name: 1.1.8 @@ -22393,10 +22420,6 @@ snapshots: es-module-lexer@1.6.0: {} - es-object-atoms@1.0.0: - dependencies: - es-errors: 1.3.0 - es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -22570,7 +22593,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.0) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0) eslint-plugin-react: 7.37.2(eslint@8.57.0) eslint-plugin-react-hooks: 5.1.0(eslint@8.57.0) @@ -22592,7 +22615,7 @@ snapshots: eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0): dependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0) eslint-import-resolver-node@0.3.9: dependencies: @@ -22609,12 +22632,12 @@ snapshots: enhanced-resolve: 5.18.0 eslint: 8.57.0 fast-glob: 3.3.2 - get-tsconfig: 4.8.1 + get-tsconfig: 4.10.0 is-bun-module: 1.3.0 is-glob: 4.0.3 stable-hash: 0.0.4 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -22648,7 +22671,7 @@ snapshots: eslint-plugin-i18n-json@4.0.0(eslint@8.57.0): dependencies: - '@formatjs/icu-messageformat-parser': 2.9.8 + '@formatjs/icu-messageformat-parser': 2.11.0 chalk: 2.4.2 eslint: 8.57.0 indent-string: 3.2.0 @@ -22691,7 +22714,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -22793,7 +22816,7 @@ snapshots: eslint-plugin-storybook@0.11.1(eslint@8.57.0)(typescript@5.7.2): dependencies: '@storybook/csf': 0.1.13 - '@typescript-eslint/utils': 8.19.1(eslint@8.57.0)(typescript@5.7.2) + '@typescript-eslint/utils': 8.22.0(eslint@8.57.0)(typescript@5.7.2) eslint: 8.57.0 ts-dedent: 2.2.0 transitivePeerDependencies: @@ -22840,13 +22863,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2)(vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)): + eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2)(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): dependencies: '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.7.2) eslint: 8.57.0 optionalDependencies: '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2) - vitest: 2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0) + vitest: 3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) transitivePeerDependencies: - supports-color - typescript @@ -22876,7 +22899,7 @@ snapshots: '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.1 + '@ungap/structured-clone': 1.3.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 @@ -23025,45 +23048,45 @@ snapshots: expect-type@1.1.0: {} - expo-asset@11.0.1(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): + expo-asset@11.0.2(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): dependencies: - '@expo/image-utils': 0.6.3 - expo: 52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) - expo-constants: 17.0.3(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1)) + '@expo/image-utils': 0.6.4 + expo: 52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + expo-constants: 17.0.5(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1)) invariant: 2.2.4 md5-file: 3.2.3 react: 18.3.1 - react-native: 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1) + react-native: 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1) transitivePeerDependencies: - supports-color - expo-constants@17.0.3(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1)): + expo-constants@17.0.5(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1)): dependencies: - '@expo/config': 10.0.7 - '@expo/env': 0.4.0 - expo: 52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) - react-native: 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1) + '@expo/config': 10.0.8 + '@expo/env': 0.4.1 + expo: 52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + react-native: 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1) transitivePeerDependencies: - supports-color - expo-file-system@18.0.6(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1)): + expo-file-system@18.0.7(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1)): dependencies: - expo: 52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) - react-native: 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1) + expo: 52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + react-native: 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1) web-streams-polyfill: 3.3.3 - expo-font@13.0.2(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react@18.3.1): + expo-font@13.0.3(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: - expo: 52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + expo: 52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) fontfaceobserver: 2.3.0 react: 18.3.1 - expo-keep-awake@14.0.1(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react@18.3.1): + expo-keep-awake@14.0.2(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: - expo: 52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + expo: 52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) react: 18.3.1 - expo-modules-autolinking@2.0.4: + expo-modules-autolinking@2.0.7: dependencies: '@expo/spawn-async': 1.7.2 chalk: 4.1.2 @@ -23074,39 +23097,39 @@ snapshots: require-from-string: 2.0.2 resolve-from: 5.0.0 - expo-modules-core@2.1.1: + expo-modules-core@2.2.0: dependencies: invariant: 2.2.4 - expo-status-bar@2.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): + expo-status-bar@2.0.1(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 - react-native: 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1) + react-native: 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1) - expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): + expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.26.0 - '@expo/cli': 0.22.5(encoding@0.1.13) - '@expo/config': 10.0.7 - '@expo/config-plugins': 9.0.13 - '@expo/fingerprint': 0.11.3 - '@expo/metro-config': 0.19.7 + '@babel/runtime': 7.26.7 + '@expo/cli': 0.22.11(encoding@0.1.13) + '@expo/config': 10.0.8 + '@expo/config-plugins': 9.0.14 + '@expo/fingerprint': 0.11.7 + '@expo/metro-config': 0.19.9 '@expo/vector-icons': 14.0.4 - babel-preset-expo: 12.0.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0)) - expo-asset: 11.0.1(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) - expo-constants: 17.0.3(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1)) - expo-file-system: 18.0.6(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1)) - expo-font: 13.0.2(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react@18.3.1) - expo-keep-awake: 14.0.1(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react@18.3.1) - expo-modules-autolinking: 2.0.4 - expo-modules-core: 2.1.1 + babel-preset-expo: 12.0.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0)) + expo-asset: 11.0.2(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + expo-constants: 17.0.5(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1)) + expo-file-system: 18.0.7(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1)) + expo-font: 13.0.3(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react@18.3.1) + expo-keep-awake: 14.0.2(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13)(react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react@18.3.1) + expo-modules-autolinking: 2.0.7 + expo-modules-core: 2.2.0 fbemitter: 3.0.0(encoding@0.1.13) react: 18.3.1 - react-native: 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1) + react-native: 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1) web-streams-polyfill: 3.3.3 whatwg-url-without-unicode: 8.0.0-3 optionalDependencies: - react-native-webview: 13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + react-native-webview: 13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - '@babel/core' - '@babel/preset-env' @@ -23158,7 +23181,7 @@ snapshots: fast-shallow-equal@1.0.0: {} - fast-uri@3.0.5: {} + fast-uri@3.0.6: {} fast-write-atomic@0.2.1: {} @@ -23200,7 +23223,7 @@ snapshots: transitivePeerDependencies: - encoding - fdir@6.4.2(picomatch@4.0.2): + fdir@6.4.3(picomatch@4.0.2): optionalDependencies: picomatch: 4.0.2 @@ -23281,13 +23304,13 @@ snapshots: flow-enums-runtime@0.0.6: {} - flow-parser@0.258.1: {} + flow-parser@0.259.1: {} follow-redirects@1.15.9: {} fontfaceobserver@2.3.0: {} - for-each@0.3.3: + for-each@0.3.4: dependencies: is-callable: 1.2.7 @@ -23324,8 +23347,8 @@ snapshots: framer-motion@11.15.0(@emotion/is-prop-valid@1.2.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - motion-dom: 11.16.4 - motion-utils: 11.16.0 + motion-dom: 11.18.1 + motion-utils: 11.18.1 tslib: 2.8.1 optionalDependencies: '@emotion/is-prop-valid': 1.2.2 @@ -23435,7 +23458,7 @@ snapshots: call-bind-apply-helpers: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 @@ -23452,7 +23475,7 @@ snapshots: get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 get-stdin@9.0.0: {} @@ -23470,7 +23493,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.2.7 - get-tsconfig@4.8.1: + get-tsconfig@4.10.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -23564,7 +23587,7 @@ snapshots: dependencies: csstype: 3.1.3 - google-auth-library@9.15.0(encoding@0.1.13): + google-auth-library@9.15.1(encoding@0.1.13): dependencies: base64-js: 1.5.1 ecdsa-sig-formatter: 1.0.11 @@ -23580,8 +23603,8 @@ snapshots: dependencies: extend: 3.0.2 gaxios: 6.7.1(encoding@0.1.13) - google-auth-library: 9.15.0(encoding@0.1.13) - qs: 6.13.1 + google-auth-library: 9.15.1(encoding@0.1.13) + qs: 6.14.0 url-template: 2.0.8 uuid: 9.0.1 transitivePeerDependencies: @@ -23590,7 +23613,7 @@ snapshots: googleapis@144.0.0(encoding@0.1.13): dependencies: - google-auth-library: 9.15.0(encoding@0.1.13) + google-auth-library: 9.15.1(encoding@0.1.13) googleapis-common: 7.2.0(encoding@0.1.13) transitivePeerDependencies: - encoding @@ -23664,7 +23687,7 @@ snapshots: estree-util-is-identifier-name: 3.0.0 hast-util-whitespace: 3.0.0 mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.1.3 + mdast-util-mdx-jsx: 3.2.0 mdast-util-mdxjs-esm: 2.0.1 property-information: 6.5.0 space-separated-tokens: 2.0.2 @@ -23698,7 +23721,7 @@ snapshots: estree-util-is-identifier-name: 3.0.0 hast-util-whitespace: 3.0.0 mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.1.3 + mdast-util-mdx-jsx: 3.2.0 mdast-util-mdxjs-esm: 2.0.1 property-information: 6.5.0 space-separated-tokens: 2.0.2 @@ -23786,6 +23809,8 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 + html-escaper@2.0.2: {} + html-to-text@9.0.5: dependencies: '@selderee/plugin-htmlparser2': 0.11.0 @@ -23942,11 +23967,11 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - intl-messageformat@10.7.11: + intl-messageformat@10.7.14: dependencies: '@formatjs/ecma402-abstract': 2.3.2 '@formatjs/fast-memoize': 2.2.6 - '@formatjs/icu-messageformat-parser': 2.9.8 + '@formatjs/icu-messageformat-parser': 2.11.0 tslib: 2.8.1 invariant@2.2.4: @@ -23988,8 +24013,9 @@ snapshots: is-arrayish@0.3.2: {} - is-async-function@2.1.0: + is-async-function@2.1.1: dependencies: + async-function: 1.0.0 call-bound: 1.0.3 get-proto: 1.0.1 has-tostringtag: 1.0.2 @@ -24206,17 +24232,36 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: '@babel/core': 7.26.0 - '@babel/parser': 7.26.5 + '@babel/parser': 7.26.7 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 transitivePeerDependencies: - supports-color + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + debug: 4.4.0 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 get-intrinsic: 1.2.7 get-proto: 1.0.1 has-symbols: 1.1.0 @@ -24364,21 +24409,21 @@ snapshots: jsc-safe-url@0.2.4: {} - jscodeshift@0.14.0(@babel/preset-env@7.26.0(@babel/core@7.26.0)): + jscodeshift@0.14.0(@babel/preset-env@7.26.7(@babel/core@7.26.0)): dependencies: '@babel/core': 7.26.0 - '@babel/parser': 7.26.3 + '@babel/parser': 7.26.7 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.26.0) '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.26.0) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.26.0) '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) - '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + '@babel/preset-env': 7.26.7(@babel/core@7.26.0) '@babel/preset-flow': 7.25.9(@babel/core@7.26.0) '@babel/preset-typescript': 7.26.0(@babel/core@7.26.0) '@babel/register': 7.25.9(@babel/core@7.26.0) babel-core: 7.0.0-bridge.0(@babel/core@7.26.0) chalk: 4.1.2 - flow-parser: 0.258.1 + flow-parser: 0.259.1 graceful-fs: 4.2.11 micromatch: 4.0.8 neo-async: 2.6.2 @@ -24395,7 +24440,7 @@ snapshots: dependencies: cssstyle: 4.2.1 data-urls: 5.0.0 - decimal.js: 10.4.3 + decimal.js: 10.5.0 form-data: 4.0.1 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 @@ -24523,19 +24568,19 @@ snapshots: kolorist@1.8.0: {} - langfuse-core@3.32.0: + langfuse-core@3.33.1: dependencies: mustache: 4.2.0 - langfuse-vercel@3.31.3(ai@4.0.18(react@19.0.0)(zod@3.24.1)): + langfuse-vercel@3.31.3(ai@4.1.17(react@19.0.0)(zod@3.24.1)): dependencies: - ai: 4.0.18(react@19.0.0)(zod@3.24.1) - langfuse: 3.32.0 - langfuse-core: 3.32.0 + ai: 4.1.17(react@19.0.0)(zod@3.24.1) + langfuse: 3.33.1 + langfuse-core: 3.33.1 - langfuse@3.32.0: + langfuse@3.33.1: dependencies: - langfuse-core: 3.32.0 + langfuse-core: 3.33.1 language-subtag-registry@0.3.23: {} @@ -24665,8 +24710,8 @@ snapshots: local-pkg@0.5.1: dependencies: - mlly: 1.7.3 - pkg-types: 1.3.0 + mlly: 1.7.4 + pkg-types: 1.3.1 locate-path@3.0.0: dependencies: @@ -24758,7 +24803,7 @@ snapshots: lottie-web@5.12.2: {} - loupe@3.1.2: {} + loupe@3.1.3: {} lru-cache@10.4.3: {} @@ -24798,6 +24843,12 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + magicast@0.3.5: + dependencies: + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 + source-map-js: 1.2.1 + make-dir@2.1.0: dependencies: pify: 4.0.1 @@ -24807,6 +24858,10 @@ snapshots: dependencies: semver: 6.3.1 + make-dir@4.0.0: + dependencies: + semver: 7.6.3 + make-error@1.3.6: {} make-event-props@1.6.2: {} @@ -24957,7 +25012,7 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-mdx-jsx@3.1.3: + mdast-util-mdx-jsx@3.2.0: dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 @@ -24978,7 +25033,7 @@ snapshots: dependencies: mdast-util-from-markdown: 2.0.2 mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.1.3 + mdast-util-mdx-jsx: 3.2.0 mdast-util-mdxjs-esm: 2.0.1 mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: @@ -25004,7 +25059,7 @@ snapshots: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.2.1 + '@ungap/structured-clone': 1.3.0 devlop: 1.1.0 micromark-util-sanitize-uri: 2.0.1 trim-lines: 3.0.1 @@ -25195,18 +25250,18 @@ snapshots: metro-runtime@0.80.12: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 flow-enums-runtime: 0.0.6 metro-runtime@0.81.0: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 flow-enums-runtime: 0.0.6 metro-source-map@0.80.12: dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 flow-enums-runtime: 0.0.6 invariant: 2.2.4 metro-symbolicate: 0.80.12 @@ -25219,9 +25274,9 @@ snapshots: metro-source-map@0.81.0: dependencies: - '@babel/traverse': 7.26.4 - '@babel/traverse--for-generate-function-map': '@babel/traverse@7.26.5' - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.7 + '@babel/traverse--for-generate-function-map': '@babel/traverse@7.26.7' + '@babel/types': 7.26.7 flow-enums-runtime: 0.0.6 invariant: 2.2.4 metro-symbolicate: 0.81.0 @@ -25259,9 +25314,9 @@ snapshots: metro-transform-plugins@0.80.12: dependencies: '@babel/core': 7.26.0 - '@babel/generator': 7.26.3 + '@babel/generator': 7.26.5 '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.7 flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: @@ -25272,7 +25327,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/generator': 7.26.5 '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 + '@babel/traverse': 7.26.7 flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: @@ -25281,9 +25336,9 @@ snapshots: metro-transform-worker@0.80.12: dependencies: '@babel/core': 7.26.0 - '@babel/generator': 7.26.3 - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 flow-enums-runtime: 0.0.6 metro: 0.80.12 metro-babel-transformer: 0.80.12 @@ -25302,8 +25357,8 @@ snapshots: dependencies: '@babel/core': 7.26.0 '@babel/generator': 7.26.5 - '@babel/parser': 7.26.5 - '@babel/types': 7.26.5 + '@babel/parser': 7.26.7 + '@babel/types': 7.26.7 flow-enums-runtime: 0.0.6 metro: 0.81.0 metro-babel-transformer: 0.81.0 @@ -25322,11 +25377,11 @@ snapshots: dependencies: '@babel/code-frame': 7.26.2 '@babel/core': 7.26.0 - '@babel/generator': 7.26.3 - '@babel/parser': 7.26.3 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.7 '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0 @@ -25371,11 +25426,11 @@ snapshots: dependencies: '@babel/code-frame': 7.26.2 '@babel/core': 7.26.0 - '@babel/generator': 7.26.3 - '@babel/parser': 7.26.3 + '@babel/generator': 7.26.5 + '@babel/parser': 7.26.7 '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0 @@ -25431,7 +25486,7 @@ snapshots: micromark-util-html-tag-name: 2.0.1 micromark-util-normalize-identifier: 2.0.1 micromark-util-resolve-all: 2.0.1 - micromark-util-subtokenize: 2.0.3 + micromark-util-subtokenize: 2.0.4 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.1 @@ -25462,7 +25517,7 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.1 - micromark-extension-gfm-table@2.1.0: + micromark-extension-gfm-table@2.1.1: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.1 @@ -25487,7 +25542,7 @@ snapshots: micromark-extension-gfm-autolink-literal: 2.1.0 micromark-extension-gfm-footnote: 2.1.0 micromark-extension-gfm-strikethrough: 2.1.0 - micromark-extension-gfm-table: 2.1.0 + micromark-extension-gfm-table: 2.1.1 micromark-extension-gfm-tagfilter: 2.0.0 micromark-extension-gfm-task-list-item: 2.1.0 micromark-util-combine-extensions: 2.0.1 @@ -25649,7 +25704,7 @@ snapshots: micromark-util-encode: 2.0.1 micromark-util-symbol: 2.0.1 - micromark-util-subtokenize@2.0.3: + micromark-util-subtokenize@2.0.4: dependencies: devlop: 1.1.0 micromark-util-chunked: 2.0.1 @@ -25676,7 +25731,7 @@ snapshots: micromark-util-normalize-identifier: 2.0.1 micromark-util-resolve-all: 2.0.1 micromark-util-sanitize-uri: 2.0.1 - micromark-util-subtokenize: 2.0.3 + micromark-util-subtokenize: 2.0.4 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.1 transitivePeerDependencies: @@ -25789,11 +25844,11 @@ snapshots: mkdirp@1.0.4: {} - mlly@1.7.3: + mlly@1.7.4: dependencies: acorn: 8.14.0 - pathe: 1.1.2 - pkg-types: 1.3.0 + pathe: 2.0.2 + pkg-types: 1.3.1 ufo: 1.5.4 mobx-react-lite@4.1.0(mobx@6.13.5)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): @@ -25818,11 +25873,11 @@ snapshots: moo@0.5.2: {} - motion-dom@11.16.4: + motion-dom@11.18.1: dependencies: - motion-utils: 11.16.0 + motion-utils: 11.18.1 - motion-utils@11.16.0: {} + motion-utils@11.18.1: {} mqtt-packet@6.10.0: dependencies: @@ -25885,7 +25940,7 @@ snapshots: react-dom: 19.0.0(react@19.0.0) rtl-css-js: 1.16.1 stacktrace-js: 2.0.2 - stylis: 4.3.4 + stylis: 4.3.5 nanoid@3.3.8: {} @@ -25914,7 +25969,7 @@ snapshots: next-auth@4.24.11(next@15.1.2(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(nodemailer@6.9.16)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 '@panva/hkdf': 1.2.1 cookie: 0.7.2 jose: 4.15.9 @@ -25976,7 +26031,7 @@ snapshots: '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 - caniuse-lite: 1.0.30001692 + caniuse-lite: 1.0.30001696 postcss: 8.4.31 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) @@ -26089,7 +26144,7 @@ snapshots: normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.2 + resolve: 1.22.10 semver: 5.7.2 validate-npm-package-license: 3.0.4 @@ -26205,7 +26260,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.3 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 has-symbols: 1.1.0 object-keys: 1.1.1 @@ -26213,14 +26268,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 object.fromentries@2.0.8: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.9 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: @@ -26233,7 +26288,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.3 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 oidc-token-hash@5.0.3: {} @@ -26514,6 +26569,8 @@ snapshots: pathe@1.1.2: {} + pathe@2.0.2: {} + pathval@2.0.0: {} pbkdf2@3.1.2: @@ -26570,11 +26627,11 @@ snapshots: dependencies: find-up: 5.0.0 - pkg-types@1.3.0: + pkg-types@1.3.1: dependencies: confbox: 0.1.8 - mlly: 1.7.3 - pathe: 1.1.2 + mlly: 1.7.4 + pathe: 2.0.2 playwright-core@1.49.1: {} @@ -26602,7 +26659,7 @@ snapshots: polished@4.3.1: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 possible-typed-array-names@1.0.0: {} @@ -26626,12 +26683,12 @@ snapshots: postcss: 8.4.49 ts-node: 10.9.2(@types/node@22.10.2)(typescript@5.7.2) - postcss-load-config@6.0.1(jiti@2.4.1)(postcss@8.5.1)(tsx@4.19.2)(yaml@2.7.0): + postcss-load-config@6.0.1(jiti@2.4.1)(postcss@8.4.49)(tsx@4.19.2)(yaml@2.7.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.4.1 - postcss: 8.5.1 + postcss: 8.4.49 tsx: 4.19.2 yaml: 2.7.0 @@ -26670,12 +26727,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.1: - dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postgres-array@2.0.0: {} postgres-bytea@1.0.0: {} @@ -26693,10 +26744,9 @@ snapshots: preact: 10.25.2 web-vitals: 4.2.4 - posthog-node@4.3.2: + posthog-node@4.4.1: dependencies: axios: 1.7.9 - rusha: 0.8.14 transitivePeerDependencies: - debug @@ -26709,9 +26759,9 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-packagejson@2.5.6(prettier@3.4.2): + prettier-plugin-packagejson@2.5.8(prettier@3.4.2): dependencies: - sort-package-json: 2.12.0 + sort-package-json: 2.14.0 synckit: 0.9.2 optionalDependencies: prettier: 3.4.2 @@ -26854,7 +26904,7 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 - qs@6.13.1: + qs@6.14.0: dependencies: side-channel: 1.1.0 @@ -26954,11 +27004,11 @@ snapshots: dependencies: typescript: 5.7.2 - react-docgen@7.1.0: + react-docgen@7.1.1: dependencies: '@babel/core': 7.26.0 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 + '@babel/traverse': 7.26.7 + '@babel/types': 7.26.7 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.6 '@types/doctrine': 0.0.9 @@ -26982,7 +27032,7 @@ snapshots: react-error-boundary@3.1.4(react@19.0.0): dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 react: 19.0.0 react-fit@2.0.1(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): @@ -27039,33 +27089,33 @@ snapshots: transitivePeerDependencies: - supports-color - react-native-webview@13.12.5(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): + react-native-webview@13.12.5(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): dependencies: escape-string-regexp: 4.0.0 invariant: 2.2.4 react: 18.3.1 - react-native: 0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1) + react-native: 0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1) - react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): + react-native-webview@13.12.5(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1): dependencies: escape-string-regexp: 4.0.0 invariant: 2.2.4 react: 18.3.1 - react-native: 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1) + react-native: 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1) - react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1): + react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1): dependencies: '@jest/create-cache-key-function': 29.7.0 '@react-native-community/cli': 13.6.9(encoding@0.1.13) '@react-native-community/cli-platform-android': 13.6.9(encoding@0.1.13) '@react-native-community/cli-platform-ios': 13.6.9(encoding@0.1.13) '@react-native/assets-registry': 0.74.87 - '@react-native/codegen': 0.74.87(@babel/preset-env@7.26.0(@babel/core@7.26.0)) - '@react-native/community-cli-plugin': 0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(encoding@0.1.13) + '@react-native/codegen': 0.74.87(@babel/preset-env@7.26.7(@babel/core@7.26.0)) + '@react-native/community-cli-plugin': 0.74.87(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(encoding@0.1.13) '@react-native/gradle-plugin': 0.74.87 '@react-native/js-polyfills': 0.74.87 '@react-native/normalize-colors': 0.74.87 - '@react-native/virtualized-lists': 0.74.87(@types/react@18.3.11)(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + '@react-native/virtualized-lists': 0.74.87(@types/react@18.3.11)(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@types/react@18.3.11)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -27103,16 +27153,16 @@ snapshots: - supports-color - utf-8-validate - react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1): + react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native/assets-registry': 0.76.5 - '@react-native/codegen': 0.76.5(@babel/preset-env@7.26.0(@babel/core@7.26.0)) - '@react-native/community-cli-plugin': 0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(encoding@0.1.13) - '@react-native/gradle-plugin': 0.76.5 - '@react-native/js-polyfills': 0.76.5 - '@react-native/normalize-colors': 0.76.5 - '@react-native/virtualized-lists': 0.76.5(@types/react@19.0.1)(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@19.0.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) + '@react-native/assets-registry': 0.76.6 + '@react-native/codegen': 0.76.6(@babel/preset-env@7.26.7(@babel/core@7.26.0)) + '@react-native/community-cli-plugin': 0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(encoding@0.1.13) + '@react-native/gradle-plugin': 0.76.6 + '@react-native/js-polyfills': 0.76.6 + '@react-native/normalize-colors': 0.76.6 + '@react-native/virtualized-lists': 0.76.6(@types/react@18.3.18)(react-native@0.76.6(@babel/core@7.26.0)(@babel/preset-env@7.26.7(@babel/core@7.26.0))(@react-native-community/cli-server-api@13.6.9(encoding@0.1.13))(@types/react@18.3.18)(encoding@0.1.13)(react@18.3.1))(react@18.3.1) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -27145,7 +27195,7 @@ snapshots: ws: 6.2.3 yargs: 17.7.2 optionalDependencies: - '@types/react': 19.0.1 + '@types/react': 18.3.18 transitivePeerDependencies: - '@babel/core' - '@babel/preset-env' @@ -27167,24 +27217,24 @@ snapshots: react-refresh@0.14.2: {} - react-remove-scroll-bar@2.3.8(@types/react@18.3.11)(react@19.0.0): + react-remove-scroll-bar@2.3.8(@types/react@18.3.18)(react@19.0.0): dependencies: react: 19.0.0 - react-style-singleton: 2.2.3(@types/react@18.3.11)(react@19.0.0) + react-style-singleton: 2.2.3(@types/react@18.3.18)(react@19.0.0) tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 - react-remove-scroll@2.6.0(@types/react@18.3.11)(react@19.0.0): + react-remove-scroll@2.6.0(@types/react@18.3.18)(react@19.0.0): dependencies: react: 19.0.0 - react-remove-scroll-bar: 2.3.8(@types/react@18.3.11)(react@19.0.0) - react-style-singleton: 2.2.3(@types/react@18.3.11)(react@19.0.0) + react-remove-scroll-bar: 2.3.8(@types/react@18.3.18)(react@19.0.0) + react-style-singleton: 2.2.3(@types/react@18.3.18)(react@19.0.0) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@18.3.11)(react@19.0.0) - use-sidecar: 1.1.3(@types/react@18.3.11)(react@19.0.0) + use-callback-ref: 1.3.3(@types/react@18.3.18)(react@19.0.0) + use-sidecar: 1.1.3(@types/react@18.3.18)(react@19.0.0) optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 react-responsive-embed@2.1.0(prop-types@15.8.1)(react@19.0.0): dependencies: @@ -27203,13 +27253,13 @@ snapshots: react: 19.0.0 react-is: 18.3.1 - react-style-singleton@2.2.3(@types/react@18.3.11)(react@19.0.0): + react-style-singleton@2.2.3(@types/react@18.3.18)(react@19.0.0): dependencies: get-nonce: 1.0.1 react: 19.0.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 react-tabs@6.1.0(react@19.0.0): dependencies: @@ -27300,7 +27350,7 @@ snapshots: dependencies: picomatch: 2.3.1 - readdirp@4.0.2: {} + readdirp@4.1.1: {} readline@1.3.0: {} @@ -27366,7 +27416,7 @@ snapshots: redoc@2.2.0(core-js@3.40.0)(encoding@0.1.13)(enzyme@3.11.0)(mobx@6.13.5)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(styled-components@6.1.14(react-dom@19.0.0(react@19.0.0))(react@19.0.0)): dependencies: '@cfaester/enzyme-adapter-react-18': 0.8.0(enzyme@3.11.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@redocly/openapi-core': 1.27.1(encoding@0.1.13) + '@redocly/openapi-core': 1.27.2(encoding@0.1.13) classnames: 2.5.1 core-js: 3.40.0 decko: 1.2.0 @@ -27404,7 +27454,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 get-intrinsic: 1.2.7 get-proto: 1.0.1 which-builtin-type: 1.2.1 @@ -27429,7 +27479,7 @@ snapshots: regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 regex-recursion@4.3.0: dependencies: @@ -27548,7 +27598,7 @@ snapshots: require-from-string@2.0.2: {} - require-in-the-middle@7.4.0: + require-in-the-middle@7.5.0: dependencies: debug: 4.4.0 module-details-from-path: 1.0.3 @@ -27656,54 +27706,29 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - rollup@4.30.1: + rollup@4.32.1: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.30.1 - '@rollup/rollup-android-arm64': 4.30.1 - '@rollup/rollup-darwin-arm64': 4.30.1 - '@rollup/rollup-darwin-x64': 4.30.1 - '@rollup/rollup-freebsd-arm64': 4.30.1 - '@rollup/rollup-freebsd-x64': 4.30.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 - '@rollup/rollup-linux-arm-musleabihf': 4.30.1 - '@rollup/rollup-linux-arm64-gnu': 4.30.1 - '@rollup/rollup-linux-arm64-musl': 4.30.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 - '@rollup/rollup-linux-riscv64-gnu': 4.30.1 - '@rollup/rollup-linux-s390x-gnu': 4.30.1 - '@rollup/rollup-linux-x64-gnu': 4.30.1 - '@rollup/rollup-linux-x64-musl': 4.30.1 - '@rollup/rollup-win32-arm64-msvc': 4.30.1 - '@rollup/rollup-win32-ia32-msvc': 4.30.1 - '@rollup/rollup-win32-x64-msvc': 4.30.1 - fsevents: 2.3.3 - - rollup@4.31.0: - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.31.0 - '@rollup/rollup-android-arm64': 4.31.0 - '@rollup/rollup-darwin-arm64': 4.31.0 - '@rollup/rollup-darwin-x64': 4.31.0 - '@rollup/rollup-freebsd-arm64': 4.31.0 - '@rollup/rollup-freebsd-x64': 4.31.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.31.0 - '@rollup/rollup-linux-arm-musleabihf': 4.31.0 - '@rollup/rollup-linux-arm64-gnu': 4.31.0 - '@rollup/rollup-linux-arm64-musl': 4.31.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.31.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.31.0 - '@rollup/rollup-linux-riscv64-gnu': 4.31.0 - '@rollup/rollup-linux-s390x-gnu': 4.31.0 - '@rollup/rollup-linux-x64-gnu': 4.31.0 - '@rollup/rollup-linux-x64-musl': 4.31.0 - '@rollup/rollup-win32-arm64-msvc': 4.31.0 - '@rollup/rollup-win32-ia32-msvc': 4.31.0 - '@rollup/rollup-win32-x64-msvc': 4.31.0 + '@rollup/rollup-android-arm-eabi': 4.32.1 + '@rollup/rollup-android-arm64': 4.32.1 + '@rollup/rollup-darwin-arm64': 4.32.1 + '@rollup/rollup-darwin-x64': 4.32.1 + '@rollup/rollup-freebsd-arm64': 4.32.1 + '@rollup/rollup-freebsd-x64': 4.32.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.32.1 + '@rollup/rollup-linux-arm-musleabihf': 4.32.1 + '@rollup/rollup-linux-arm64-gnu': 4.32.1 + '@rollup/rollup-linux-arm64-musl': 4.32.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.32.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.32.1 + '@rollup/rollup-linux-riscv64-gnu': 4.32.1 + '@rollup/rollup-linux-s390x-gnu': 4.32.1 + '@rollup/rollup-linux-x64-gnu': 4.32.1 + '@rollup/rollup-linux-x64-musl': 4.32.1 + '@rollup/rollup-win32-arm64-msvc': 4.32.1 + '@rollup/rollup-win32-ia32-msvc': 4.32.1 + '@rollup/rollup-win32-x64-msvc': 4.32.1 fsevents: 2.3.3 rrweb-cssom@0.7.1: {} @@ -27717,14 +27742,12 @@ snapshots: rtl-css-js@1.16.1: dependencies: - '@babel/runtime': 7.26.0 + '@babel/runtime': 7.26.7 run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - rusha@0.8.14: {} - rxjs@7.8.1: dependencies: tslib: 2.8.1 @@ -27929,7 +27952,7 @@ snapshots: dependencies: dunder-proto: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 setimmediate@1.0.5: {} @@ -28103,7 +28126,7 @@ snapshots: sort-object-keys@1.1.3: {} - sort-package-json@2.12.0: + sort-package-json@2.14.0: dependencies: detect-indent: 7.0.1 detect-newline: 4.0.1 @@ -28145,16 +28168,16 @@ snapshots: spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.20 + spdx-license-ids: 3.0.21 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.20 + spdx-license-ids: 3.0.21 - spdx-license-ids@3.0.20: {} + spdx-license-ids@3.0.21: {} split2@3.2.2: dependencies: @@ -28278,7 +28301,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 get-intrinsic: 1.2.7 gopd: 1.2.0 has-symbols: 1.1.0 @@ -28299,7 +28322,7 @@ snapshots: define-data-property: 1.1.4 define-properties: 1.2.1 es-abstract: 1.23.9 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 string.prototype.trimend@1.0.9: @@ -28307,13 +28330,13 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.3 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 string_decoder@1.1.1: dependencies: @@ -28363,7 +28386,7 @@ snapshots: stripe@16.7.0: dependencies: '@types/node': 22.10.2 - qs: 6.13.1 + qs: 6.14.0 strnum@1.0.5: {} @@ -28394,7 +28417,7 @@ snapshots: stylis@4.3.2: {} - stylis@4.3.4: {} + stylis@4.3.5: {} sucrase@3.35.0: dependencies: @@ -28468,6 +28491,8 @@ snapshots: tailwind-merge@2.5.5: {} + tailwind-merge@3.0.1: {} + tailwindcss@3.4.16(ts-node@10.9.2(@types/node@22.10.2)(typescript@5.7.2)): dependencies: '@alloc/quick-lru': 5.2.0 @@ -28575,6 +28600,12 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 + test-exclude@7.0.1: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 10.4.5 + minimatch: 9.0.5 + text-table@0.2.0: {} thenify-all@1.6.0: @@ -28614,20 +28645,22 @@ snapshots: tinyglobby@0.2.10: dependencies: - fdir: 6.4.2(picomatch@4.0.2) + fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 tinypool@1.0.2: {} tinyrainbow@1.2.0: {} + tinyrainbow@2.0.0: {} + tinyspy@3.0.2: {} - tldts-core@6.1.71: {} + tldts-core@6.1.75: {} - tldts@6.1.71: + tldts@6.1.75: dependencies: - tldts-core: 6.1.71 + tldts-core: 6.1.75 tmp@0.0.33: dependencies: @@ -28649,7 +28682,7 @@ snapshots: tough-cookie@5.1.0: dependencies: - tldts: 6.1.71 + tldts: 6.1.75 tr46@0.0.3: {} @@ -28730,19 +28763,19 @@ snapshots: tslib@2.8.1: {} - tsup@8.3.5(@microsoft/api-extractor@7.49.1(@types/node@22.10.2))(jiti@2.4.1)(postcss@8.5.1)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.7.0): + tsup@8.3.5(@microsoft/api-extractor@7.49.1(@types/node@22.10.2))(jiti@2.4.1)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.7.0): dependencies: bundle-require: 5.1.0(esbuild@0.24.0) cac: 6.7.14 chokidar: 4.0.3 - consola: 3.3.3 + consola: 3.4.0 debug: 4.4.0 esbuild: 0.24.0 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.4.1)(postcss@8.5.1)(tsx@4.19.2)(yaml@2.7.0) + postcss-load-config: 6.0.1(jiti@2.4.1)(postcss@8.4.49)(tsx@4.19.2)(yaml@2.7.0) resolve-from: 5.0.0 - rollup: 4.30.1 + rollup: 4.32.1 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tinyexec: 0.3.2 @@ -28750,7 +28783,7 @@ snapshots: tree-kill: 1.2.2 optionalDependencies: '@microsoft/api-extractor': 7.49.1(@types/node@22.10.2) - postcss: 8.5.1 + postcss: 8.4.49 typescript: 5.7.2 transitivePeerDependencies: - jiti @@ -28766,7 +28799,7 @@ snapshots: tsx@4.19.2: dependencies: esbuild: 0.23.1 - get-tsconfig: 4.8.1 + get-tsconfig: 4.10.0 optionalDependencies: fsevents: 2.3.3 @@ -28830,7 +28863,7 @@ snapshots: typed-array-byte-length@1.0.3: dependencies: call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.4 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 @@ -28839,7 +28872,7 @@ snapshots: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.4 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 @@ -28848,7 +28881,7 @@ snapshots: typed-array-length@1.0.7: dependencies: call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.4 gopd: 1.2.0 is-typed-array: 1.1.15 possible-typed-array-names: 1.0.0 @@ -28883,8 +28916,6 @@ snapshots: undici-types@6.20.0: {} - undici@6.21.0: {} - undici@6.21.1: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -29030,28 +29061,28 @@ snapshots: url@0.11.4: dependencies: punycode: 1.4.1 - qs: 6.13.1 + qs: 6.14.0 - use-callback-ref@1.3.3(@types/react@18.3.11)(react@19.0.0): + use-callback-ref@1.3.3(@types/react@18.3.18)(react@19.0.0): dependencies: react: 19.0.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 use-intl@3.26.3(react@19.0.0): dependencies: '@formatjs/fast-memoize': 2.2.6 - intl-messageformat: 10.7.11 + intl-messageformat: 10.7.14 react: 19.0.0 - use-sidecar@1.1.3(@types/react@18.3.11)(react@19.0.0): + use-sidecar@1.1.3(@types/react@18.3.18)(react@19.0.0): dependencies: detect-node-es: 1.1.0 react: 19.0.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 18.3.11 + '@types/react': 18.3.18 use-sync-external-store@1.4.0(react@19.0.0): dependencies: @@ -29118,10 +29149,52 @@ snapshots: - supports-color - terser - vite-plugin-dts@4.3.0(@types/node@22.10.2)(rollup@4.31.0)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + vite-node@3.0.4(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 2.0.2 + vite: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-node@3.0.5(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 2.0.2 + vite: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-plugin-dts@4.3.0(@types/node@22.10.2)(rollup@4.32.1)(typescript@5.7.2)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): dependencies: '@microsoft/api-extractor': 7.49.1(@types/node@22.10.2) - '@rollup/pluginutils': 5.1.4(rollup@4.31.0) + '@rollup/pluginutils': 5.1.4(rollup@4.32.1) '@volar/typescript': 2.4.11 '@vue/language-core': 2.1.6(typescript@5.7.2) compare-versions: 6.1.1 @@ -29137,9 +29210,9 @@ snapshots: - rollup - supports-color - vite-plugin-node-polyfills@0.22.0(rollup@4.31.0)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + vite-plugin-node-polyfills@0.22.0(rollup@4.32.1)(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): dependencies: - '@rollup/plugin-inject': 5.0.5(rollup@4.31.0) + '@rollup/plugin-inject': 5.0.5(rollup@4.32.1) node-stdlib-browser: 1.3.0 vite: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) transitivePeerDependencies: @@ -29160,7 +29233,7 @@ snapshots: dependencies: esbuild: 0.21.5 postcss: 8.4.49 - rollup: 4.31.0 + rollup: 4.32.1 optionalDependencies: '@types/node': 22.10.2 fsevents: 2.3.3 @@ -29171,7 +29244,7 @@ snapshots: dependencies: esbuild: 0.24.2 postcss: 8.4.49 - rollup: 4.31.0 + rollup: 4.32.1 optionalDependencies: '@types/node': 22.10.2 fsevents: 2.3.3 @@ -29187,6 +29260,12 @@ snapshots: typescript: 5.7.2 vitest: 2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0) + vitest-mock-extended@2.0.2(typescript@5.7.2)(vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)): + dependencies: + ts-essentials: 10.0.4(typescript@5.7.2) + typescript: 5.7.2 + vitest: 3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + vitest@2.1.8(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0): dependencies: '@vitest/expect': 2.1.8 @@ -29223,6 +29302,86 @@ snapshots: - supports-color - terser + vitest@3.0.4(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0): + dependencies: + '@vitest/expect': 3.0.4 + '@vitest/mocker': 3.0.4(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + '@vitest/pretty-format': 3.0.5 + '@vitest/runner': 3.0.4 + '@vitest/snapshot': 3.0.4 + '@vitest/spy': 3.0.4 + '@vitest/utils': 3.0.4 + chai: 5.1.2 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 2.0.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + vite-node: 3.0.4(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.10.2 + jsdom: 25.0.1 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@3.0.5(@types/debug@4.1.12)(@types/node@22.10.2)(jiti@2.4.1)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0): + dependencies: + '@vitest/expect': 3.0.5 + '@vitest/mocker': 3.0.5(vite@6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0)) + '@vitest/pretty-format': 3.0.5 + '@vitest/runner': 3.0.5 + '@vitest/snapshot': 3.0.5 + '@vitest/spy': 3.0.5 + '@vitest/utils': 3.0.5 + chai: 5.1.2 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 2.0.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 6.0.9(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + vite-node: 3.0.5(@types/node@22.10.2)(jiti@2.4.1)(lightningcss@1.27.0)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.10.2 + jsdom: 25.0.1 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vlq@1.0.1: {} vm-browserify@1.1.2: {} @@ -29341,7 +29500,7 @@ snapshots: call-bound: 1.0.3 function.prototype.name: 1.1.8 has-tostringtag: 1.0.2 - is-async-function: 2.1.0 + is-async-function: 2.1.1 is-date-object: 1.1.0 is-finalizationregistry: 1.1.1 is-generator-function: 1.1.0 @@ -29366,7 +29525,7 @@ snapshots: available-typed-arrays: 1.0.7 call-bind: 1.0.8 call-bound: 1.0.3 - for-each: 0.3.3 + for-each: 0.3.4 gopd: 1.2.0 has-tostringtag: 1.0.2 @@ -29519,7 +29678,7 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yjs@13.6.21: + yjs@13.6.23: dependencies: lib0: 0.2.99 diff --git a/sonar-project.properties b/sonar-project.properties index e98a715567..68ca14c10d 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,5 +1,6 @@ sonar.projectKey=formbricks_formbricks sonar.organization=formbricks +sonar.javascript.lcov.reportPaths=./apps/web/coverage/lcov.info # This is the name and version displayed in the SonarCloud UI. #sonar.projectName=formbricks diff --git a/turbo.json b/turbo.json index 32d10489ce..f74b80a5fe 100644 --- a/turbo.json +++ b/turbo.json @@ -49,6 +49,9 @@ "@formbricks/react-native#lint": { "dependsOn": ["@formbricks/api#build"] }, + "@formbricks/react-native#test": { + "dependsOn": ["@formbricks/api#build"] + }, "@formbricks/surveys#build": { "dependsOn": ["^build"], "outputs": ["dist/**"]