diff --git a/.github/actions/cache-build-web/action.yml b/.github/actions/cache-build-web/action.yml
index 6ae00d0203..25d18f4245 100644
--- a/.github/actions/cache-build-web/action.yml
+++ b/.github/actions/cache-build-web/action.yml
@@ -57,9 +57,6 @@ runs:
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
- sed -i "s/ENTERPRISE_LICENSE_KEY=.*/ENTERPRISE_LICENSE_KEY=${RANDOM_KEY}/" .env
echo "E2E_TESTING=${{ inputs.e2e_testing_mode }}" >> .env
shell: bash
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..d27ee547a7
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,84 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
+
+version: 2
+updates:
+ - package-ecosystem: "npm" # For pnpm monorepos, use npm ecosystem
+ directory: "/" # Root package.json
+ schedule:
+ interval: "weekly"
+ versioning-strategy: increase
+
+ # Apps directory packages
+ - package-ecosystem: "npm"
+ directory: "/apps/demo"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/apps/demo-react-native"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/apps/storybook"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/apps/web"
+ schedule:
+ interval: "weekly"
+
+ # Packages directory
+ - package-ecosystem: "npm"
+ directory: "/packages/database"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/packages/lib"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/packages/types"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/packages/config-eslint"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/packages/config-prettier"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/packages/config-typescript"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/packages/js-core"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/packages/surveys"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "npm"
+ directory: "/packages/logger"
+ schedule:
+ interval: "weekly"
+
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/release-docker-github-experimental.yml b/.github/workflows/release-docker-github-experimental.yml
index c009debdcd..25b8e5e61e 100644
--- a/.github/workflows/release-docker-github-experimental.yml
+++ b/.github/workflows/release-docker-github-experimental.yml
@@ -15,7 +15,6 @@ env:
IMAGE_NAME: ${{ github.repository }}-experimental
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/formbricks?schema=public"
permissions:
contents: read
@@ -80,6 +79,9 @@ jobs:
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+ secrets: |
+ database_url=${{ secrets.DUMMY_DATABASE_URL }}
+ encryption_key=${{ secrets.DUMMY_ENCRYPTION_KEY }}
cache-from: type=gha
cache-to: type=gha,mode=max
diff --git a/.github/workflows/release-docker-github.yml b/.github/workflows/release-docker-github.yml
index c09d66d553..457940fb7e 100644
--- a/.github/workflows/release-docker-github.yml
+++ b/.github/workflows/release-docker-github.yml
@@ -19,7 +19,6 @@ env:
IMAGE_NAME: ${{ github.repository }}
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/formbricks?schema=public"
permissions:
contents: read
@@ -100,6 +99,9 @@ jobs:
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+ secrets: |
+ database_url=${{ secrets.DUMMY_DATABASE_URL }}
+ encryption_key=${{ secrets.DUMMY_ENCRYPTION_KEY }}
cache-from: type=gha
cache-to: type=gha,mode=max
diff --git a/.github/workflows/terrafrom-plan-and-apply.yml b/.github/workflows/terrafrom-plan-and-apply.yml
index 49f2b99081..78d0c72e6c 100644
--- a/.github/workflows/terrafrom-plan-and-apply.yml
+++ b/.github/workflows/terrafrom-plan-and-apply.yml
@@ -3,16 +3,21 @@ name: 'Terraform'
on:
workflow_dispatch:
# TODO: enable it back when migration is completed.
-# push:
-# branches:
-# - main
-# pull_request:
-# branches:
-# - main
+ push:
+ branches:
+ - main
+ paths:
+ - "infra/terraform/**"
+ pull_request:
+ branches:
+ - main
+ paths:
+ - "infra/terraform/**"
permissions:
id-token: write
contents: write
+ pull-requests: write
jobs:
terraform:
@@ -58,18 +63,17 @@ jobs:
run: terraform plan -out .planfile
working-directory: infra/terraform
-# - name: Post PR comment
-# uses: borchero/terraform-plan-comment@3399d8dbae8b05185e815e02361ede2949cd99c4 # v2.4.0
-# if: always() && github.ref != 'refs/heads/main' && (steps.validate.outcome == 'success' || steps.validate.outcome == 'failure')
-# with:
-# token: ${{ github.token }}
-# planfile: .planfile
-# working-directory: "infra/terraform"
-# skip-comment: true
+ - name: Post PR comment
+ uses: borchero/terraform-plan-comment@3399d8dbae8b05185e815e02361ede2949cd99c4 # v2.4.0
+ if: always() && github.ref != 'refs/heads/main' && (steps.plan.outcome == 'success' || steps.plan.outcome == 'failure')
+ with:
+ token: ${{ github.token }}
+ planfile: .planfile
+ working-directory: "infra/terraform"
- name: Terraform Apply
id: apply
-# if: github.ref == 'refs/heads/main' && github.event_name == 'push'
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply .planfile
working-directory: "infra/terraform"
diff --git a/apps/storybook/package.json b/apps/storybook/package.json
index 5ffdc138d3..2399cd16d9 100644
--- a/apps/storybook/package.json
+++ b/apps/storybook/package.json
@@ -35,6 +35,6 @@
"prop-types": "15.8.1",
"storybook": "8.4.7",
"tsup": "8.3.5",
- "vite": "6.0.9"
+ "vite": "6.0.12"
}
}
diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile
index cf30c09ef2..3e8039e5f2 100644
--- a/apps/web/Dockerfile
+++ b/apps/web/Dockerfile
@@ -24,17 +24,27 @@ RUN corepack enable
# Install necessary build tools and compilers
RUN apk update && apk add --no-cache g++ cmake make gcc python3 openssl-dev jq
-# Set hardcoded environment variables
-ENV DATABASE_URL="postgresql://placeholder:for@build:5432/gets_overwritten_at_runtime?schema=public"
-ENV NEXTAUTH_SECRET="placeholder_for_next_auth_of_64_chars_get_overwritten_at_runtime"
-ENV ENCRYPTION_KEY="placeholder_for_build_key_of_64_chars_get_overwritten_at_runtime"
-ENV CRON_SECRET="placeholder_for_cron_secret_of_64_chars_get_overwritten_at_runtime"
+# BuildKit secret handling without hardcoded fallback values
+# This approach relies entirely on secrets passed from GitHub Actions
+RUN echo '#!/bin/sh' > /tmp/read-secrets.sh && \
+ echo 'if [ -f "/run/secrets/database_url" ]; then' >> /tmp/read-secrets.sh && \
+ echo ' export DATABASE_URL=$(cat /run/secrets/database_url)' >> /tmp/read-secrets.sh && \
+ echo 'else' >> /tmp/read-secrets.sh && \
+ echo ' echo "DATABASE_URL secret not found. Build may fail if this is required."' >> /tmp/read-secrets.sh && \
+ echo 'fi' >> /tmp/read-secrets.sh && \
+ echo 'if [ -f "/run/secrets/encryption_key" ]; then' >> /tmp/read-secrets.sh && \
+ echo ' export ENCRYPTION_KEY=$(cat /run/secrets/encryption_key)' >> /tmp/read-secrets.sh && \
+ echo 'else' >> /tmp/read-secrets.sh && \
+ echo ' echo "ENCRYPTION_KEY secret not found. Build may fail if this is required."' >> /tmp/read-secrets.sh && \
+ echo 'fi' >> /tmp/read-secrets.sh && \
+ echo 'exec "$@"' >> /tmp/read-secrets.sh && \
+ chmod +x /tmp/read-secrets.sh
-ARG NEXT_PUBLIC_SENTRY_DSN
ARG SENTRY_AUTH_TOKEN
-# Increase Node.js memory limit
-# ENV NODE_OPTIONS="--max_old_space_size=4096"
+# Increase Node.js memory limit as a regular build argument
+ARG NODE_OPTIONS="--max_old_space_size=4096"
+ENV NODE_OPTIONS=${NODE_OPTIONS}
# Set the working directory
WORKDIR /app
@@ -53,8 +63,11 @@ RUN touch apps/web/.env
# Install the dependencies
RUN pnpm install
-# Build the project
-RUN NODE_OPTIONS="--max_old_space_size=4096" pnpm build --filter=@formbricks/web...
+# Build the project using our secret reader script
+# This mounts the secrets only during this build step without storing them in layers
+RUN --mount=type=secret,id=database_url \
+ --mount=type=secret,id=encryption_key \
+ /tmp/read-secrets.sh pnpm build --filter=@formbricks/web...
# Extract Prisma version
RUN jq -r '.devDependencies.prisma' packages/database/package.json > /prisma_version.txt
diff --git a/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/components/OnboardingSetupInstructions.test.tsx b/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/components/OnboardingSetupInstructions.test.tsx
new file mode 100644
index 0000000000..d26c801406
--- /dev/null
+++ b/apps/web/app/(app)/(onboarding)/environments/[environmentId]/connect/components/OnboardingSetupInstructions.test.tsx
@@ -0,0 +1,103 @@
+import { cleanup, render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import toast from "react-hot-toast";
+import { afterEach, beforeAll, describe, expect, test, vi } from "vitest";
+import { OnboardingSetupInstructions } from "./OnboardingSetupInstructions";
+
+// Mock react-hot-toast so we can assert that a success message is shown
+vi.mock("react-hot-toast", () => ({
+ __esModule: true,
+ default: {
+ success: vi.fn(),
+ },
+}));
+
+// Set up a spy for navigator.clipboard.writeText so it becomes a ViTest spy.
+beforeAll(() => {
+ Object.defineProperty(navigator, "clipboard", {
+ configurable: true,
+ writable: true,
+ value: {
+ // Using a mockResolvedValue resolves the promise as writeText is async.
+ writeText: vi.fn().mockResolvedValue(undefined),
+ },
+ });
+});
+
+describe("OnboardingSetupInstructions", () => {
+ afterEach(() => {
+ cleanup();
+ vi.clearAllMocks();
+ });
+
+ // Provide some default props for testing
+ const defaultProps = {
+ environmentId: "env-123",
+ webAppUrl: "https://example.com",
+ channel: "app" as const, // Assuming channel is either "app" or "website"
+ widgetSetupCompleted: false,
+ };
+
+ test("renders HTML tab content by default", () => {
+ render( );
+
+ // Since the default active tab is "html", we check for a unique text
+ expect(
+ screen.getByText(/environments.connect.insert_this_code_into_the_head_tag_of_your_website/i)
+ ).toBeInTheDocument();
+
+ // The HTML snippet contains a marker comment
+ expect(screen.getByText("START")).toBeInTheDocument();
+
+ // Verify the "Copy Code" button is present
+ expect(screen.getByRole("button", { name: /common.copy_code/i })).toBeInTheDocument();
+ });
+
+ test("renders NPM tab content when selected", async () => {
+ render( );
+ const user = userEvent.setup();
+
+ // Click on the "NPM" tab to switch views.
+ const npmTab = screen.getByText("NPM");
+ await user.click(npmTab);
+
+ // Check that the install commands are present
+ expect(screen.getByText(/npm install @formbricks\/js/)).toBeInTheDocument();
+ expect(screen.getByText(/yarn add @formbricks\/js/)).toBeInTheDocument();
+
+ // Verify the "Read Docs" link has the correct URL (based on channel prop)
+ const readDocsLink = screen.getByRole("link", { name: /common.read_docs/i });
+ expect(readDocsLink).toHaveAttribute("href", "https://formbricks.com/docs/app-surveys/framework-guides");
+ });
+
+ test("copies HTML snippet to clipboard and shows success toast when Copy Code button is clicked", async () => {
+ render( );
+ const user = userEvent.setup();
+
+ const writeTextSpy = vi.spyOn(navigator.clipboard, "writeText");
+
+ // Click the "Copy Code" button
+ const copyButton = screen.getByRole("button", { name: /common.copy_code/i });
+ await user.click(copyButton);
+
+ // Ensure navigator.clipboard.writeText was called.
+ expect(writeTextSpy).toHaveBeenCalled();
+ const writtenText = (navigator.clipboard.writeText as any).mock.calls[0][0] as string;
+
+ // Check that the pasted snippet contains the expected environment values
+ expect(writtenText).toContain('var appUrl = "https://example.com"');
+ expect(writtenText).toContain('var environmentId = "env-123"');
+
+ // Verify that a success toast was shown
+ expect(toast.success).toHaveBeenCalledWith("common.copied_to_clipboard");
+ });
+
+ test("renders step-by-step manual link with correct URL in HTML tab", () => {
+ render( );
+ const manualLink = screen.getByRole("link", { name: /common.step_by_step_manual/i });
+ expect(manualLink).toHaveAttribute(
+ "href",
+ "https://formbricks.com/docs/app-surveys/framework-guides#html"
+ );
+ });
+});
diff --git a/apps/web/app/(app)/components/FormbricksClient.test.tsx b/apps/web/app/(app)/components/FormbricksClient.test.tsx
new file mode 100644
index 0000000000..a0e0b986ca
--- /dev/null
+++ b/apps/web/app/(app)/components/FormbricksClient.test.tsx
@@ -0,0 +1,77 @@
+import { render } from "@testing-library/react";
+import { afterEach, describe, expect, test, vi } from "vitest";
+import formbricks from "@formbricks/js";
+import { FormbricksClient } from "./FormbricksClient";
+
+// Mock next/navigation hooks.
+vi.mock("next/navigation", () => ({
+ usePathname: () => "/test-path",
+ useSearchParams: () => new URLSearchParams("foo=bar"),
+}));
+
+// Mock the environment variables.
+vi.mock("@formbricks/lib/env", () => ({
+ env: {
+ NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID: "env-test",
+ NEXT_PUBLIC_FORMBRICKS_API_HOST: "https://api.test.com",
+ },
+}));
+
+// Mock the flag that enables Formbricks.
+vi.mock("@/app/lib/formbricks", () => ({
+ formbricksEnabled: true,
+}));
+
+// Mock the Formbricks SDK module.
+vi.mock("@formbricks/js", () => ({
+ __esModule: true,
+ default: {
+ setup: vi.fn(),
+ setUserId: vi.fn(),
+ setEmail: vi.fn(),
+ registerRouteChange: vi.fn(),
+ },
+}));
+
+describe("FormbricksClient", () => {
+ afterEach(() => {
+ vi.clearAllMocks();
+ });
+
+ test("calls setup, setUserId, setEmail and registerRouteChange on mount when enabled", () => {
+ const mockSetup = vi.spyOn(formbricks, "setup");
+ const mockSetUserId = vi.spyOn(formbricks, "setUserId");
+ const mockSetEmail = vi.spyOn(formbricks, "setEmail");
+ const mockRegisterRouteChange = vi.spyOn(formbricks, "registerRouteChange");
+
+ render( );
+
+ // Expect the first effect to call setup and assign the provided user details.
+ expect(mockSetup).toHaveBeenCalledWith({
+ environmentId: "env-test",
+ appUrl: "https://api.test.com",
+ });
+ expect(mockSetUserId).toHaveBeenCalledWith("user-123");
+ expect(mockSetEmail).toHaveBeenCalledWith("test@example.com");
+
+ // And the second effect should always register the route change when Formbricks is enabled.
+ expect(mockRegisterRouteChange).toHaveBeenCalled();
+ });
+
+ test("does not call setup, setUserId, or setEmail if userId is not provided yet still calls registerRouteChange", () => {
+ const mockSetup = vi.spyOn(formbricks, "setup");
+ const mockSetUserId = vi.spyOn(formbricks, "setUserId");
+ const mockSetEmail = vi.spyOn(formbricks, "setEmail");
+ const mockRegisterRouteChange = vi.spyOn(formbricks, "registerRouteChange");
+
+ render( );
+
+ // Since userId is falsy, the first effect should not call setup or assign user details.
+ expect(mockSetup).not.toHaveBeenCalled();
+ expect(mockSetUserId).not.toHaveBeenCalled();
+ expect(mockSetEmail).not.toHaveBeenCalled();
+
+ // The second effect only checks formbricksEnabled, so registerRouteChange should be called.
+ expect(mockRegisterRouteChange).toHaveBeenCalled();
+ });
+});
diff --git a/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx b/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx
index eea9218900..205de99e2d 100644
--- a/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx
@@ -1,6 +1,5 @@
import { TopControlButtons } from "@/app/(app)/environments/[environmentId]/components/TopControlButtons";
import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/team";
-import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
import { TEnvironment } from "@formbricks/types/environment";
import { TOrganizationRole } from "@formbricks/types/memberships";
@@ -24,7 +23,6 @@ export const TopControlBar = ({
diff --git a/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx b/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx
index 2646546db3..22ef9d8218 100644
--- a/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx
@@ -6,9 +6,9 @@ import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { Button } from "@/modules/ui/components/button";
import { TooltipRenderer } from "@/modules/ui/components/tooltip";
import { useTranslate } from "@tolgee/react";
-import { CircleUserIcon, MessageCircleQuestionIcon, PlusIcon } from "lucide-react";
+import { BugIcon, CircleUserIcon, PlusIcon } from "lucide-react";
+import Link from "next/link";
import { useRouter } from "next/navigation";
-import formbricks from "@formbricks/js";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
import { TEnvironment } from "@formbricks/types/environment";
import { TOrganizationRole } from "@formbricks/types/memberships";
@@ -16,7 +16,6 @@ import { TOrganizationRole } from "@formbricks/types/memberships";
interface TopControlButtonsProps {
environment: TEnvironment;
environments: TEnvironment[];
- isFormbricksCloud: boolean;
membershipRole?: TOrganizationRole;
projectPermission: TTeamPermission | null;
}
@@ -24,7 +23,6 @@ interface TopControlButtonsProps {
export const TopControlButtons = ({
environment,
environments,
- isFormbricksCloud,
membershipRole,
projectPermission,
}: TopControlButtonsProps) => {
@@ -38,19 +36,15 @@ export const TopControlButtons = ({
return (
{!isBilling && }
- {isFormbricksCloud && (
-
- {
- formbricks.track("Top Menu: Product Feedback");
- }}>
-
-
-
- )}
+
+
+
+
+
+
+
+
+
}) => {
)}
-
+
);
};
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/page.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/page.tsx
index 018953705f..a76105d877 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/page.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/page.tsx
@@ -7,6 +7,7 @@ import { getIsAIEnabled } from "@/modules/ee/license-check/lib/utils";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
+import { SettingsId } from "@/modules/ui/components/settings-id";
import { getTranslate } from "@/tolgee/server";
import { notFound } from "next/navigation";
import {
@@ -93,6 +94,8 @@ const SurveyPage = async (props: { params: Promise<{ environmentId: string; surv
isReadOnly={isReadOnly}
locale={user.locale ?? DEFAULT_LOCALE}
/>
+
+
);
};
diff --git a/apps/web/app/api/(internal)/insights/lib/utils.test.ts b/apps/web/app/api/(internal)/insights/lib/utils.test.ts
new file mode 100644
index 0000000000..f772f17a32
--- /dev/null
+++ b/apps/web/app/api/(internal)/insights/lib/utils.test.ts
@@ -0,0 +1,390 @@
+import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
+import { CRON_SECRET, WEBAPP_URL } from "@formbricks/lib/constants";
+import { getSurvey, updateSurvey } from "@formbricks/lib/survey/service";
+import { mockSurveyOutput } from "@formbricks/lib/survey/tests/__mock__/survey.mock";
+import { doesSurveyHasOpenTextQuestion } from "@formbricks/lib/survey/utils";
+import { ResourceNotFoundError } from "@formbricks/types/errors";
+import { TSurvey, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
+import {
+ doesResponseHasAnyOpenTextAnswer,
+ generateInsightsEnabledForSurveyQuestions,
+ generateInsightsForSurvey,
+} from "./utils";
+
+// Mock all dependencies
+vi.mock("@formbricks/lib/constants", () => ({
+ CRON_SECRET: vi.fn(() => "mocked-cron-secret"),
+ WEBAPP_URL: "https://mocked-webapp-url.com",
+}));
+
+vi.mock("@formbricks/lib/survey/cache", () => ({
+ surveyCache: {
+ revalidate: vi.fn(),
+ },
+}));
+
+vi.mock("@formbricks/lib/survey/service", () => ({
+ getSurvey: vi.fn(),
+ updateSurvey: vi.fn(),
+}));
+
+vi.mock("@formbricks/lib/survey/utils", () => ({
+ doesSurveyHasOpenTextQuestion: vi.fn(),
+}));
+
+vi.mock("@formbricks/lib/utils/validate", () => ({
+ validateInputs: vi.fn(),
+}));
+
+// Mock global fetch
+const mockFetch = vi.fn();
+global.fetch = mockFetch;
+
+describe("Insights Utils", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ });
+
+ describe("generateInsightsForSurvey", () => {
+ test("should call fetch with correct parameters", () => {
+ const surveyId = "survey-123";
+ mockFetch.mockResolvedValueOnce({ ok: true });
+
+ generateInsightsForSurvey(surveyId);
+
+ expect(mockFetch).toHaveBeenCalledWith(`${WEBAPP_URL}/api/insights`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "x-api-key": CRON_SECRET,
+ },
+ body: JSON.stringify({
+ surveyId,
+ }),
+ });
+ });
+
+ test("should handle errors and return error object", () => {
+ const surveyId = "survey-123";
+ mockFetch.mockImplementationOnce(() => {
+ throw new Error("Network error");
+ });
+
+ const result = generateInsightsForSurvey(surveyId);
+
+ expect(result).toEqual({
+ ok: false,
+ error: new Error("Error while generating insights for survey: Network error"),
+ });
+ });
+
+ test("should throw error if CRON_SECRET is not set", async () => {
+ // Reset modules to ensure clean state
+ vi.resetModules();
+
+ // Mock CRON_SECRET as undefined
+ vi.doMock("@formbricks/lib/constants", () => ({
+ CRON_SECRET: undefined,
+ WEBAPP_URL: "https://mocked-webapp-url.com",
+ }));
+
+ // Re-import the utils module to get the mocked CRON_SECRET
+ const { generateInsightsForSurvey } = await import("./utils");
+
+ expect(() => generateInsightsForSurvey("survey-123")).toThrow("CRON_SECRET is not set");
+
+ // Reset modules after test
+ vi.resetModules();
+ });
+ });
+
+ describe("generateInsightsEnabledForSurveyQuestions", () => {
+ test("should return success=false when survey has no open text questions", async () => {
+ // Mock data
+ const surveyId = "survey-123";
+ const mockSurvey: TSurvey = {
+ ...mockSurveyOutput,
+ type: "link",
+ segment: null,
+ displayPercentage: null,
+ questions: [
+ {
+ id: "cm8cjnse3000009jxf20v91ic",
+ type: TSurveyQuestionTypeEnum.MultipleChoiceSingle,
+ headline: { default: "Question 1" },
+ required: true,
+ choices: [
+ {
+ id: "cm8cjnse3000009jxf20v91ic",
+ label: { default: "Choice 1" },
+ },
+ ],
+ },
+ {
+ id: "cm8cjo19c000109jx6znygc0u",
+ type: TSurveyQuestionTypeEnum.Rating,
+ headline: { default: "Question 2" },
+ required: true,
+ scale: "number",
+ range: 5,
+ isColorCodingEnabled: false,
+ },
+ ],
+ };
+
+ // Setup mocks
+ vi.mocked(getSurvey).mockResolvedValueOnce(mockSurvey);
+ vi.mocked(doesSurveyHasOpenTextQuestion).mockReturnValueOnce(false);
+
+ // Execute function
+ const result = await generateInsightsEnabledForSurveyQuestions(surveyId);
+
+ // Verify results
+ expect(result).toEqual({ success: false });
+ expect(updateSurvey).not.toHaveBeenCalled();
+ });
+
+ test("should return success=true when survey is updated with insights enabled", async () => {
+ vi.clearAllMocks();
+ // Mock data
+ const surveyId = "cm8ckvchx000008lb710n0gdn";
+
+ // Mock survey with open text questions that have no insightsEnabled property
+ const mockSurveyWithOpenTextQuestions: TSurvey = {
+ ...mockSurveyOutput,
+ id: surveyId,
+ type: "link",
+ segment: null,
+ displayPercentage: null,
+ questions: [
+ {
+ id: "cm8cjnse3000009jxf20v91ic",
+ type: TSurveyQuestionTypeEnum.OpenText,
+ headline: { default: "Question 1" },
+ required: true,
+ inputType: "text",
+ charLimit: {},
+ },
+ {
+ id: "cm8cjo19c000109jx6znygc0u",
+ type: TSurveyQuestionTypeEnum.OpenText,
+ headline: { default: "Question 2" },
+ required: true,
+ inputType: "text",
+ charLimit: {},
+ },
+ ],
+ };
+
+ // Define the updated survey that should be returned after updateSurvey
+ const mockUpdatedSurveyWithOpenTextQuestions: TSurvey = {
+ ...mockSurveyWithOpenTextQuestions,
+ questions: mockSurveyWithOpenTextQuestions.questions.map((q) => ({
+ ...q,
+ insightsEnabled: true, // Updated property
+ })),
+ };
+
+ // Setup mocks
+ vi.mocked(getSurvey).mockResolvedValueOnce(mockSurveyWithOpenTextQuestions);
+ vi.mocked(doesSurveyHasOpenTextQuestion).mockReturnValueOnce(true);
+ vi.mocked(updateSurvey).mockResolvedValueOnce(mockUpdatedSurveyWithOpenTextQuestions);
+
+ // Execute function
+ const result = await generateInsightsEnabledForSurveyQuestions(surveyId);
+
+ expect(result).toEqual({
+ success: true,
+ survey: mockUpdatedSurveyWithOpenTextQuestions,
+ });
+ });
+
+ test("should return success=false when all open text questions already have insightsEnabled defined", async () => {
+ // Mock data
+ const surveyId = "survey-123";
+ const mockSurvey: TSurvey = {
+ ...mockSurveyOutput,
+ type: "link",
+ segment: null,
+ displayPercentage: null,
+ questions: [
+ {
+ id: "cm8cjnse3000009jxf20v91ic",
+ type: TSurveyQuestionTypeEnum.OpenText,
+ headline: { default: "Question 1" },
+ required: true,
+ inputType: "text",
+ charLimit: {},
+ insightsEnabled: true,
+ },
+ {
+ id: "cm8cjo19c000109jx6znygc0u",
+ type: TSurveyQuestionTypeEnum.MultipleChoiceSingle,
+ headline: { default: "Question 2" },
+ required: true,
+ choices: [
+ {
+ id: "cm8cjnse3000009jxf20v91ic",
+ label: { default: "Choice 1" },
+ },
+ ],
+ },
+ ],
+ };
+
+ // Setup mocks
+ vi.mocked(getSurvey).mockResolvedValueOnce(mockSurvey);
+ vi.mocked(doesSurveyHasOpenTextQuestion).mockReturnValueOnce(true);
+
+ // Execute function
+ const result = await generateInsightsEnabledForSurveyQuestions(surveyId);
+
+ // Verify results
+ expect(result).toEqual({ success: false });
+ expect(updateSurvey).not.toHaveBeenCalled();
+ });
+
+ test("should throw ResourceNotFoundError if survey is not found", async () => {
+ // Setup mocks
+ vi.mocked(getSurvey).mockResolvedValueOnce(null);
+
+ // Execute and verify function
+ await expect(generateInsightsEnabledForSurveyQuestions("survey-123")).rejects.toThrow(
+ new ResourceNotFoundError("Survey", "survey-123")
+ );
+ });
+
+ test("should throw ResourceNotFoundError if updateSurvey returns null", async () => {
+ // Mock data
+ const surveyId = "survey-123";
+ const mockSurvey: TSurvey = {
+ ...mockSurveyOutput,
+ type: "link",
+ segment: null,
+ displayPercentage: null,
+ questions: [
+ {
+ id: "cm8cjnse3000009jxf20v91ic",
+ type: TSurveyQuestionTypeEnum.OpenText,
+ headline: { default: "Question 1" },
+ required: true,
+ inputType: "text",
+ charLimit: {},
+ },
+ ],
+ };
+
+ // Setup mocks
+ vi.mocked(getSurvey).mockResolvedValueOnce(mockSurvey);
+ vi.mocked(doesSurveyHasOpenTextQuestion).mockReturnValueOnce(true);
+ // Type assertion to handle the null case
+ vi.mocked(updateSurvey).mockResolvedValueOnce(null as unknown as TSurvey);
+
+ // Execute and verify function
+ await expect(generateInsightsEnabledForSurveyQuestions(surveyId)).rejects.toThrow(
+ new ResourceNotFoundError("Survey", surveyId)
+ );
+ });
+
+ test("should return success=false when no questions have insights enabled after update", async () => {
+ // Mock data
+ const surveyId = "survey-123";
+ const mockSurvey: TSurvey = {
+ ...mockSurveyOutput,
+ type: "link",
+ segment: null,
+ displayPercentage: null,
+ questions: [
+ {
+ id: "cm8cjnse3000009jxf20v91ic",
+ type: TSurveyQuestionTypeEnum.OpenText,
+ headline: { default: "Question 1" },
+ required: true,
+ inputType: "text",
+ charLimit: {},
+ insightsEnabled: false,
+ },
+ ],
+ };
+
+ // Setup mocks
+ vi.mocked(getSurvey).mockResolvedValueOnce(mockSurvey);
+ vi.mocked(doesSurveyHasOpenTextQuestion).mockReturnValueOnce(true);
+ vi.mocked(updateSurvey).mockResolvedValueOnce(mockSurvey);
+
+ // Execute function
+ const result = await generateInsightsEnabledForSurveyQuestions(surveyId);
+
+ // Verify results
+ expect(result).toEqual({ success: false });
+ });
+
+ test("should propagate any errors that occur", async () => {
+ // Setup mocks
+ const testError = new Error("Test error");
+ vi.mocked(getSurvey).mockRejectedValueOnce(testError);
+
+ // Execute and verify function
+ await expect(generateInsightsEnabledForSurveyQuestions("survey-123")).rejects.toThrow(testError);
+ });
+ });
+
+ describe("doesResponseHasAnyOpenTextAnswer", () => {
+ test("should return true when at least one open text question has an answer", () => {
+ const openTextQuestionIds = ["q1", "q2", "q3"];
+ const response = {
+ q1: "",
+ q2: "This is an answer",
+ q3: "",
+ q4: "This is not an open text answer",
+ };
+
+ const result = doesResponseHasAnyOpenTextAnswer(openTextQuestionIds, response);
+
+ expect(result).toBe(true);
+ });
+
+ test("should return false when no open text questions have answers", () => {
+ const openTextQuestionIds = ["q1", "q2", "q3"];
+ const response = {
+ q1: "",
+ q2: "",
+ q3: "",
+ q4: "This is not an open text answer",
+ };
+
+ const result = doesResponseHasAnyOpenTextAnswer(openTextQuestionIds, response);
+
+ expect(result).toBe(false);
+ });
+
+ test("should return false when response does not contain any open text question IDs", () => {
+ const openTextQuestionIds = ["q1", "q2", "q3"];
+ const response = {
+ q4: "This is not an open text answer",
+ q5: "Another answer",
+ };
+
+ const result = doesResponseHasAnyOpenTextAnswer(openTextQuestionIds, response);
+
+ expect(result).toBe(false);
+ });
+
+ test("should return false for non-string answers", () => {
+ const openTextQuestionIds = ["q1", "q2", "q3"];
+ const response = {
+ q1: "",
+ q2: 123,
+ q3: true,
+ } as any; // Use type assertion to handle mixed types in the test
+
+ const result = doesResponseHasAnyOpenTextAnswer(openTextQuestionIds, response);
+
+ expect(result).toBe(false);
+ });
+ });
+});
diff --git a/apps/web/app/api/(internal)/insights/lib/utils.ts b/apps/web/app/api/(internal)/insights/lib/utils.ts
index 53faeb9e0c..c8feaf1ab2 100644
--- a/apps/web/app/api/(internal)/insights/lib/utils.ts
+++ b/apps/web/app/api/(internal)/insights/lib/utils.ts
@@ -11,6 +11,10 @@ import { TResponse } from "@formbricks/types/responses";
import { TSurvey } from "@formbricks/types/surveys/types";
export const generateInsightsForSurvey = (surveyId: string) => {
+ if (!CRON_SECRET) {
+ throw new Error("CRON_SECRET is not set");
+ }
+
try {
return fetch(`${WEBAPP_URL}/api/insights`, {
method: "POST",
diff --git a/apps/web/app/api/v1/client/[environmentId]/storage/local/route.ts b/apps/web/app/api/v1/client/[environmentId]/storage/local/route.ts
index d209325c67..36bbfd3bb8 100644
--- a/apps/web/app/api/v1/client/[environmentId]/storage/local/route.ts
+++ b/apps/web/app/api/v1/client/[environmentId]/storage/local/route.ts
@@ -31,6 +31,9 @@ export const OPTIONS = async (): Promise => {
};
export const POST = async (req: NextRequest, context: Context): Promise => {
+ if (!ENCRYPTION_KEY) {
+ return responses.internalServerErrorResponse("Encryption key is not set");
+ }
const params = await context.params;
const environmentId = params.environmentId;
diff --git a/apps/web/app/api/v1/management/storage/local/route.ts b/apps/web/app/api/v1/management/storage/local/route.ts
index f4e8c8f00d..4c1398903e 100644
--- a/apps/web/app/api/v1/management/storage/local/route.ts
+++ b/apps/web/app/api/v1/management/storage/local/route.ts
@@ -12,6 +12,10 @@ import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
import { putFileToLocalStorage } from "@formbricks/lib/storage/service";
export const POST = async (req: NextRequest): Promise => {
+ if (!ENCRYPTION_KEY) {
+ return responses.internalServerErrorResponse("Encryption key is not set");
+ }
+
const accessType = "public"; // public files are accessible by anyone
const headersList = await headers();
diff --git a/apps/web/app/api/v1/og/route.tsx b/apps/web/app/api/v1/og/route.tsx
index 4b79654d98..bc8b17d5b7 100644
--- a/apps/web/app/api/v1/og/route.tsx
+++ b/apps/web/app/api/v1/og/route.tsx
@@ -29,7 +29,6 @@ export const GET = async (req: NextRequest) => {
{name}
- Complete in ~ 4 minutes
diff --git a/apps/web/app/api/v1/webhooks/[webhookId]/lib/webhook.ts b/apps/web/app/api/v1/webhooks/[webhookId]/lib/webhook.ts
index 9f299ee840..4e7ffb9a47 100644
--- a/apps/web/app/api/v1/webhooks/[webhookId]/lib/webhook.ts
+++ b/apps/web/app/api/v1/webhooks/[webhookId]/lib/webhook.ts
@@ -1,6 +1,7 @@
import { webhookCache } from "@/lib/cache/webhook";
import { Prisma, Webhook } from "@prisma/client";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { cache } from "@formbricks/lib/cache";
import { validateInputs } from "@formbricks/lib/utils/validate";
import { ZId } from "@formbricks/types/common";
@@ -25,7 +26,10 @@ export const deleteWebhook = async (id: string): Promise
=> {
return deletedWebhook;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2025") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.RelatedRecordDoesNotExist
+ ) {
throw new ResourceNotFoundError("Webhook", id);
}
throw new DatabaseError(`Database error when deleting webhook with ID ${id}`);
diff --git a/apps/web/app/api/v2/management/webhooks/[webhookId]/route.ts b/apps/web/app/api/v2/management/webhooks/[webhookId]/route.ts
new file mode 100644
index 0000000000..6655f124cc
--- /dev/null
+++ b/apps/web/app/api/v2/management/webhooks/[webhookId]/route.ts
@@ -0,0 +1,3 @@
+import { DELETE, GET, PUT } from "@/modules/api/v2/management/webhooks/[webhookId]/route";
+
+export { GET, PUT, DELETE };
diff --git a/apps/web/app/api/v2/management/webhooks/route.ts b/apps/web/app/api/v2/management/webhooks/route.ts
new file mode 100644
index 0000000000..d6497c990c
--- /dev/null
+++ b/apps/web/app/api/v2/management/webhooks/route.ts
@@ -0,0 +1,3 @@
+import { GET, POST } from "@/modules/api/v2/management/webhooks/route";
+
+export { GET, POST };
diff --git a/apps/web/app/layout.test.tsx b/apps/web/app/layout.test.tsx
index 51abc5195b..495ec8f9ce 100644
--- a/apps/web/app/layout.test.tsx
+++ b/apps/web/app/layout.test.tsx
@@ -29,6 +29,7 @@ vi.mock("@formbricks/lib/constants", () => ({
OIDC_SIGNING_ALGORITHM: "test-oidc-signing-algorithm",
WEBAPP_URL: "test-webapp-url",
IS_PRODUCTION: false,
+ SENTRY_DSN: "mock-sentry-dsn",
}));
vi.mock("@/tolgee/language", () => ({
@@ -69,6 +70,15 @@ vi.mock("@/tolgee/client", () => ({
),
}));
+vi.mock("@/app/sentry/SentryProvider", () => ({
+ SentryProvider: ({ children, sentryDsn }: { children: React.ReactNode; sentryDsn?: string }) => (
+
+ SentryProvider: {sentryDsn}
+ {children}
+
+ ),
+}));
+
describe("RootLayout", () => {
beforeEach(() => {
cleanup();
@@ -97,6 +107,7 @@ describe("RootLayout", () => {
expect(screen.getByTestId("speed-insights")).toBeInTheDocument();
expect(screen.getByTestId("ph-provider")).toBeInTheDocument();
expect(screen.getByTestId("tolgee-next-provider")).toBeInTheDocument();
+ expect(screen.getByTestId("sentry-provider")).toBeInTheDocument();
expect(screen.getByTestId("child")).toHaveTextContent("Child Content");
});
});
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
index ede4738cd7..bc235ff730 100644
--- a/apps/web/app/layout.tsx
+++ b/apps/web/app/layout.tsx
@@ -1,3 +1,4 @@
+import { SentryProvider } from "@/app/sentry/SentryProvider";
import { PHProvider } from "@/modules/ui/components/post-hog-client";
import { TolgeeNextProvider } from "@/tolgee/client";
import { getLocale } from "@/tolgee/language";
@@ -6,7 +7,7 @@ import { TolgeeStaticData } from "@tolgee/react";
import { SpeedInsights } from "@vercel/speed-insights/next";
import { Metadata } from "next";
import React from "react";
-import { IS_POSTHOG_CONFIGURED } from "@formbricks/lib/constants";
+import { IS_POSTHOG_CONFIGURED, SENTRY_DSN } from "@formbricks/lib/constants";
import "../modules/ui/globals.css";
export const metadata: Metadata = {
@@ -27,11 +28,13 @@ const RootLayout = async ({ children }: { children: React.ReactNode }) => {
{process.env.VERCEL === "1" && }
-
-
- {children}
-
-
+
+
+
+ {children}
+
+
+
);
diff --git a/apps/web/app/lib/pipelines.test.ts b/apps/web/app/lib/pipelines.test.ts
new file mode 100644
index 0000000000..306a4260d5
--- /dev/null
+++ b/apps/web/app/lib/pipelines.test.ts
@@ -0,0 +1,113 @@
+import { TPipelineInput } from "@/app/lib/types/pipelines";
+import { PipelineTriggers } from "@prisma/client";
+import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
+import { logger } from "@formbricks/logger";
+import { TResponse } from "@formbricks/types/responses";
+import { sendToPipeline } from "./pipelines";
+
+// Mock the constants module
+vi.mock("@formbricks/lib/constants", () => ({
+ CRON_SECRET: "mocked-cron-secret",
+ WEBAPP_URL: "https://test.formbricks.com",
+}));
+
+// Mock the logger
+vi.mock("@formbricks/logger", () => ({
+ logger: {
+ error: vi.fn(),
+ },
+}));
+
+// Mock global fetch
+const mockFetch = vi.fn();
+global.fetch = mockFetch;
+
+describe("pipelines", () => {
+ // Reset mocks before each test
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ // Clean up after each test
+ afterEach(() => {
+ vi.clearAllMocks();
+ });
+
+ test("sendToPipeline should call fetch with correct parameters", async () => {
+ // Mock the fetch implementation to return a successful response
+ mockFetch.mockResolvedValueOnce({
+ ok: true,
+ json: async () => ({ success: true }),
+ });
+
+ // Create sample data for testing
+ const testData: TPipelineInput = {
+ event: PipelineTriggers.responseCreated,
+ surveyId: "cm8ckvchx000008lb710n0gdn",
+ environmentId: "cm8cmp9hp000008jf7l570ml2",
+ response: { id: "cm8cmpnjj000108jfdr9dfqe6" } as TResponse,
+ };
+
+ // Call the function with test data
+ await sendToPipeline(testData);
+
+ // Check that fetch was called with the correct arguments
+ expect(mockFetch).toHaveBeenCalledTimes(1);
+ expect(mockFetch).toHaveBeenCalledWith("https://test.formbricks.com/api/pipeline", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "x-api-key": "mocked-cron-secret",
+ },
+ body: JSON.stringify({
+ environmentId: testData.environmentId,
+ surveyId: testData.surveyId,
+ event: testData.event,
+ response: testData.response,
+ }),
+ });
+ });
+
+ test("sendToPipeline should handle fetch errors", async () => {
+ // Mock fetch to throw an error
+ const testError = new Error("Network error");
+ mockFetch.mockRejectedValueOnce(testError);
+
+ // Create sample data for testing
+ const testData: TPipelineInput = {
+ event: PipelineTriggers.responseCreated,
+ surveyId: "cm8ckvchx000008lb710n0gdn",
+ environmentId: "cm8cmp9hp000008jf7l570ml2",
+ response: { id: "cm8cmpnjj000108jfdr9dfqe6" } as TResponse,
+ };
+
+ // Call the function
+ await sendToPipeline(testData);
+
+ // Check that the error was logged using logger
+ expect(logger.error).toHaveBeenCalledWith(testError, "Error sending event to pipeline");
+ });
+
+ test("sendToPipeline should throw error if CRON_SECRET is not set", async () => {
+ // For this test, we need to mock CRON_SECRET as undefined
+ // Let's use a more compatible approach to reset the mocks
+ const originalModule = await import("@formbricks/lib/constants");
+ const mockConstants = { ...originalModule, CRON_SECRET: undefined };
+
+ vi.doMock("@formbricks/lib/constants", () => mockConstants);
+
+ // Re-import the module to get the new mocked values
+ const { sendToPipeline: sendToPipelineNoSecret } = await import("./pipelines");
+
+ // Create sample data for testing
+ const testData: TPipelineInput = {
+ event: PipelineTriggers.responseCreated,
+ surveyId: "cm8ckvchx000008lb710n0gdn",
+ environmentId: "cm8cmp9hp000008jf7l570ml2",
+ response: { id: "cm8cmpnjj000108jfdr9dfqe6" } as TResponse,
+ };
+
+ // Expect the function to throw an error
+ await expect(sendToPipelineNoSecret(testData)).rejects.toThrow("CRON_SECRET is not set");
+ });
+});
diff --git a/apps/web/app/lib/pipelines.ts b/apps/web/app/lib/pipelines.ts
index 686ece3f51..d1f040efa2 100644
--- a/apps/web/app/lib/pipelines.ts
+++ b/apps/web/app/lib/pipelines.ts
@@ -3,6 +3,10 @@ import { CRON_SECRET, WEBAPP_URL } from "@formbricks/lib/constants";
import { logger } from "@formbricks/logger";
export const sendToPipeline = async ({ event, surveyId, environmentId, response }: TPipelineInput) => {
+ if (!CRON_SECRET) {
+ throw new Error("CRON_SECRET is not set");
+ }
+
return fetch(`${WEBAPP_URL}/api/pipeline`, {
method: "POST",
headers: {
diff --git a/apps/web/app/lib/singleUseSurveys.test.ts b/apps/web/app/lib/singleUseSurveys.test.ts
new file mode 100644
index 0000000000..c941c135d4
--- /dev/null
+++ b/apps/web/app/lib/singleUseSurveys.test.ts
@@ -0,0 +1,120 @@
+import cuid2 from "@paralleldrive/cuid2";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import * as crypto from "@formbricks/lib/crypto";
+import { generateSurveySingleUseId, validateSurveySingleUseId } from "./singleUseSurveys";
+
+// Mock the crypto module
+vi.mock("@formbricks/lib/crypto", () => ({
+ symmetricEncrypt: vi.fn(),
+ symmetricDecrypt: vi.fn(),
+ decryptAES128: vi.fn(),
+}));
+
+// Mock constants
+vi.mock("@formbricks/lib/constants", () => ({
+ ENCRYPTION_KEY: "test-encryption-key",
+ FORMBRICKS_ENCRYPTION_KEY: "test-formbricks-encryption-key",
+}));
+
+// Mock cuid2
+vi.mock("@paralleldrive/cuid2", () => {
+ const createIdMock = vi.fn();
+ const isCuidMock = vi.fn();
+
+ return {
+ default: {
+ createId: createIdMock,
+ isCuid: isCuidMock,
+ },
+ createId: createIdMock,
+ isCuid: isCuidMock,
+ };
+});
+
+describe("generateSurveySingleUseId", () => {
+ const mockCuid = "test-cuid-123";
+ const mockEncryptedCuid = "encrypted-cuid-123";
+
+ beforeEach(() => {
+ // Setup mocks
+ vi.mocked(cuid2.createId).mockReturnValue(mockCuid);
+ vi.mocked(crypto.symmetricEncrypt).mockReturnValue(mockEncryptedCuid);
+ });
+
+ afterEach(() => {
+ vi.resetAllMocks();
+ });
+
+ it("returns unencrypted cuid when isEncrypted is false", () => {
+ const result = generateSurveySingleUseId(false);
+
+ expect(result).toBe(mockCuid);
+ expect(crypto.symmetricEncrypt).not.toHaveBeenCalled();
+ });
+
+ it("returns encrypted cuid when isEncrypted is true", () => {
+ const result = generateSurveySingleUseId(true);
+
+ expect(result).toBe(mockEncryptedCuid);
+ expect(crypto.symmetricEncrypt).toHaveBeenCalledWith(mockCuid, "test-encryption-key");
+ });
+
+ it("returns undefined when cuid is not valid", () => {
+ vi.mocked(cuid2.isCuid).mockReturnValue(false);
+
+ const result = validateSurveySingleUseId(mockEncryptedCuid);
+
+ expect(result).toBeUndefined();
+ });
+
+ it("returns undefined when decryption fails", () => {
+ vi.mocked(crypto.symmetricDecrypt).mockImplementation(() => {
+ throw new Error("Decryption failed");
+ });
+
+ const result = validateSurveySingleUseId(mockEncryptedCuid);
+
+ expect(result).toBeUndefined();
+ });
+
+ it("throws error when ENCRYPTION_KEY is not set in generateSurveySingleUseId", async () => {
+ // Temporarily mock ENCRYPTION_KEY as undefined
+ vi.doMock("@formbricks/lib/constants", () => ({
+ ENCRYPTION_KEY: undefined,
+ FORMBRICKS_ENCRYPTION_KEY: "test-formbricks-encryption-key",
+ }));
+
+ // Re-import to get the new mock values
+ const { generateSurveySingleUseId: generateSurveySingleUseIdNoKey } = await import("./singleUseSurveys");
+
+ expect(() => generateSurveySingleUseIdNoKey(true)).toThrow("ENCRYPTION_KEY is not set");
+ });
+
+ it("throws error when ENCRYPTION_KEY is not set in validateSurveySingleUseId for symmetric encryption", async () => {
+ // Temporarily mock ENCRYPTION_KEY as undefined
+ vi.doMock("@formbricks/lib/constants", () => ({
+ ENCRYPTION_KEY: undefined,
+ FORMBRICKS_ENCRYPTION_KEY: "test-formbricks-encryption-key",
+ }));
+
+ // Re-import to get the new mock values
+ const { validateSurveySingleUseId: validateSurveySingleUseIdNoKey } = await import("./singleUseSurveys");
+
+ expect(() => validateSurveySingleUseIdNoKey(mockEncryptedCuid)).toThrow("ENCRYPTION_KEY is not set");
+ });
+
+ it("throws error when FORMBRICKS_ENCRYPTION_KEY is not set in validateSurveySingleUseId for AES128", async () => {
+ // Temporarily mock FORMBRICKS_ENCRYPTION_KEY as undefined
+ vi.doMock("@formbricks/lib/constants", () => ({
+ ENCRYPTION_KEY: "test-encryption-key",
+ FORMBRICKS_ENCRYPTION_KEY: undefined,
+ }));
+
+ // Re-import to get the new mock values
+ const { validateSurveySingleUseId: validateSurveySingleUseIdNoKey } = await import("./singleUseSurveys");
+
+ expect(() =>
+ validateSurveySingleUseIdNoKey("M(.Bob=dS1!wUSH2lb,E7hxO=He1cnnitmXrG|Su/DKYZrPy~zgS)u?dgI53sfs/")
+ ).toThrow("FORMBRICKS_ENCRYPTION_KEY is not defined");
+ });
+});
diff --git a/apps/web/app/lib/singleUseSurveys.ts b/apps/web/app/lib/singleUseSurveys.ts
index 33318b6567..aaceacd6d9 100644
--- a/apps/web/app/lib/singleUseSurveys.ts
+++ b/apps/web/app/lib/singleUseSurveys.ts
@@ -9,31 +9,42 @@ export const generateSurveySingleUseId = (isEncrypted: boolean): string => {
return cuid;
}
+ if (!ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
+
const encryptedCuid = symmetricEncrypt(cuid, ENCRYPTION_KEY);
return encryptedCuid;
};
// validate the survey single use id
export const validateSurveySingleUseId = (surveySingleUseId: string): string | undefined => {
- try {
- let decryptedCuid: string | null = null;
+ let decryptedCuid: string | null = null;
- if (surveySingleUseId.length === 64) {
- if (!FORMBRICKS_ENCRYPTION_KEY) {
- throw new Error("FORMBRICKS_ENCRYPTION_KEY is not defined");
- }
-
- decryptedCuid = decryptAES128(FORMBRICKS_ENCRYPTION_KEY!, surveySingleUseId);
- } else {
- decryptedCuid = symmetricDecrypt(surveySingleUseId, ENCRYPTION_KEY);
+ if (surveySingleUseId.length === 64) {
+ if (!FORMBRICKS_ENCRYPTION_KEY) {
+ throw new Error("FORMBRICKS_ENCRYPTION_KEY is not defined");
}
- if (cuid2.isCuid(decryptedCuid)) {
- return decryptedCuid;
- } else {
+ try {
+ decryptedCuid = decryptAES128(FORMBRICKS_ENCRYPTION_KEY, surveySingleUseId);
+ } catch (error) {
return undefined;
}
- } catch (error) {
+ } else {
+ if (!ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
+ try {
+ decryptedCuid = symmetricDecrypt(surveySingleUseId, ENCRYPTION_KEY);
+ } catch (error) {
+ return undefined;
+ }
+ }
+
+ if (cuid2.isCuid(decryptedCuid)) {
+ return decryptedCuid;
+ } else {
return undefined;
}
};
diff --git a/apps/web/app/sentry/SentryProvider.test.tsx b/apps/web/app/sentry/SentryProvider.test.tsx
new file mode 100644
index 0000000000..40b58e7165
--- /dev/null
+++ b/apps/web/app/sentry/SentryProvider.test.tsx
@@ -0,0 +1,101 @@
+import * as Sentry from "@sentry/nextjs";
+import { cleanup, render, screen } from "@testing-library/react";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { SentryProvider } from "./SentryProvider";
+
+vi.mock("@sentry/nextjs", async () => {
+ const actual = await vi.importActual("@sentry/nextjs");
+ return {
+ ...actual,
+ replayIntegration: (options: any) => {
+ return {
+ name: "Replay",
+ id: "Replay",
+ options,
+ };
+ },
+ };
+});
+
+describe("SentryProvider", () => {
+ afterEach(() => {
+ cleanup();
+ });
+
+ it("calls Sentry.init when sentryDsn is provided", () => {
+ const sentryDsn = "https://examplePublicKey@o0.ingest.sentry.io/0";
+ const initSpy = vi.spyOn(Sentry, "init").mockImplementation(() => undefined);
+
+ render(
+
+ Test Content
+
+ );
+
+ // The useEffect runs after mount, so Sentry.init should have been called.
+ expect(initSpy).toHaveBeenCalled();
+ expect(initSpy).toHaveBeenCalledWith(
+ expect.objectContaining({
+ dsn: sentryDsn,
+ tracesSampleRate: 1,
+ debug: false,
+ replaysOnErrorSampleRate: 1.0,
+ replaysSessionSampleRate: 0.1,
+ integrations: expect.any(Array),
+ beforeSend: expect.any(Function),
+ })
+ );
+ });
+
+ it("does not call Sentry.init when sentryDsn is not provided", () => {
+ const initSpy = vi.spyOn(Sentry, "init").mockImplementation(() => undefined);
+
+ render(
+
+ Test Content
+
+ );
+
+ expect(initSpy).not.toHaveBeenCalled();
+ });
+
+ it("renders children", () => {
+ const sentryDsn = "https://examplePublicKey@o0.ingest.sentry.io/0";
+ render(
+
+ Test Content
+
+ );
+ expect(screen.getByTestId("child")).toHaveTextContent("Test Content");
+ });
+
+ it("processes beforeSend correctly", () => {
+ const sentryDsn = "https://examplePublicKey@o0.ingest.sentry.io/0";
+ const initSpy = vi.spyOn(Sentry, "init").mockImplementation(() => undefined);
+
+ render(
+
+ Test Content
+
+ );
+
+ const config = initSpy.mock.calls[0][0];
+ expect(config).toHaveProperty("beforeSend");
+ const beforeSend = config.beforeSend;
+
+ if (!beforeSend) {
+ throw new Error("beforeSend is not defined");
+ }
+
+ const dummyEvent = { some: "event" } as unknown as Sentry.ErrorEvent;
+
+ const hintWithNextNotFound = { originalException: { digest: "NEXT_NOT_FOUND" } };
+ expect(beforeSend(dummyEvent, hintWithNextNotFound)).toBeNull();
+
+ const hintWithOtherError = { originalException: { digest: "OTHER_ERROR" } };
+ expect(beforeSend(dummyEvent, hintWithOtherError)).toEqual(dummyEvent);
+
+ const hintWithoutError = { originalException: undefined };
+ expect(beforeSend(dummyEvent, hintWithoutError)).toEqual(dummyEvent);
+ });
+});
diff --git a/apps/web/app/sentry/SentryProvider.tsx b/apps/web/app/sentry/SentryProvider.tsx
new file mode 100644
index 0000000000..b01e71dfc4
--- /dev/null
+++ b/apps/web/app/sentry/SentryProvider.tsx
@@ -0,0 +1,53 @@
+"use client";
+
+import * as Sentry from "@sentry/nextjs";
+import { useEffect } from "react";
+
+interface SentryProviderProps {
+ children: React.ReactNode;
+ sentryDsn?: string;
+}
+
+export const SentryProvider = ({ children, sentryDsn }: SentryProviderProps) => {
+ useEffect(() => {
+ if (sentryDsn) {
+ Sentry.init({
+ dsn: sentryDsn,
+
+ // Adjust this value in production, or use tracesSampler for greater control
+ tracesSampleRate: 1,
+
+ // Setting this option to true will print useful information to the console while you're setting up Sentry.
+ debug: false,
+
+ replaysOnErrorSampleRate: 1.0,
+
+ // This sets the sample rate to be 10%. You may want this to be 100% while
+ // in development and sample at a lower rate in production
+ replaysSessionSampleRate: 0.1,
+
+ // You can remove this option if you're not planning to use the Sentry Session Replay feature:
+ integrations: [
+ Sentry.replayIntegration({
+ // Additional Replay configuration goes in here, for example:
+ maskAllText: true,
+ blockAllMedia: true,
+ }),
+ ],
+
+ beforeSend(event, hint) {
+ const error = hint.originalException as Error;
+
+ // @ts-expect-error
+ if (error && error.digest === "NEXT_NOT_FOUND") {
+ return null;
+ }
+
+ return event;
+ },
+ });
+ }
+ }, []);
+
+ return <>{children}>;
+};
diff --git a/apps/web/cache-handler.mjs b/apps/web/cache-handler.mjs
index 5292d88e97..1065fa3b83 100644
--- a/apps/web/cache-handler.mjs
+++ b/apps/web/cache-handler.mjs
@@ -57,8 +57,6 @@ CacheHandler.onCreation(async () => {
timeoutMs: 1000,
};
- redisHandlerOptions.ttl = Number(process.env.REDIS_DEFAULT_TTL) || 86400; // 1 day
-
// Create the `redis-stack` Handler if the client is available and connected.
handler = await createRedisHandler(redisHandlerOptions);
} else {
@@ -70,6 +68,11 @@ CacheHandler.onCreation(async () => {
return {
handlers: [handler],
+ ttl: {
+ // We set the stale and the expire age to the same value, because the stale age is determined by the unstable_cache revalidation.
+ defaultStaleAge: (process.env.REDIS_URL && Number(process.env.REDIS_DEFAULT_TTL)) || 86400,
+ estimateExpireAge: (staleAge) => staleAge,
+ },
};
});
diff --git a/apps/web/instrumentation.ts b/apps/web/instrumentation.ts
index 0b527429c2..e86284efd3 100644
--- a/apps/web/instrumentation.ts
+++ b/apps/web/instrumentation.ts
@@ -1,8 +1,14 @@
-import { env } from "@formbricks/lib/env";
+import { PROMETHEUS_ENABLED, SENTRY_DSN } from "@formbricks/lib/constants";
// instrumentation.ts
export const register = async () => {
- if (process.env.NEXT_RUNTIME === "nodejs" && env.PROMETHEUS_ENABLED) {
+ if (process.env.NEXT_RUNTIME === "nodejs" && PROMETHEUS_ENABLED) {
await import("./instrumentation-node");
}
+ if (process.env.NEXT_RUNTIME === "nodejs" && SENTRY_DSN) {
+ await import("./sentry.server.config");
+ }
+ if (process.env.NEXT_RUNTIME === "edge" && SENTRY_DSN) {
+ await import("./sentry.edge.config");
+ }
};
diff --git a/apps/web/modules/api/v2/management/auth/authenticate-request.ts b/apps/web/modules/api/v2/management/auth/authenticate-request.ts
index 7e6a1cacde..f0ec9e3165 100644
--- a/apps/web/modules/api/v2/management/auth/authenticate-request.ts
+++ b/apps/web/modules/api/v2/management/auth/authenticate-request.ts
@@ -8,6 +8,7 @@ export const authenticateRequest = async (
request: Request
): Promise> => {
const apiKey = request.headers.get("x-api-key");
+
if (apiKey) {
const environmentIdResult = await getEnvironmentIdFromApiKey(apiKey);
if (!environmentIdResult.ok) {
diff --git a/apps/web/modules/api/v2/management/lib/helper.ts b/apps/web/modules/api/v2/management/lib/helper.ts
index 0b5d07e406..0e86d002e1 100644
--- a/apps/web/modules/api/v2/management/lib/helper.ts
+++ b/apps/web/modules/api/v2/management/lib/helper.ts
@@ -1,4 +1,7 @@
-import { fetchEnvironmentId } from "@/modules/api/v2/management/lib/services";
+import {
+ fetchEnvironmentId,
+ fetchEnvironmentIdFromSurveyIds,
+} from "@/modules/api/v2/management/lib/services";
import { ApiErrorResponseV2 } from "@/modules/api/v2/types/api-error";
import { Result, ok } from "@formbricks/types/error-handlers";
@@ -14,3 +17,31 @@ export const getEnvironmentId = async (
return ok(result.data.environmentId);
};
+
+/**
+ * Validates that all surveys are in the same environment and return the environment id
+ * @param surveyIds array of survey ids from the same environment
+ * @returns the common environment id
+ */
+export const getEnvironmentIdFromSurveyIds = async (
+ surveyIds: string[]
+): Promise> => {
+ const result = await fetchEnvironmentIdFromSurveyIds(surveyIds);
+
+ if (!result.ok) {
+ return result;
+ }
+
+ // Check if all items in the array are the same
+ if (new Set(result.data).size !== 1) {
+ return {
+ ok: false,
+ error: {
+ type: "bad_request",
+ details: [{ field: "surveyIds", issue: "not all surveys are in the same environment" }],
+ },
+ };
+ }
+
+ return ok(result.data[0]);
+};
diff --git a/apps/web/modules/api/v2/management/lib/openapi.ts b/apps/web/modules/api/v2/management/lib/openapi.ts
deleted file mode 100644
index f268bb2516..0000000000
--- a/apps/web/modules/api/v2/management/lib/openapi.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import {
- deleteResponseEndpoint,
- getResponseEndpoint,
- updateResponseEndpoint,
-} from "@/modules/api/v2/management/responses/[responseId]/lib/openapi";
-import {
- createResponseEndpoint,
- getResponsesEndpoint,
-} from "@/modules/api/v2/management/responses/lib/openapi";
-import { ZodOpenApiPathsObject } from "zod-openapi";
-
-export const responsePaths: ZodOpenApiPathsObject = {
- "/responses": {
- get: getResponsesEndpoint,
- post: createResponseEndpoint,
- },
- "/responses/{id}": {
- get: getResponseEndpoint,
- put: updateResponseEndpoint,
- delete: deleteResponseEndpoint,
- },
-};
diff --git a/apps/web/modules/api/v2/management/lib/services.ts b/apps/web/modules/api/v2/management/lib/services.ts
index 1d1a769104..9420165725 100644
--- a/apps/web/modules/api/v2/management/lib/services.ts
+++ b/apps/web/modules/api/v2/management/lib/services.ts
@@ -41,3 +41,36 @@ export const fetchEnvironmentId = reactCache(async (id: string, isResponseId: bo
}
)()
);
+
+export const fetchEnvironmentIdFromSurveyIds = reactCache(async (surveyIds: string[]) =>
+ cache(
+ async (): Promise> => {
+ try {
+ const results = await prisma.survey.findMany({
+ where: { id: { in: surveyIds } },
+ select: {
+ environmentId: true,
+ },
+ });
+
+ if (results.length !== surveyIds.length) {
+ return err({
+ type: "not_found",
+ details: [{ field: "survey", issue: "not found" }],
+ });
+ }
+
+ return ok(results.map((result) => result.environmentId));
+ } catch (error) {
+ return err({
+ type: "internal_server_error",
+ details: [{ field: "survey", issue: error.message }],
+ });
+ }
+ },
+ [`services-fetchEnvironmentIdFromSurveyIds-${surveyIds.join("-")}`],
+ {
+ tags: surveyIds.map((surveyId) => surveyCache.tag.byId(surveyId)),
+ }
+ )()
+);
diff --git a/apps/web/modules/api/v2/management/lib/tests/helper.test.ts b/apps/web/modules/api/v2/management/lib/tests/helper.test.ts
index 5b76f2360b..845c61cd15 100644
--- a/apps/web/modules/api/v2/management/lib/tests/helper.test.ts
+++ b/apps/web/modules/api/v2/management/lib/tests/helper.test.ts
@@ -1,14 +1,17 @@
+import { fetchEnvironmentIdFromSurveyIds } from "@/modules/api/v2/management/lib/services";
import { ApiErrorResponseV2 } from "@/modules/api/v2/types/api-error";
+import { createId } from "@paralleldrive/cuid2";
import { describe, expect, it, vi } from "vitest";
import { err, ok } from "@formbricks/types/error-handlers";
-import { getEnvironmentId } from "../helper";
+import { getEnvironmentId, getEnvironmentIdFromSurveyIds } from "../helper";
import { fetchEnvironmentId } from "../services";
vi.mock("../services", () => ({
fetchEnvironmentId: vi.fn(),
+ fetchEnvironmentIdFromSurveyIds: vi.fn(),
}));
-describe("Helper Functions", () => {
+describe("Tests for getEnvironmentId", () => {
it("should return environmentId for surveyId", async () => {
vi.mocked(fetchEnvironmentId).mockResolvedValue(ok({ environmentId: "env-id" }));
@@ -41,3 +44,42 @@ describe("Helper Functions", () => {
}
});
});
+
+describe("getEnvironmentIdFromSurveyIds", () => {
+ const envId1 = createId();
+ const envId2 = createId();
+
+ it("returns the common environment id when all survey ids are in the same environment", async () => {
+ vi.mocked(fetchEnvironmentIdFromSurveyIds).mockResolvedValueOnce({
+ ok: true,
+ data: [envId1, envId1],
+ });
+ const result = await getEnvironmentIdFromSurveyIds(["survey1", "survey2"]);
+ expect(result).toEqual(ok(envId1));
+ });
+
+ it("returns error when surveys are not in the same environment", async () => {
+ vi.mocked(fetchEnvironmentIdFromSurveyIds).mockResolvedValueOnce({
+ ok: true,
+ data: [envId1, envId2],
+ });
+ const result = await getEnvironmentIdFromSurveyIds(["survey1", "survey2"]);
+ expect(result.ok).toBe(false);
+ if (!result.ok) {
+ expect(result.error).toEqual({
+ type: "bad_request",
+ details: [{ field: "surveyIds", issue: "not all surveys are in the same environment" }],
+ });
+ }
+ });
+
+ it("returns error when API call fails", async () => {
+ const apiError = {
+ type: "server_error",
+ details: [{ field: "api", issue: "failed" }],
+ } as unknown as ApiErrorResponseV2;
+ vi.mocked(fetchEnvironmentIdFromSurveyIds).mockResolvedValueOnce({ ok: false, error: apiError });
+ const result = await getEnvironmentIdFromSurveyIds(["survey1", "survey2"]);
+ expect(result).toEqual({ ok: false, error: apiError });
+ });
+});
diff --git a/apps/web/modules/api/v2/management/lib/tests/services.test.ts b/apps/web/modules/api/v2/management/lib/tests/services.test.ts
index 9e22295f7a..02af5f1406 100644
--- a/apps/web/modules/api/v2/management/lib/tests/services.test.ts
+++ b/apps/web/modules/api/v2/management/lib/tests/services.test.ts
@@ -1,18 +1,17 @@
-import { beforeEach, describe, expect, test, vi } from "vitest";
+import { describe, expect, test, vi } from "vitest";
import { prisma } from "@formbricks/database";
-import { fetchEnvironmentId } from "../services";
+import { fetchEnvironmentId, fetchEnvironmentIdFromSurveyIds } from "../services";
vi.mock("@formbricks/database", () => ({
prisma: {
- survey: { findFirst: vi.fn() },
+ survey: {
+ findFirst: vi.fn(),
+ findMany: vi.fn(),
+ },
},
}));
describe("Services", () => {
- beforeEach(() => {
- vi.clearAllMocks();
- });
-
describe("getSurveyAndEnvironmentId", () => {
test("should return surveyId and environmentId for responseId", async () => {
vi.mocked(prisma.survey.findFirst).mockResolvedValue({
@@ -80,4 +79,36 @@ describe("Services", () => {
}
});
});
+
+ describe("fetchEnvironmentIdFromSurveyIds", () => {
+ test("should return an array of environmentIds if all surveys exist", async () => {
+ vi.mocked(prisma.survey.findMany).mockResolvedValue([
+ { environmentId: "env-1" },
+ { environmentId: "env-2" },
+ ]);
+ const result = await fetchEnvironmentIdFromSurveyIds(["survey1", "survey2"]);
+ expect(result.ok).toBe(true);
+ if (result.ok) {
+ expect(result.data).toEqual(["env-1", "env-2"]);
+ }
+ });
+
+ test("should return not_found error if any survey is missing", async () => {
+ vi.mocked(prisma.survey.findMany).mockResolvedValue([{ environmentId: "env-1" }]);
+ const result = await fetchEnvironmentIdFromSurveyIds(["survey1", "survey2"]);
+ expect(result.ok).toBe(false);
+ if (!result.ok) {
+ expect(result.error.type).toBe("not_found");
+ }
+ });
+
+ test("should return internal_server_error if prisma query fails", async () => {
+ vi.mocked(prisma.survey.findMany).mockRejectedValue(new Error("Query failed"));
+ const result = await fetchEnvironmentIdFromSurveyIds(["survey1", "survey2"]);
+ expect(result.ok).toBe(false);
+ if (!result.ok) {
+ expect(result.error.type).toBe("internal_server_error");
+ }
+ });
+ });
});
diff --git a/apps/web/modules/api/v2/management/lib/tests/utils.test.ts b/apps/web/modules/api/v2/management/lib/tests/utils.test.ts
index a748f15451..f189e4f76a 100644
--- a/apps/web/modules/api/v2/management/lib/tests/utils.test.ts
+++ b/apps/web/modules/api/v2/management/lib/tests/utils.test.ts
@@ -1,5 +1,7 @@
+import { TGetFilter } from "@/modules/api/v2/types/api-filter";
+import { Prisma } from "@prisma/client";
import { describe, expect, test } from "vitest";
-import { hashApiKey } from "../utils";
+import { buildCommonFilterQuery, hashApiKey, pickCommonFilter } from "../utils";
describe("hashApiKey", () => {
test("generate the correct sha256 hash for a given input", () => {
@@ -15,3 +17,72 @@ describe("hashApiKey", () => {
expect(result).toHaveLength(9); // mocked on the vitestSetup.ts file;;
});
});
+
+describe("pickCommonFilter", () => {
+ test("picks the common filter fields correctly", () => {
+ const params = {
+ limit: 10,
+ skip: 5,
+ sortBy: "createdAt",
+ order: "asc",
+ startDate: new Date("2023-01-01"),
+ endDate: new Date("2023-12-31"),
+ } as TGetFilter;
+ const result = pickCommonFilter(params);
+ expect(result).toEqual(params);
+ });
+
+ test("handles missing fields gracefully", () => {
+ const params = { limit: 10 } as TGetFilter;
+ const result = pickCommonFilter(params);
+ expect(result).toEqual({
+ limit: 10,
+ skip: undefined,
+ sortBy: undefined,
+ order: undefined,
+ startDate: undefined,
+ endDate: undefined,
+ });
+ });
+
+ describe("buildCommonFilterQuery", () => {
+ test("applies startDate and endDate when provided", () => {
+ const query: Prisma.WebhookFindManyArgs = { where: {} };
+ const params = {
+ startDate: new Date("2023-01-01"),
+ endDate: new Date("2023-12-31"),
+ } as TGetFilter;
+ const result = buildCommonFilterQuery(query, params);
+ expect(result.where?.createdAt?.gte).toEqual(params.startDate);
+ expect(result.where?.createdAt?.lte).toEqual(params.endDate);
+ });
+
+ test("applies sortBy and order when provided", () => {
+ const query: Prisma.WebhookFindManyArgs = { where: {} };
+ const params = { sortBy: "createdAt", order: "desc" } as TGetFilter;
+ const result = buildCommonFilterQuery(query, params);
+ expect(result.orderBy).toEqual({ createdAt: "desc" });
+ });
+
+ test("applies limit (take) when provided", () => {
+ const query: Prisma.WebhookFindManyArgs = { where: {} };
+ const params = { limit: 5 } as TGetFilter;
+ const result = buildCommonFilterQuery(query, params);
+ expect(result.take).toBe(5);
+ });
+
+ test("applies skip when provided", () => {
+ const query: Prisma.WebhookFindManyArgs = { where: {} };
+ const params = { skip: 10 } as TGetFilter;
+ const result = buildCommonFilterQuery(query, params);
+ expect(result.skip).toBe(10);
+ });
+
+ test("handles missing fields gracefully", () => {
+ const query = {};
+ const params = {} as TGetFilter;
+ const result = buildCommonFilterQuery(query, params);
+ expect(result).toEqual({});
+ });
+ });
+});
diff --git a/apps/web/modules/api/v2/management/lib/utils.ts b/apps/web/modules/api/v2/management/lib/utils.ts
index 0d8195da8a..3e601de2cc 100644
--- a/apps/web/modules/api/v2/management/lib/utils.ts
+++ b/apps/web/modules/api/v2/management/lib/utils.ts
@@ -1,3 +1,65 @@
+import { TGetFilter } from "@/modules/api/v2/types/api-filter";
+import { Prisma } from "@prisma/client";
import { createHash } from "crypto";
export const hashApiKey = (key: string): string => createHash("sha256").update(key).digest("hex");
+
+export function pickCommonFilter(params: T) {
+ const { limit, skip, sortBy, order, startDate, endDate } = params;
+ return { limit, skip, sortBy, order, startDate, endDate };
+}
+
+type HasFindMany = Prisma.WebhookFindManyArgs | Prisma.ResponseFindManyArgs;
+
+export function buildCommonFilterQuery(query: T, params: TGetFilter): T {
+ const { limit, skip, sortBy, order, startDate, endDate } = params || {};
+
+ let filteredQuery = {
+ ...query,
+ };
+
+ if (startDate) {
+ filteredQuery = {
+ ...filteredQuery,
+ where: {
+ ...filteredQuery.where,
+ createdAt: {
+ ...((filteredQuery.where?.createdAt as Prisma.DateTimeFilter) ?? {}),
+ gte: startDate,
+ },
+ },
+ };
+ }
+
+ if (endDate) {
+ filteredQuery = {
+ ...filteredQuery,
+ where: {
+ ...filteredQuery.where,
+ createdAt: {
+ ...((filteredQuery.where?.createdAt as Prisma.DateTimeFilter) ?? {}),
+ lte: endDate,
+ },
+ },
+ };
+ }
+
+ if (sortBy) {
+ filteredQuery = {
+ ...filteredQuery,
+ orderBy: {
+ [sortBy]: order,
+ },
+ };
+ }
+
+ if (limit) {
+ filteredQuery = { ...filteredQuery, take: limit };
+ }
+
+ if (skip) {
+ filteredQuery = { ...filteredQuery, skip };
+ }
+
+ return filteredQuery;
+}
diff --git a/apps/web/modules/api/v2/management/responses/[responseId]/lib/display.ts b/apps/web/modules/api/v2/management/responses/[responseId]/lib/display.ts
index a957d09e3b..b13245d343 100644
--- a/apps/web/modules/api/v2/management/responses/[responseId]/lib/display.ts
+++ b/apps/web/modules/api/v2/management/responses/[responseId]/lib/display.ts
@@ -1,6 +1,7 @@
import { ApiErrorResponseV2 } from "@/modules/api/v2/types/api-error";
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { displayCache } from "@formbricks/lib/display/cache";
import { Result, err, ok } from "@formbricks/types/error-handlers";
@@ -26,7 +27,10 @@ export const deleteDisplay = async (displayId: string): Promise ({
@@ -39,7 +40,7 @@ describe("Display Lib", () => {
test("return a not_found error when the display is not found", async () => {
vi.mocked(prisma.display.delete).mockRejectedValue(
new PrismaClientKnownRequestError("Display not found", {
- code: "P2025",
+ code: PrismaErrorType.RelatedRecordDoesNotExist,
clientVersion: "1.0.0",
meta: {
cause: "Display not found",
diff --git a/apps/web/modules/api/v2/management/responses/[responseId]/lib/tests/response.test.ts b/apps/web/modules/api/v2/management/responses/[responseId]/lib/tests/response.test.ts
index b4a5717337..edd9fb78d6 100644
--- a/apps/web/modules/api/v2/management/responses/[responseId]/lib/tests/response.test.ts
+++ b/apps/web/modules/api/v2/management/responses/[responseId]/lib/tests/response.test.ts
@@ -2,6 +2,7 @@ import { response, responseId, responseInput, survey } from "./__mocks__/respons
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
import { beforeEach, describe, expect, test, vi } from "vitest";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { ok, okVoid } from "@formbricks/types/error-handlers";
import { deleteDisplay } from "../display";
import { deleteResponse, getResponse, updateResponse } from "../response";
@@ -154,7 +155,7 @@ describe("Response Lib", () => {
test("handle prisma client error code P2025", async () => {
vi.mocked(prisma.response.delete).mockRejectedValue(
new PrismaClientKnownRequestError("Response not found", {
- code: "P2025",
+ code: PrismaErrorType.RelatedRecordDoesNotExist,
clientVersion: "1.0.0",
meta: {
cause: "Response not found",
@@ -192,7 +193,7 @@ describe("Response Lib", () => {
test("return a not_found error when the response is not found", async () => {
vi.mocked(prisma.response.update).mockRejectedValue(
new PrismaClientKnownRequestError("Response not found", {
- code: "P2025",
+ code: PrismaErrorType.RelatedRecordDoesNotExist,
clientVersion: "1.0.0",
meta: {
cause: "Response not found",
diff --git a/apps/web/modules/api/v2/management/responses/[responseId]/route.ts b/apps/web/modules/api/v2/management/responses/[responseId]/route.ts
index 08a01513aa..90443a5202 100644
--- a/apps/web/modules/api/v2/management/responses/[responseId]/route.ts
+++ b/apps/web/modules/api/v2/management/responses/[responseId]/route.ts
@@ -47,7 +47,7 @@ export const GET = async (request: Request, props: { params: Promise<{ responseI
return handleApiError(request, response.error);
}
- return responses.successResponse({ data: response.data });
+ return responses.successResponse(response);
},
});
@@ -88,7 +88,7 @@ export const DELETE = async (request: Request, props: { params: Promise<{ respon
return handleApiError(request, response.error);
}
- return responses.successResponse({ data: response.data });
+ return responses.successResponse(response);
},
});
@@ -130,6 +130,6 @@ export const PUT = (request: Request, props: { params: Promise<{ responseId: str
return handleApiError(request, response.error);
}
- return responses.successResponse({ data: response.data });
+ return responses.successResponse(response);
},
});
diff --git a/apps/web/modules/api/v2/management/responses/lib/openapi.ts b/apps/web/modules/api/v2/management/responses/lib/openapi.ts
index f562b1c3c6..e46da37627 100644
--- a/apps/web/modules/api/v2/management/responses/lib/openapi.ts
+++ b/apps/web/modules/api/v2/management/responses/lib/openapi.ts
@@ -3,10 +3,11 @@ import {
getResponseEndpoint,
updateResponseEndpoint,
} from "@/modules/api/v2/management/responses/[responseId]/lib/openapi";
-import { ZGetResponsesFilter } from "@/modules/api/v2/management/responses/types/responses";
+import { ZGetResponsesFilter, ZResponseInput } from "@/modules/api/v2/management/responses/types/responses";
+import { makePartialSchema, responseWithMetaSchema } from "@/modules/api/v2/types/openapi-response";
import { z } from "zod";
import { ZodOpenApiOperationObject, ZodOpenApiPathsObject } from "zod-openapi";
-import { ZResponse, ZResponseInput } from "@formbricks/types/responses";
+import { ZResponse } from "@formbricks/database/zod/responses";
export const getResponsesEndpoint: ZodOpenApiOperationObject = {
operationId: "getResponses",
@@ -21,7 +22,7 @@ export const getResponsesEndpoint: ZodOpenApiOperationObject = {
description: "Responses retrieved successfully.",
content: {
"application/json": {
- schema: z.array(ZResponse),
+ schema: z.array(responseWithMetaSchema(makePartialSchema(ZResponse))),
},
},
},
@@ -47,7 +48,7 @@ export const createResponseEndpoint: ZodOpenApiOperationObject = {
description: "Response created successfully.",
content: {
"application/json": {
- schema: ZResponse,
+ schema: makePartialSchema(ZResponse),
},
},
},
diff --git a/apps/web/modules/api/v2/management/responses/lib/organization.ts b/apps/web/modules/api/v2/management/responses/lib/organization.ts
index 9ca2a06cef..334f892e02 100644
--- a/apps/web/modules/api/v2/management/responses/lib/organization.ts
+++ b/apps/web/modules/api/v2/management/responses/lib/organization.ts
@@ -48,7 +48,7 @@ export const getOrganizationIdFromEnvironmentId = reactCache(async (environmentI
export const getOrganizationBilling = reactCache(async (organizationId: string) =>
cache(
- async (): Promise, ApiErrorResponseV2>> => {
+ async (): Promise> => {
try {
const organization = await prisma.organization.findFirst({
where: {
@@ -62,7 +62,8 @@ export const getOrganizationBilling = reactCache(async (organizationId: string)
if (!organization) {
return err({ type: "not_found", details: [{ field: "organization", issue: "not found" }] });
}
- return ok(organization);
+
+ return ok(organization.billing);
} catch (error) {
return err({
type: "internal_server_error",
@@ -126,26 +127,27 @@ export const getMonthlyOrganizationResponseCount = reactCache(async (organizatio
cache(
async (): Promise> => {
try {
- const organization = await getOrganizationBilling(organizationId);
- if (!organization.ok) {
- return err(organization.error);
+ const billing = await getOrganizationBilling(organizationId);
+ if (!billing.ok) {
+ return err(billing.error);
}
// Determine the start date based on the plan type
let startDate: Date;
- if (organization.data.billing.plan === "free") {
+
+ if (billing.data.plan === "free") {
// For free plans, use the first day of the current calendar month
const now = new Date();
startDate = new Date(now.getFullYear(), now.getMonth(), 1);
} else {
// For other plans, use the periodStart from billing
- if (!organization.data.billing.periodStart) {
+ if (!billing.data.periodStart) {
return err({
type: "internal_server_error",
details: [{ field: "organization", issue: "billing period start is not set" }],
});
}
- startDate = organization.data.billing.periodStart;
+ startDate = billing.data.periodStart;
}
// Get all environment IDs for the organization
diff --git a/apps/web/modules/api/v2/management/responses/lib/response.ts b/apps/web/modules/api/v2/management/responses/lib/response.ts
index f48eb413d8..6e0ce2516d 100644
--- a/apps/web/modules/api/v2/management/responses/lib/response.ts
+++ b/apps/web/modules/api/v2/management/responses/lib/response.ts
@@ -41,7 +41,14 @@ export const createResponse = async (
} = responseInput;
try {
- const ttc = initialTtc ? (finished ? calculateTtcTotal(initialTtc) : initialTtc) : {};
+ let ttc = {};
+ if (initialTtc) {
+ if (finished) {
+ ttc = calculateTtcTotal(initialTtc);
+ } else {
+ ttc = initialTtc;
+ }
+ }
const prismaData: Prisma.ResponseCreateInput = {
survey: {
@@ -67,11 +74,11 @@ export const createResponse = async (
return err(organizationIdResult.error);
}
- const organizationResult = await getOrganizationBilling(organizationIdResult.data);
- if (!organizationResult.ok) {
- return err(organizationResult.error);
+ const billing = await getOrganizationBilling(organizationIdResult.data);
+ if (!billing.ok) {
+ return err(billing.error);
}
- const organization = organizationResult.data;
+ const billingData = billing.data;
const response = await prisma.response.create({
data: prismaData,
@@ -95,12 +102,12 @@ export const createResponse = async (
}
const responsesCount = responsesCountResult.data;
- const responsesLimit = organization.billing.limits.monthly.responses;
+ const responsesLimit = billingData.limits?.monthly.responses;
if (responsesLimit && responsesCount >= responsesLimit) {
try {
await sendPlanLimitsReachedEventToPosthogWeekly(environmentId, {
- plan: organization.billing.plan,
+ plan: billingData.plan,
limits: {
projects: null,
monthly: {
diff --git a/apps/web/modules/api/v2/management/responses/lib/tests/organization.test.ts b/apps/web/modules/api/v2/management/responses/lib/tests/organization.test.ts
index 3dc84295d0..d908a5d1b4 100644
--- a/apps/web/modules/api/v2/management/responses/lib/tests/organization.test.ts
+++ b/apps/web/modules/api/v2/management/responses/lib/tests/organization.test.ts
@@ -85,7 +85,7 @@ describe("Organization Lib", () => {
});
expect(result.ok).toBe(true);
if (result.ok) {
- expect(result.data.billing).toEqual(organizationBilling);
+ expect(result.data).toEqual(organizationBilling);
}
});
diff --git a/apps/web/modules/api/v2/management/responses/lib/tests/response.test.ts b/apps/web/modules/api/v2/management/responses/lib/tests/response.test.ts
index d225af34a1..524749896c 100644
--- a/apps/web/modules/api/v2/management/responses/lib/tests/response.test.ts
+++ b/apps/web/modules/api/v2/management/responses/lib/tests/response.test.ts
@@ -55,7 +55,7 @@ describe("Response Lib", () => {
vi.mocked(prisma.response.create).mockResolvedValue(response);
vi.mocked(getOrganizationIdFromEnvironmentId).mockResolvedValue(ok(organizationId));
- vi.mocked(getOrganizationBilling).mockResolvedValue(ok({ billing: organizationBilling }));
+ vi.mocked(getOrganizationBilling).mockResolvedValue(ok(organizationBilling));
vi.mocked(getMonthlyOrganizationResponseCount).mockResolvedValue(ok(50));
const result = await createResponse(environmentId, responseInput);
@@ -70,7 +70,7 @@ describe("Response Lib", () => {
vi.mocked(prisma.response.create).mockResolvedValue(response);
vi.mocked(getOrganizationIdFromEnvironmentId).mockResolvedValue(ok(organizationId));
- vi.mocked(getOrganizationBilling).mockResolvedValue(ok({ billing: organizationBilling }));
+ vi.mocked(getOrganizationBilling).mockResolvedValue(ok(organizationBilling));
vi.mocked(getMonthlyOrganizationResponseCount).mockResolvedValue(ok(50));
const result = await createResponse(environmentId, responseInputNotFinished);
@@ -85,7 +85,7 @@ describe("Response Lib", () => {
vi.mocked(prisma.response.create).mockResolvedValue(response);
vi.mocked(getOrganizationIdFromEnvironmentId).mockResolvedValue(ok(organizationId));
- vi.mocked(getOrganizationBilling).mockResolvedValue(ok({ billing: organizationBilling }));
+ vi.mocked(getOrganizationBilling).mockResolvedValue(ok(organizationBilling));
vi.mocked(getMonthlyOrganizationResponseCount).mockResolvedValue(ok(50));
const result = await createResponse(environmentId, responseInputWithoutTtc);
@@ -100,7 +100,7 @@ describe("Response Lib", () => {
vi.mocked(prisma.response.create).mockResolvedValue(response);
vi.mocked(getOrganizationIdFromEnvironmentId).mockResolvedValue(ok(organizationId));
- vi.mocked(getOrganizationBilling).mockResolvedValue(ok({ billing: organizationBilling }));
+ vi.mocked(getOrganizationBilling).mockResolvedValue(ok(organizationBilling));
vi.mocked(getMonthlyOrganizationResponseCount).mockResolvedValue(ok(50));
const result = await createResponse(environmentId, responseInputWithoutDisplay);
@@ -145,7 +145,7 @@ describe("Response Lib", () => {
vi.mocked(getOrganizationIdFromEnvironmentId).mockResolvedValue(ok(organizationId));
- vi.mocked(getOrganizationBilling).mockResolvedValue(ok({ billing: organizationBilling }));
+ vi.mocked(getOrganizationBilling).mockResolvedValue(ok(organizationBilling));
vi.mocked(getMonthlyOrganizationResponseCount).mockResolvedValue(ok(100));
@@ -165,7 +165,7 @@ describe("Response Lib", () => {
vi.mocked(getOrganizationIdFromEnvironmentId).mockResolvedValue(ok(organizationId));
- vi.mocked(getOrganizationBilling).mockResolvedValue(ok({ billing: organizationBilling }));
+ vi.mocked(getOrganizationBilling).mockResolvedValue(ok(organizationBilling));
vi.mocked(getMonthlyOrganizationResponseCount).mockResolvedValue(
err({ type: "internal_server_error", details: [{ field: "organization", issue: "Aggregate error" }] })
@@ -186,7 +186,7 @@ describe("Response Lib", () => {
vi.mocked(getOrganizationIdFromEnvironmentId).mockResolvedValue(ok(organizationId));
- vi.mocked(getOrganizationBilling).mockResolvedValue(ok({ billing: organizationBilling }));
+ vi.mocked(getOrganizationBilling).mockResolvedValue(ok(organizationBilling));
vi.mocked(getMonthlyOrganizationResponseCount).mockResolvedValue(ok(100));
diff --git a/apps/web/modules/api/v2/management/responses/lib/tests/utils.test.ts b/apps/web/modules/api/v2/management/responses/lib/tests/utils.test.ts
index 088c955350..6ee8be7731 100644
--- a/apps/web/modules/api/v2/management/responses/lib/tests/utils.test.ts
+++ b/apps/web/modules/api/v2/management/responses/lib/tests/utils.test.ts
@@ -1,97 +1,40 @@
+import { buildCommonFilterQuery, pickCommonFilter } from "@/modules/api/v2/management/lib/utils";
import { TGetResponsesFilter } from "@/modules/api/v2/management/responses/types/responses";
-import { describe, expect, test } from "vitest";
+import { Prisma } from "@prisma/client";
+import { describe, expect, it, vi } from "vitest";
import { getResponsesQuery } from "../utils";
+vi.mock("@/modules/api/v2/management/lib/utils", () => ({
+ pickCommonFilter: vi.fn(),
+ buildCommonFilterQuery: vi.fn(),
+}));
+
describe("getResponsesQuery", () => {
- const environmentId = "env_1";
- const filters: TGetResponsesFilter = {
- limit: 10,
- skip: 0,
- sortBy: "createdAt",
- order: "asc",
- };
-
- test("return the base query when no params are provided", () => {
- const query = getResponsesQuery(environmentId);
- expect(query).toEqual({
- where: {
- survey: { environmentId },
- },
- });
+ it("adds surveyId to where clause if provided", () => {
+ const result = getResponsesQuery("env-id", { surveyId: "survey123" } as TGetResponsesFilter);
+ expect(result?.where?.surveyId).toBe("survey123");
});
- test("add surveyId to the query when provided", () => {
- const query = getResponsesQuery(environmentId, { ...filters, surveyId: "survey_1" });
- expect(query.where).toEqual({
- survey: { environmentId },
- surveyId: "survey_1",
- });
+ it("adds contactId to where clause if provided", () => {
+ const result = getResponsesQuery("env-id", { contactId: "contact123" } as TGetResponsesFilter);
+ expect(result?.where?.contactId).toBe("contact123");
});
- test("add startDate filter to the query", () => {
- const startDate = new Date("2023-01-01");
- const query = getResponsesQuery(environmentId, { ...filters, startDate });
- expect(query.where).toEqual({
- survey: { environmentId },
- createdAt: { gte: startDate },
- });
- });
+ it("calls pickCommonFilter & buildCommonFilterQuery with correct arguments", () => {
+ vi.mocked(pickCommonFilter).mockReturnValueOnce({ someFilter: true } as any);
+ vi.mocked(buildCommonFilterQuery).mockReturnValueOnce({ where: { combined: true } as any });
- test("add endDate filter to the query", () => {
- const endDate = new Date("2023-01-31");
- const query = getResponsesQuery(environmentId, { ...filters, endDate });
- expect(query.where).toEqual({
- survey: { environmentId },
- createdAt: { lte: endDate },
- });
- });
-
- test("add sortBy and order to the query", () => {
- const query = getResponsesQuery(environmentId, { ...filters, sortBy: "createdAt", order: "desc" });
- expect(query.orderBy).toEqual({
- createdAt: "desc",
- });
- });
-
- test("add limit (take) to the query", () => {
- const query = getResponsesQuery(environmentId, { ...filters, limit: 10 });
- expect(query.take).toBe(10);
- });
-
- test("add skip to the query", () => {
- const query = getResponsesQuery(environmentId, { ...filters, skip: 5 });
- expect(query.skip).toBe(5);
- });
-
- test("add contactId to the query", () => {
- const query = getResponsesQuery(environmentId, { ...filters, contactId: "contact_1" });
- expect(query.where).toEqual({
- survey: { environmentId },
- contactId: "contact_1",
- });
- });
-
- test("combine multiple filters correctly", () => {
- const params = {
- ...filters,
- surveyId: "survey_1",
- startDate: new Date("2023-01-01"),
- endDate: new Date("2023-01-31"),
- limit: 20,
- skip: 10,
- contactId: "contact_1",
- };
- const query = getResponsesQuery(environmentId, params);
- expect(query.where).toEqual({
- survey: { environmentId },
- surveyId: "survey_1",
- createdAt: { lte: params.endDate, gte: params.startDate },
- contactId: "contact_1",
- });
- expect(query.orderBy).toEqual({
- createdAt: "asc",
- });
- expect(query.take).toBe(20);
- expect(query.skip).toBe(10);
+ const result = getResponsesQuery("env-id", { surveyId: "test" } as TGetResponsesFilter);
+ expect(pickCommonFilter).toHaveBeenCalledWith({ surveyId: "test" });
+ expect(buildCommonFilterQuery).toHaveBeenCalledWith(
+ expect.objectContaining({
+ where: {
+ survey: { environmentId: "env-id" },
+ surveyId: "test",
+ },
+ }),
+ { someFilter: true }
+ );
+ expect(result).toEqual({ where: { combined: true } });
});
});
diff --git a/apps/web/modules/api/v2/management/responses/lib/utils.ts b/apps/web/modules/api/v2/management/responses/lib/utils.ts
index 536022d508..5fa258311c 100644
--- a/apps/web/modules/api/v2/management/responses/lib/utils.ts
+++ b/apps/web/modules/api/v2/management/responses/lib/utils.ts
@@ -1,9 +1,8 @@
+import { buildCommonFilterQuery, pickCommonFilter } from "@/modules/api/v2/management/lib/utils";
import { TGetResponsesFilter } from "@/modules/api/v2/management/responses/types/responses";
import { Prisma } from "@prisma/client";
export const getResponsesQuery = (environmentId: string, params?: TGetResponsesFilter) => {
- const { surveyId, limit, skip, sortBy, order, startDate, endDate, contactId } = params || {};
-
let query: Prisma.ResponseFindManyArgs = {
where: {
survey: {
@@ -12,6 +11,10 @@ export const getResponsesQuery = (environmentId: string, params?: TGetResponsesF
},
};
+ if (!params) return query;
+
+ const { surveyId, contactId } = params || {};
+
if (surveyId) {
query = {
...query,
@@ -22,55 +25,6 @@ export const getResponsesQuery = (environmentId: string, params?: TGetResponsesF
};
}
- if (startDate) {
- query = {
- ...query,
- where: {
- ...query.where,
- createdAt: {
- ...(query.where?.createdAt as Prisma.DateTimeFilter<"Response">),
- gte: startDate,
- },
- },
- };
- }
-
- if (endDate) {
- query = {
- ...query,
- where: {
- ...query.where,
- createdAt: {
- ...(query.where?.createdAt as Prisma.DateTimeFilter<"Response">),
- lte: endDate,
- },
- },
- };
- }
-
- if (sortBy) {
- query = {
- ...query,
- orderBy: {
- [sortBy]: order,
- },
- };
- }
-
- if (limit) {
- query = {
- ...query,
- take: limit,
- };
- }
-
- if (skip) {
- query = {
- ...query,
- skip: skip,
- };
- }
-
if (contactId) {
query = {
...query,
@@ -81,5 +35,11 @@ export const getResponsesQuery = (environmentId: string, params?: TGetResponsesF
};
}
+ const baseFilter = pickCommonFilter(params);
+
+ if (baseFilter) {
+ query = buildCommonFilterQuery(query, baseFilter);
+ }
+
return query;
};
diff --git a/apps/web/modules/api/v2/management/responses/types/responses.ts b/apps/web/modules/api/v2/management/responses/types/responses.ts
index b2161aa953..96a1655929 100644
--- a/apps/web/modules/api/v2/management/responses/types/responses.ts
+++ b/apps/web/modules/api/v2/management/responses/types/responses.ts
@@ -1,28 +1,21 @@
+import { ZGetFilter } from "@/modules/api/v2/types/api-filter";
import { z } from "zod";
import { ZResponse } from "@formbricks/database/zod/responses";
-export const ZGetResponsesFilter = z
- .object({
- limit: z.coerce.number().positive().min(1).max(100).optional().default(10),
- skip: z.coerce.number().nonnegative().optional().default(0),
- sortBy: z.enum(["createdAt", "updatedAt"]).optional().default("createdAt"),
- order: z.enum(["asc", "desc"]).optional().default("desc"),
- startDate: z.coerce.date().optional(),
- endDate: z.coerce.date().optional(),
- surveyId: z.string().cuid2().optional(),
- contactId: z.string().optional(),
- })
- .refine(
- (data) => {
- if (data.startDate && data.endDate && data.startDate > data.endDate) {
- return false;
- }
- return true;
- },
- {
- message: "startDate must be before endDate",
+export const ZGetResponsesFilter = ZGetFilter.extend({
+ surveyId: z.string().cuid2().optional(),
+ contactId: z.string().optional(),
+}).refine(
+ (data) => {
+ if (data.startDate && data.endDate && data.startDate > data.endDate) {
+ return false;
}
- );
+ return true;
+ },
+ {
+ message: "startDate must be before endDate",
+ }
+);
export type TGetResponsesFilter = z.infer;
@@ -39,21 +32,16 @@ export const ZResponseInput = ZResponse.pick({
variables: true,
ttc: true,
meta: true,
-})
- .partial({
- displayId: true,
- singleUseId: true,
- endingId: true,
- language: true,
- variables: true,
- ttc: true,
- meta: true,
- createdAt: true,
- updatedAt: true,
- })
- .openapi({
- ref: "responseCreate",
- description: "A response to create",
- });
+}).partial({
+ displayId: true,
+ singleUseId: true,
+ endingId: true,
+ language: true,
+ variables: true,
+ ttc: true,
+ meta: true,
+ createdAt: true,
+ updatedAt: true,
+});
export type TResponseInput = z.infer;
diff --git a/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/openapi.ts b/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/openapi.ts
new file mode 100644
index 0000000000..6d0c6e2615
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/openapi.ts
@@ -0,0 +1,81 @@
+import { webhookIdSchema } from "@/modules/api/v2/management/webhooks/[webhookId]/types/webhooks";
+import { ZWebhookInput } from "@/modules/api/v2/management/webhooks/types/webhooks";
+import { makePartialSchema } from "@/modules/api/v2/types/openapi-response";
+import { z } from "zod";
+import { ZodOpenApiOperationObject } from "zod-openapi";
+import { ZWebhook } from "@formbricks/database/zod/webhooks";
+
+export const getWebhookEndpoint: ZodOpenApiOperationObject = {
+ operationId: "getWebhook",
+ summary: "Get a webhook",
+ description: "Gets a webhook from the database.",
+ requestParams: {
+ path: z.object({
+ webhookId: webhookIdSchema,
+ }),
+ },
+ tags: ["Management API > Webhooks"],
+ responses: {
+ "200": {
+ description: "Webhook retrieved successfully.",
+ content: {
+ "application/json": {
+ schema: makePartialSchema(ZWebhook),
+ },
+ },
+ },
+ },
+};
+
+export const deleteWebhookEndpoint: ZodOpenApiOperationObject = {
+ operationId: "deleteWebhook",
+ summary: "Delete a webhook",
+ description: "Deletes a webhook from the database.",
+ tags: ["Management API > Webhooks"],
+ requestParams: {
+ path: z.object({
+ webhookId: webhookIdSchema,
+ }),
+ },
+ responses: {
+ "200": {
+ description: "Webhook deleted successfully.",
+ content: {
+ "application/json": {
+ schema: makePartialSchema(ZWebhook),
+ },
+ },
+ },
+ },
+};
+
+export const updateWebhookEndpoint: ZodOpenApiOperationObject = {
+ operationId: "updateWebhook",
+ summary: "Update a webhook",
+ description: "Updates a webhook in the database.",
+ tags: ["Management API > Webhooks"],
+ requestParams: {
+ path: z.object({
+ webhookId: webhookIdSchema,
+ }),
+ },
+ requestBody: {
+ required: true,
+ description: "The webhook to update",
+ content: {
+ "application/json": {
+ schema: ZWebhookInput,
+ },
+ },
+ },
+ responses: {
+ "200": {
+ description: "Webhook updated successfully.",
+ content: {
+ "application/json": {
+ schema: makePartialSchema(ZWebhook),
+ },
+ },
+ },
+ },
+};
diff --git a/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/tests/mocks/webhook.mock.ts b/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/tests/mocks/webhook.mock.ts
new file mode 100644
index 0000000000..a6b335ba5e
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/tests/mocks/webhook.mock.ts
@@ -0,0 +1,20 @@
+import { WebhookSource } from "@prisma/client";
+import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
+import { PrismaErrorType } from "@formbricks/database/types/error";
+
+export const mockedPrismaWebhookUpdateReturn = {
+ id: "123",
+ url: "",
+ name: null,
+ createdAt: new Date("2025-03-24T07:27:36.850Z"),
+ updatedAt: new Date("2025-03-24T07:27:36.850Z"),
+ source: "user" as WebhookSource,
+ environmentId: "",
+ triggers: [],
+ surveyIds: [],
+};
+
+export const prismaNotFoundError = new PrismaClientKnownRequestError("Record does not exist", {
+ code: PrismaErrorType.RecordDoesNotExist,
+ clientVersion: "PrismaClient 4.0.0",
+});
diff --git a/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/tests/webhook.test.ts b/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/tests/webhook.test.ts
new file mode 100644
index 0000000000..858f7fc74c
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/tests/webhook.test.ts
@@ -0,0 +1,126 @@
+import { webhookCache } from "@/lib/cache/webhook";
+import {
+ mockedPrismaWebhookUpdateReturn,
+ prismaNotFoundError,
+} from "@/modules/api/v2/management/webhooks/[webhookId]/lib/tests/mocks/webhook.mock";
+import { webhookUpdateSchema } from "@/modules/api/v2/management/webhooks/[webhookId]/types/webhooks";
+import { describe, expect, test, vi } from "vitest";
+import { z } from "zod";
+import { prisma } from "@formbricks/database";
+import { deleteWebhook, getWebhook, updateWebhook } from "../webhook";
+
+vi.mock("@formbricks/database", () => ({
+ prisma: {
+ webhook: {
+ findUnique: vi.fn(),
+ update: vi.fn(),
+ delete: vi.fn(),
+ },
+ },
+}));
+
+vi.mock("@/lib/cache/webhook", () => ({
+ webhookCache: {
+ tag: {
+ byId: () => "mockTag",
+ },
+ revalidate: vi.fn(),
+ },
+}));
+
+describe("getWebhook", () => {
+ test("returns ok if webhook is found", async () => {
+ vi.mocked(prisma.webhook.findUnique).mockResolvedValueOnce({ id: "123" });
+ const result = await getWebhook("123");
+ expect(result.ok).toBe(true);
+
+ if (result.ok) {
+ expect(result.data).toEqual({ id: "123" });
+ }
+ });
+
+ test("returns err if webhook not found", async () => {
+ vi.mocked(prisma.webhook.findUnique).mockResolvedValueOnce(null);
+ const result = await getWebhook("999");
+ expect(result.ok).toBe(false);
+
+ if (!result.ok) {
+ expect(result.error?.type).toBe("not_found");
+ }
+ });
+
+ test("returns err on Prisma error", async () => {
+ vi.mocked(prisma.webhook.findUnique).mockRejectedValueOnce(new Error("DB error"));
+ const result = await getWebhook("error");
+ expect(result.ok).toBe(false);
+
+ if (!result.ok) {
+ expect(result.error.type).toBe("internal_server_error");
+ }
+ });
+});
+
+describe("updateWebhook", () => {
+ const mockedWebhookUpdateReturn = { url: "https://example.com" } as z.infer;
+
+ test("returns ok on successful update", async () => {
+ vi.mocked(prisma.webhook.update).mockResolvedValueOnce(mockedPrismaWebhookUpdateReturn);
+ const result = await updateWebhook("123", mockedWebhookUpdateReturn);
+ expect(result.ok).toBe(true);
+
+ if (result.ok) {
+ expect(result.data).toEqual(mockedPrismaWebhookUpdateReturn);
+ }
+
+ expect(webhookCache.revalidate).toHaveBeenCalled();
+ });
+
+ test("returns not_found if record does not exist", async () => {
+ vi.mocked(prisma.webhook.update).mockRejectedValueOnce(prismaNotFoundError);
+ const result = await updateWebhook("999", mockedWebhookUpdateReturn);
+ expect(result.ok).toBe(false);
+
+ if (!result.ok) {
+ expect(result.error?.type).toBe("not_found");
+ }
+ });
+
+ test("returns internal_server_error if other error occurs", async () => {
+ vi.mocked(prisma.webhook.update).mockRejectedValueOnce(new Error("Unknown error"));
+ const result = await updateWebhook("abc", mockedWebhookUpdateReturn);
+ expect(result.ok).toBe(false);
+
+ if (!result.ok) {
+ expect(result.error?.type).toBe("internal_server_error");
+ }
+ });
+});
+
+describe("deleteWebhook", () => {
+ test("returns ok on successful delete", async () => {
+ vi.mocked(prisma.webhook.delete).mockResolvedValueOnce(mockedPrismaWebhookUpdateReturn);
+ const result = await deleteWebhook("123");
+ expect(result.ok).toBe(true);
+ expect(webhookCache.revalidate).toHaveBeenCalled();
+ });
+
+ test("returns not_found if record does not exist", async () => {
+ vi.mocked(prisma.webhook.delete).mockRejectedValueOnce(prismaNotFoundError);
+ const result = await deleteWebhook("999");
+ expect(result.ok).toBe(false);
+
+ if (!result.ok) {
+ expect(result.error?.type).toBe("not_found");
+ }
+ });
+
+ test("returns internal_server_error on other errors", async () => {
+ vi.mocked(prisma.webhook.delete).mockRejectedValueOnce(new Error("Delete error"));
+ const result = await deleteWebhook("abc");
+ expect(result.ok).toBe(false);
+
+ if (!result.ok) {
+ expect(result.error?.type).toBe("internal_server_error");
+ }
+ });
+});
diff --git a/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/webhook.ts b/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/webhook.ts
new file mode 100644
index 0000000000..519cc3a9a7
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/[webhookId]/lib/webhook.ts
@@ -0,0 +1,111 @@
+import { webhookCache } from "@/lib/cache/webhook";
+import { webhookUpdateSchema } from "@/modules/api/v2/management/webhooks/[webhookId]/types/webhooks";
+import { ApiErrorResponseV2 } from "@/modules/api/v2/types/api-error";
+import { Webhook } from "@prisma/client";
+import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
+import { z } from "zod";
+import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
+import { cache } from "@formbricks/lib/cache";
+import { Result, err, ok } from "@formbricks/types/error-handlers";
+
+export const getWebhook = async (webhookId: string) =>
+ cache(
+ async (): Promise> => {
+ try {
+ const webhook = await prisma.webhook.findUnique({
+ where: {
+ id: webhookId,
+ },
+ });
+
+ if (!webhook) {
+ return err({
+ type: "not_found",
+ details: [{ field: "webhook", issue: "not found" }],
+ });
+ }
+
+ return ok(webhook);
+ } catch (error) {
+ return err({
+ type: "internal_server_error",
+ details: [{ field: "webhook", issue: error.message }],
+ });
+ }
+ },
+ [`management-getWebhook-${webhookId}`],
+ {
+ tags: [webhookCache.tag.byId(webhookId)],
+ }
+ )();
+
+export const updateWebhook = async (
+ webhookId: string,
+ webhookInput: z.infer
+): Promise> => {
+ try {
+ const updatedWebhook = await prisma.webhook.update({
+ where: {
+ id: webhookId,
+ },
+ data: webhookInput,
+ });
+
+ webhookCache.revalidate({
+ id: webhookId,
+ });
+
+ return ok(updatedWebhook);
+ } catch (error) {
+ if (error instanceof PrismaClientKnownRequestError) {
+ if (
+ error.code === PrismaErrorType.RecordDoesNotExist ||
+ error.code === PrismaErrorType.RelatedRecordDoesNotExist
+ ) {
+ return err({
+ type: "not_found",
+ details: [{ field: "webhook", issue: "not found" }],
+ });
+ }
+ }
+ return err({
+ type: "internal_server_error",
+ details: [{ field: "webhook", issue: error.message }],
+ });
+ }
+};
+
+export const deleteWebhook = async (webhookId: string): Promise> => {
+ try {
+ const deletedWebhook = await prisma.webhook.delete({
+ where: {
+ id: webhookId,
+ },
+ });
+
+ webhookCache.revalidate({
+ id: deletedWebhook.id,
+ environmentId: deletedWebhook.environmentId,
+ source: deletedWebhook.source,
+ });
+
+ return ok(deletedWebhook);
+ } catch (error) {
+ if (error instanceof PrismaClientKnownRequestError) {
+ if (
+ error.code === PrismaErrorType.RecordDoesNotExist ||
+ error.code === PrismaErrorType.RelatedRecordDoesNotExist
+ ) {
+ return err({
+ type: "not_found",
+ details: [{ field: "webhook", issue: "not found" }],
+ });
+ }
+ }
+ return err({
+ type: "internal_server_error",
+ details: [{ field: "webhook", issue: error.message }],
+ });
+ }
+};
diff --git a/apps/web/modules/api/v2/management/webhooks/[webhookId]/route.ts b/apps/web/modules/api/v2/management/webhooks/[webhookId]/route.ts
new file mode 100644
index 0000000000..2c1fa0cb53
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/[webhookId]/route.ts
@@ -0,0 +1,156 @@
+import { responses } from "@/modules/api/v2/lib/response";
+import { handleApiError } from "@/modules/api/v2/lib/utils";
+import { authenticatedApiClient } from "@/modules/api/v2/management/auth/authenticated-api-client";
+import { checkAuthorization } from "@/modules/api/v2/management/auth/check-authorization";
+import { getEnvironmentIdFromSurveyIds } from "@/modules/api/v2/management/lib/helper";
+import {
+ deleteWebhook,
+ getWebhook,
+ updateWebhook,
+} from "@/modules/api/v2/management/webhooks/[webhookId]/lib/webhook";
+import {
+ webhookIdSchema,
+ webhookUpdateSchema,
+} from "@/modules/api/v2/management/webhooks/[webhookId]/types/webhooks";
+import { NextRequest } from "next/server";
+import { z } from "zod";
+
+export const GET = async (request: NextRequest, props: { params: Promise<{ webhookId: string }> }) =>
+ authenticatedApiClient({
+ request,
+ schemas: {
+ params: z.object({ webhookId: webhookIdSchema }),
+ },
+ externalParams: props.params,
+ handler: async ({ authentication, parsedInput }) => {
+ const { params } = parsedInput;
+
+ if (!params) {
+ return handleApiError(request, {
+ type: "bad_request",
+ details: [{ field: "params", issue: "missing" }],
+ });
+ }
+
+ const webhook = await getWebhook(params.webhookId);
+
+ if (!webhook.ok) {
+ return handleApiError(request, webhook.error);
+ }
+
+ const checkAuthorizationResult = await checkAuthorization({
+ authentication,
+ environmentId: webhook.ok ? webhook.data.environmentId : "",
+ });
+
+ if (!checkAuthorizationResult.ok) {
+ return handleApiError(request, checkAuthorizationResult.error);
+ }
+
+ return responses.successResponse(webhook);
+ },
+ });
+
+export const PUT = async (request: NextRequest, props: { params: Promise<{ webhookId: string }> }) =>
+ authenticatedApiClient({
+ request,
+ schemas: {
+ params: z.object({ webhookId: webhookIdSchema }),
+ body: webhookUpdateSchema,
+ },
+ externalParams: props.params,
+ handler: async ({ authentication, parsedInput }) => {
+ const { params, body } = parsedInput;
+
+ if (!body || !params) {
+ return handleApiError(request, {
+ type: "bad_request",
+ details: [{ field: !body ? "body" : "params", issue: "missing" }],
+ });
+ }
+
+ // get surveys environment
+ const surveysEnvironmentId = await getEnvironmentIdFromSurveyIds(body.surveyIds);
+
+ if (!surveysEnvironmentId.ok) {
+ return handleApiError(request, surveysEnvironmentId.error);
+ }
+
+ // get webhook environment
+ const webhook = await getWebhook(params.webhookId);
+
+ if (!webhook.ok) {
+ return handleApiError(request, webhook.error);
+ }
+
+ // check webhook environment against the api key environment
+ const checkAuthorizationResult = await checkAuthorization({
+ authentication,
+ environmentId: webhook.ok ? webhook.data.environmentId : "",
+ });
+
+ if (!checkAuthorizationResult.ok) {
+ return handleApiError(request, checkAuthorizationResult.error);
+ }
+
+ // check if webhook environment matches the surveys environment
+ if (webhook.data.environmentId !== surveysEnvironmentId.data) {
+ return handleApiError(request, {
+ type: "bad_request",
+ details: [
+ { field: "surveys id", issue: "webhook environment does not match the surveys environment" },
+ ],
+ });
+ }
+
+ const updatedWebhook = await updateWebhook(params.webhookId, body);
+
+ if (!updatedWebhook.ok) {
+ return handleApiError(request, updatedWebhook.error);
+ }
+
+ return responses.successResponse(updatedWebhook);
+ },
+ });
+
+export const DELETE = async (request: NextRequest, props: { params: Promise<{ webhookId: string }> }) =>
+ authenticatedApiClient({
+ request,
+ schemas: {
+ params: z.object({ webhookId: webhookIdSchema }),
+ },
+ externalParams: props.params,
+ handler: async ({ authentication, parsedInput }) => {
+ const { params } = parsedInput;
+
+ if (!params) {
+ return handleApiError(request, {
+ type: "bad_request",
+ details: [{ field: "params", issue: "missing" }],
+ });
+ }
+
+ const webhook = await getWebhook(params.webhookId);
+
+ if (!webhook.ok) {
+ return handleApiError(request, webhook.error);
+ }
+
+ const checkAuthorizationResult = await checkAuthorization({
+ authentication,
+ environmentId: webhook.ok ? webhook.data.environmentId : "",
+ });
+
+ if (!checkAuthorizationResult.ok) {
+ return handleApiError(request, checkAuthorizationResult.error);
+ }
+
+ const deletedWebhook = await deleteWebhook(params.webhookId);
+
+ if (!deletedWebhook.ok) {
+ return handleApiError(request, deletedWebhook.error);
+ }
+
+ return responses.successResponse(deletedWebhook);
+ },
+ });
diff --git a/apps/web/modules/api/v2/management/webhooks/[webhookId]/types/webhooks.ts b/apps/web/modules/api/v2/management/webhooks/[webhookId]/types/webhooks.ts
new file mode 100644
index 0000000000..9bcc7a708a
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/[webhookId]/types/webhooks.ts
@@ -0,0 +1,27 @@
+import { z } from "zod";
+import { extendZodWithOpenApi } from "zod-openapi";
+import { ZWebhook } from "@formbricks/database/zod/webhooks";
+
+extendZodWithOpenApi(z);
+
+export const webhookIdSchema = z
+ .string()
+ .cuid2()
+ .openapi({
+ ref: "webhookId",
+ description: "The ID of the webhook",
+ param: {
+ name: "id",
+ in: "path",
+ },
+ });
+
+export const webhookUpdateSchema = ZWebhook.omit({
+ id: true,
+ createdAt: true,
+ updatedAt: true,
+ environmentId: true,
+}).openapi({
+ ref: "webhookUpdate",
+ description: "A webhook to update.",
+});
diff --git a/apps/web/modules/api/v2/management/webhooks/lib/openapi.ts b/apps/web/modules/api/v2/management/webhooks/lib/openapi.ts
new file mode 100644
index 0000000000..92bac070d2
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/lib/openapi.ts
@@ -0,0 +1,68 @@
+import {
+ deleteWebhookEndpoint,
+ getWebhookEndpoint,
+ updateWebhookEndpoint,
+} from "@/modules/api/v2/management/webhooks/[webhookId]/lib/openapi";
+import { ZGetWebhooksFilter, ZWebhookInput } from "@/modules/api/v2/management/webhooks/types/webhooks";
+import { makePartialSchema, responseWithMetaSchema } from "@/modules/api/v2/types/openapi-response";
+import { z } from "zod";
+import { ZodOpenApiOperationObject, ZodOpenApiPathsObject } from "zod-openapi";
+import { ZWebhook } from "@formbricks/database/zod/webhooks";
+
+export const getWebhooksEndpoint: ZodOpenApiOperationObject = {
+ operationId: "getWebhooks",
+ summary: "Get webhooks",
+ description: "Gets webhooks from the database.",
+ requestParams: {
+ query: ZGetWebhooksFilter.sourceType().required(),
+ },
+ tags: ["Management API > Webhooks"],
+ responses: {
+ "200": {
+ description: "Webhooks retrieved successfully.",
+ content: {
+ "application/json": {
+ schema: z.array(responseWithMetaSchema(makePartialSchema(ZWebhook))),
+ },
+ },
+ },
+ },
+};
+
+export const createWebhookEndpoint: ZodOpenApiOperationObject = {
+ operationId: "createWebhook",
+ summary: "Create a webhook",
+ description: "Creates a webhook in the database.",
+ tags: ["Management API > Webhooks"],
+ requestBody: {
+ required: true,
+ description: "The webhook to create",
+ content: {
+ "application/json": {
+ schema: ZWebhookInput,
+ },
+ },
+ },
+ responses: {
+ "201": {
+ description: "Webhook created successfully.",
+ content: {
+ "application/json": {
+ schema: makePartialSchema(ZWebhook),
+ },
+ },
+ },
+ },
+};
+
+export const webhookPaths: ZodOpenApiPathsObject = {
+ "/webhooks": {
+ get: getWebhooksEndpoint,
+ post: createWebhookEndpoint,
+ },
+ "/webhooks/{webhookId}": {
+ get: getWebhookEndpoint,
+ put: updateWebhookEndpoint,
+ delete: deleteWebhookEndpoint,
+ },
+};
diff --git a/apps/web/modules/api/v2/management/webhooks/lib/tests/utils.test.ts b/apps/web/modules/api/v2/management/webhooks/lib/tests/utils.test.ts
new file mode 100644
index 0000000000..1314708eaf
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/lib/tests/utils.test.ts
@@ -0,0 +1,36 @@
+import { buildCommonFilterQuery, pickCommonFilter } from "@/modules/api/v2/management/lib/utils";
+import { TGetWebhooksFilter } from "@/modules/api/v2/management/webhooks/types/webhooks";
+import { describe, expect, it, vi } from "vitest";
+import { getWebhooksQuery } from "../utils";
+
+vi.mock("@/modules/api/v2/management/lib/utils", () => ({
+ pickCommonFilter: vi.fn(),
+ buildCommonFilterQuery: vi.fn(),
+}));
+
+describe("getWebhooksQuery", () => {
+ const environmentId = "env-123";
+
+ it("adds surveyIds condition when provided", () => {
+ const params = { surveyIds: ["survey1"] } as TGetWebhooksFilter;
+ const result = getWebhooksQuery(environmentId, params);
+ expect(result).toBeDefined();
+ expect(result?.where).toMatchObject({
+ environmentId,
+ surveyIds: { hasSome: ["survey1"] },
+ });
+ });
+
+ it("calls pickCommonFilter and buildCommonFilterQuery when baseFilter is present", () => {
+ vi.mocked(pickCommonFilter).mockReturnValue({ someFilter: "test" } as any);
+ getWebhooksQuery(environmentId, { surveyIds: ["survey1"] } as TGetWebhooksFilter);
+ expect(pickCommonFilter).toHaveBeenCalled();
+ expect(buildCommonFilterQuery).toHaveBeenCalled();
+ });
+
+ it("buildCommonFilterQuery is not called if no baseFilter is picked", () => {
+ vi.mocked(pickCommonFilter).mockReturnValue(undefined as any);
+ getWebhooksQuery(environmentId, {} as any);
+ expect(buildCommonFilterQuery).not.toHaveBeenCalled();
+ });
+});
diff --git a/apps/web/modules/api/v2/management/webhooks/lib/tests/webhook.test.ts b/apps/web/modules/api/v2/management/webhooks/lib/tests/webhook.test.ts
new file mode 100644
index 0000000000..b0e2104d9c
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/lib/tests/webhook.test.ts
@@ -0,0 +1,117 @@
+import { webhookCache } from "@/lib/cache/webhook";
+import { TGetWebhooksFilter, TWebhookInput } from "@/modules/api/v2/management/webhooks/types/webhooks";
+import { WebhookSource } from "@prisma/client";
+import { describe, expect, it, vi } from "vitest";
+import { prisma } from "@formbricks/database";
+import { captureTelemetry } from "@formbricks/lib/telemetry";
+import { createWebhook, getWebhooks } from "../webhook";
+
+vi.mock("@formbricks/database", () => ({
+ prisma: {
+ $transaction: vi.fn(),
+ webhook: {
+ findMany: vi.fn(),
+ count: vi.fn(),
+ create: vi.fn(),
+ },
+ },
+}));
+vi.mock("@/lib/cache/webhook", () => ({
+ webhookCache: {
+ revalidate: vi.fn(),
+ },
+}));
+vi.mock("@formbricks/lib/telemetry", () => ({
+ captureTelemetry: vi.fn(),
+}));
+
+describe("getWebhooks", () => {
+ const environmentId = "env1";
+ const params = {
+ limit: 10,
+ skip: 0,
+ };
+ const fakeWebhooks = [
+ { id: "w1", environmentId, name: "Webhook One" },
+ { id: "w2", environmentId, name: "Webhook Two" },
+ ];
+ const count = fakeWebhooks.length;
+
+ it("returns ok response with webhooks and meta", async () => {
+ vi.mocked(prisma.$transaction).mockResolvedValueOnce([fakeWebhooks, count]);
+
+ const result = await getWebhooks(environmentId, params as TGetWebhooksFilter);
+ expect(result.ok).toBe(true);
+
+ if (result.ok) {
+ expect(result.data.data).toEqual(fakeWebhooks);
+ expect(result.data.meta).toEqual({
+ total: count,
+ limit: params.limit,
+ offset: params.skip,
+ });
+ }
+ });
+
+ it("returns error when prisma.$transaction throws", async () => {
+ vi.mocked(prisma.$transaction).mockRejectedValueOnce(new Error("Test error"));
+
+ const result = await getWebhooks(environmentId, params as TGetWebhooksFilter);
+ expect(result.ok).toBe(false);
+
+ if (!result.ok) {
+ expect(result.error?.type).toEqual("internal_server_error");
+ }
+ });
+});
+
+describe("createWebhook", () => {
+ const inputWebhook = {
+ environmentId: "env1",
+ name: "New Webhook",
+ url: "http://example.com",
+ source: "user" as WebhookSource,
+ triggers: ["trigger1"],
+ surveyIds: ["s1", "s2"],
+ } as unknown as TWebhookInput;
+
+ const createdWebhook = {
+ id: "w100",
+ environmentId: inputWebhook.environmentId,
+ name: inputWebhook.name,
+ url: inputWebhook.url,
+ source: inputWebhook.source,
+ triggers: inputWebhook.triggers,
+ surveyIds: inputWebhook.surveyIds,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ };
+
+ it("creates a webhook and revalidates cache", async () => {
+ vi.mocked(prisma.webhook.create).mockResolvedValueOnce(createdWebhook);
+
+ const result = await createWebhook(inputWebhook);
+ expect(captureTelemetry).toHaveBeenCalledWith("webhook_created");
+ expect(prisma.webhook.create).toHaveBeenCalled();
+ expect(webhookCache.revalidate).toHaveBeenCalledWith({
+ environmentId: createdWebhook.environmentId,
+ source: createdWebhook.source,
+ });
+ expect(result.ok).toBe(true);
+
+ if (result.ok) {
+ expect(result.data).toEqual(createdWebhook);
+ }
+ });
+
+ it("returns error when creation fails", async () => {
+ vi.mocked(prisma.webhook.create).mockRejectedValueOnce(new Error("Creation failed"));
+
+ const result = await createWebhook(inputWebhook);
+ expect(result.ok).toBe(false);
+
+ if (!result.ok) {
+ expect(result.error.type).toEqual("internal_server_error");
+ }
+ });
+});
diff --git a/apps/web/modules/api/v2/management/webhooks/lib/utils.ts b/apps/web/modules/api/v2/management/webhooks/lib/utils.ts
new file mode 100644
index 0000000000..59716e4cd8
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/lib/utils.ts
@@ -0,0 +1,35 @@
+import { buildCommonFilterQuery, pickCommonFilter } from "@/modules/api/v2/management/lib/utils";
+import { TGetWebhooksFilter } from "@/modules/api/v2/management/webhooks/types/webhooks";
+import { Prisma } from "@prisma/client";
+
+export const getWebhooksQuery = (environmentId: string, params?: TGetWebhooksFilter) => {
+ let query: Prisma.WebhookFindManyArgs = {
+ where: {
+ environmentId,
+ },
+ };
+
+ if (!params) return query;
+
+ const { surveyIds } = params || {};
+
+ if (surveyIds) {
+ query = {
+ ...query,
+ where: {
+ ...query.where,
+ surveyIds: {
+ hasSome: surveyIds,
+ },
+ },
+ };
+ }
+
+ const baseFilter = pickCommonFilter(params);
+
+ if (baseFilter) {
+ query = buildCommonFilterQuery(query, baseFilter);
+ }
+
+ return query;
+};
diff --git a/apps/web/modules/api/v2/management/webhooks/lib/webhook.ts b/apps/web/modules/api/v2/management/webhooks/lib/webhook.ts
new file mode 100644
index 0000000000..7d9d15fbf3
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/lib/webhook.ts
@@ -0,0 +1,83 @@
+import { webhookCache } from "@/lib/cache/webhook";
+import { getWebhooksQuery } from "@/modules/api/v2/management/webhooks/lib/utils";
+import { TGetWebhooksFilter, TWebhookInput } from "@/modules/api/v2/management/webhooks/types/webhooks";
+import { ApiErrorResponseV2 } from "@/modules/api/v2/types/api-error";
+import { ApiResponseWithMeta } from "@/modules/api/v2/types/api-success";
+import { Prisma, Webhook } from "@prisma/client";
+import { prisma } from "@formbricks/database";
+import { captureTelemetry } from "@formbricks/lib/telemetry";
+import { Result, err, ok } from "@formbricks/types/error-handlers";
+
+export const getWebhooks = async (
+ environmentId: string,
+ params: TGetWebhooksFilter
+): Promise, ApiErrorResponseV2>> => {
+ try {
+ const [webhooks, count] = await prisma.$transaction([
+ prisma.webhook.findMany({
+ ...getWebhooksQuery(environmentId, params),
+ }),
+ prisma.webhook.count({
+ where: getWebhooksQuery(environmentId, params).where,
+ }),
+ ]);
+
+ if (!webhooks) {
+ return err({
+ type: "not_found",
+ details: [{ field: "webhooks", issue: "not_found" }],
+ });
+ }
+
+ return ok({
+ data: webhooks,
+ meta: {
+ total: count,
+ limit: params?.limit,
+ offset: params?.skip,
+ },
+ });
+ } catch (error) {
+ return err({
+ type: "internal_server_error",
+ details: [{ field: "webhooks", issue: error.message }],
+ });
+ }
+};
+
+export const createWebhook = async (webhook: TWebhookInput): Promise> => {
+ captureTelemetry("webhook_created");
+
+ const { environmentId, name, url, source, triggers, surveyIds } = webhook;
+
+ try {
+ const prismaData: Prisma.WebhookCreateInput = {
+ environment: {
+ connect: {
+ id: environmentId,
+ },
+ },
+ name,
+ url,
+ source,
+ triggers,
+ surveyIds,
+ };
+
+ const createdWebhook = await prisma.webhook.create({
+ data: prismaData,
+ });
+
+ webhookCache.revalidate({
+ environmentId: createdWebhook.environmentId,
+ source: createdWebhook.source,
+ });
+
+ return ok(createdWebhook);
+ } catch (error) {
+ return err({
+ type: "internal_server_error",
+ details: [{ field: "webhook", issue: error.message }],
+ });
+ }
+};
diff --git a/apps/web/modules/api/v2/management/webhooks/route.ts b/apps/web/modules/api/v2/management/webhooks/route.ts
new file mode 100644
index 0000000000..994635e13e
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/route.ts
@@ -0,0 +1,86 @@
+import { responses } from "@/modules/api/v2/lib/response";
+import { handleApiError } from "@/modules/api/v2/lib/utils";
+import { authenticatedApiClient } from "@/modules/api/v2/management/auth/authenticated-api-client";
+import { checkAuthorization } from "@/modules/api/v2/management/auth/check-authorization";
+import { getEnvironmentIdFromSurveyIds } from "@/modules/api/v2/management/lib/helper";
+import { createWebhook, getWebhooks } from "@/modules/api/v2/management/webhooks/lib/webhook";
+import { ZGetWebhooksFilter, ZWebhookInput } from "@/modules/api/v2/management/webhooks/types/webhooks";
+import { NextRequest } from "next/server";
+
+export const GET = async (request: NextRequest) =>
+ authenticatedApiClient({
+ request,
+ schemas: {
+ query: ZGetWebhooksFilter.sourceType(),
+ },
+ handler: async ({ authentication, parsedInput }) => {
+ const { query } = parsedInput;
+
+ if (!query) {
+ return handleApiError(request, {
+ type: "bad_request",
+ details: [{ field: "query", issue: "missing" }],
+ });
+ }
+
+ const environmentId = authentication.environmentId;
+
+ const res = await getWebhooks(environmentId, query);
+
+ if (res.ok) {
+ return responses.successResponse(res.data);
+ }
+
+ return handleApiError(request, res.error);
+ },
+ });
+
+export const POST = async (request: NextRequest) =>
+ authenticatedApiClient({
+ request,
+ schemas: {
+ body: ZWebhookInput,
+ },
+ handler: async ({ authentication, parsedInput }) => {
+ const { body } = parsedInput;
+
+ if (!body) {
+ return handleApiError(request, {
+ type: "bad_request",
+ details: [{ field: "body", issue: "missing" }],
+ });
+ }
+
+ const environmentIdResult = await getEnvironmentIdFromSurveyIds(body.surveyIds);
+
+ if (!environmentIdResult.ok) {
+ return handleApiError(request, environmentIdResult.error);
+ }
+
+ const environmentId = environmentIdResult.data;
+
+ if (body.environmentId !== environmentId) {
+ return handleApiError(request, {
+ type: "bad_request",
+ details: [{ field: "environmentId", issue: "does not match the surveys environment" }],
+ });
+ }
+
+ const checkAuthorizationResult = await checkAuthorization({
+ authentication,
+ environmentId,
+ });
+
+ if (!checkAuthorizationResult.ok) {
+ return handleApiError(request, checkAuthorizationResult.error);
+ }
+
+ const createWebhookResult = await createWebhook(body);
+
+ if (!createWebhookResult.ok) {
+ return handleApiError(request, createWebhookResult.error);
+ }
+
+ return responses.successResponse(createWebhookResult);
+ },
+ });
diff --git a/apps/web/modules/api/v2/management/webhooks/types/webhooks.ts b/apps/web/modules/api/v2/management/webhooks/types/webhooks.ts
new file mode 100644
index 0000000000..e049c92413
--- /dev/null
+++ b/apps/web/modules/api/v2/management/webhooks/types/webhooks.ts
@@ -0,0 +1,30 @@
+import { ZGetFilter } from "@/modules/api/v2/types/api-filter";
+import { z } from "zod";
+import { ZWebhook } from "@formbricks/database/zod/webhooks";
+
+export const ZGetWebhooksFilter = ZGetFilter.extend({
+ surveyIds: z.array(z.string().cuid2()).optional(),
+}).refine(
+ (data) => {
+ if (data.startDate && data.endDate && data.startDate > data.endDate) {
+ return false;
+ }
+ return true;
+ },
+ {
+ message: "startDate must be before endDate",
+ }
+);
+
+export type TGetWebhooksFilter = z.infer;
+
+export const ZWebhookInput = ZWebhook.pick({
+ name: true,
+ url: true,
+ source: true,
+ environmentId: true,
+ triggers: true,
+ surveyIds: true,
+});
+
+export type TWebhookInput = z.infer;
diff --git a/apps/web/modules/api/v2/openapi-document.ts b/apps/web/modules/api/v2/openapi-document.ts
index 319392e532..250e8f3dc6 100644
--- a/apps/web/modules/api/v2/openapi-document.ts
+++ b/apps/web/modules/api/v2/openapi-document.ts
@@ -3,6 +3,7 @@ import { contactAttributePaths } from "@/modules/api/v2/management/contact-attri
import { contactPaths } from "@/modules/api/v2/management/contacts/lib/openapi";
import { responsePaths } from "@/modules/api/v2/management/responses/lib/openapi";
import { surveyPaths } from "@/modules/api/v2/management/surveys/lib/openapi";
+import { webhookPaths } from "@/modules/api/v2/management/webhooks/lib/openapi";
import { bulkContactPaths } from "@/modules/ee/contacts/api/v2/management/contacts/bulk/lib/openapi";
import * as yaml from "yaml";
import { z } from "zod";
@@ -12,6 +13,7 @@ import { ZContactAttributeKey } from "@formbricks/database/zod/contact-attribute
import { ZContactAttribute } from "@formbricks/database/zod/contact-attributes";
import { ZResponse } from "@formbricks/database/zod/responses";
import { ZSurveyWithoutQuestionType } from "@formbricks/database/zod/surveys";
+import { ZWebhook } from "@formbricks/database/zod/webhooks";
extendZodWithOpenApi(z);
@@ -29,6 +31,7 @@ const document = createDocument({
...contactAttributePaths,
...contactAttributeKeyPaths,
...surveyPaths,
+ ...webhookPaths,
},
servers: [
{
@@ -57,6 +60,10 @@ const document = createDocument({
name: "Management API > Surveys",
description: "Operations for managing surveys.",
},
+ {
+ name: "Management API > Webhooks",
+ description: "Operations for managing webhooks.",
+ },
],
components: {
securitySchemes: {
@@ -73,6 +80,7 @@ const document = createDocument({
contactAttribute: ZContactAttribute,
contactAttributeKey: ZContactAttributeKey,
survey: ZSurveyWithoutQuestionType,
+ webhook: ZWebhook,
},
},
security: [
diff --git a/apps/web/modules/api/v2/types/api-filter.ts b/apps/web/modules/api/v2/types/api-filter.ts
new file mode 100644
index 0000000000..29fe9ab051
--- /dev/null
+++ b/apps/web/modules/api/v2/types/api-filter.ts
@@ -0,0 +1,12 @@
+import { z } from "zod";
+
+export const ZGetFilter = z.object({
+ limit: z.coerce.number().positive().min(1).max(100).optional().default(10),
+ skip: z.coerce.number().nonnegative().optional().default(0),
+ sortBy: z.enum(["createdAt", "updatedAt"]).optional().default("createdAt"),
+ order: z.enum(["asc", "desc"]).optional().default("desc"),
+ startDate: z.coerce.date().optional(),
+ endDate: z.coerce.date().optional(),
+});
+
+export type TGetFilter = z.infer;
diff --git a/apps/web/modules/api/v2/types/openapi-response.ts b/apps/web/modules/api/v2/types/openapi-response.ts
new file mode 100644
index 0000000000..50c2e8445a
--- /dev/null
+++ b/apps/web/modules/api/v2/types/openapi-response.ts
@@ -0,0 +1,19 @@
+import { z } from "zod";
+
+export function responseWithMetaSchema(contentSchema: T) {
+ return z.object({
+ data: z.array(contentSchema).optional(),
+ meta: z
+ .object({
+ total: z.number().optional(),
+ limit: z.number().optional(),
+ offset: z.number().optional(),
+ })
+ .optional(),
+ });
+}
+
+// We use the partial method to make all properties optional so we don't show the response fields as required in the OpenAPI documentation
+export function makePartialSchema>(schema: T) {
+ return schema.partial();
+}
diff --git a/apps/web/modules/auth/lib/authOptions.ts b/apps/web/modules/auth/lib/authOptions.ts
index b9f89c2122..eb9fdd9cfe 100644
--- a/apps/web/modules/auth/lib/authOptions.ts
+++ b/apps/web/modules/auth/lib/authOptions.ts
@@ -173,6 +173,9 @@ export const authOptions: NextAuthOptions = {
// Conditionally add enterprise SSO providers
...(ENTERPRISE_LICENSE_KEY ? getSSOProviders() : []),
],
+ session: {
+ maxAge: 3600,
+ },
callbacks: {
async jwt({ token }) {
const existingUser = await getUserByEmail(token?.email!);
diff --git a/apps/web/modules/auth/lib/user.test.ts b/apps/web/modules/auth/lib/user.test.ts
index 1cbbd63dc8..10a7f6b984 100644
--- a/apps/web/modules/auth/lib/user.test.ts
+++ b/apps/web/modules/auth/lib/user.test.ts
@@ -1,6 +1,7 @@
import { Prisma } from "@prisma/client";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { userCache } from "@formbricks/lib/user/cache";
import { InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
import { mockUser } from "./mock-data";
@@ -57,7 +58,7 @@ describe("User Management", () => {
it("throws InvalidInputError when email already exists", async () => {
const errToThrow = new Prisma.PrismaClientKnownRequestError("Mock error message", {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
vi.mocked(prisma.user.create).mockRejectedValueOnce(errToThrow);
@@ -86,7 +87,7 @@ describe("User Management", () => {
it("throws ResourceNotFoundError when user doesn't exist", async () => {
const errToThrow = new Prisma.PrismaClientKnownRequestError("Mock error message", {
- code: "P2016",
+ code: PrismaErrorType.RecordDoesNotExist,
clientVersion: "0.0.1",
});
vi.mocked(prisma.user.update).mockRejectedValueOnce(errToThrow);
diff --git a/apps/web/modules/auth/lib/user.ts b/apps/web/modules/auth/lib/user.ts
index ab47f40e6e..b5d647dc9e 100644
--- a/apps/web/modules/auth/lib/user.ts
+++ b/apps/web/modules/auth/lib/user.ts
@@ -1,6 +1,7 @@
import { Prisma } from "@prisma/client";
import { cache as reactCache } from "react";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { cache } from "@formbricks/lib/cache";
import { userCache } from "@formbricks/lib/user/cache";
import { validateInputs } from "@formbricks/lib/utils/validate";
@@ -32,7 +33,10 @@ export const updateUser = async (id: string, data: TUserUpdateInput) => {
return updatedUser;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.RecordDoesNotExist
+ ) {
throw new ResourceNotFoundError("User", id);
}
throw error;
@@ -129,7 +133,10 @@ export const createUser = async (data: TUserCreateInput) => {
return user;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.UniqueConstraintViolation
+ ) {
throw new InvalidInputError("User with this email already exists");
}
diff --git a/apps/web/modules/ee/billing/components/pricing-card.tsx b/apps/web/modules/ee/billing/components/pricing-card.tsx
index 09fab34367..5edcc3d297 100644
--- a/apps/web/modules/ee/billing/components/pricing-card.tsx
+++ b/apps/web/modules/ee/billing/components/pricing-card.tsx
@@ -215,6 +215,10 @@ export const PricingCard = ({
text={t("environments.settings.billing.switch_plan_confirmation_text", {
plan: t(plan.name),
price: planPeriod === "monthly" ? plan.price.monthly : plan.price.yearly,
+ period:
+ planPeriod === "monthly"
+ ? t("environments.settings.billing.per_month")
+ : t("environments.settings.billing.per_year"),
})}
buttonVariant="default"
buttonLoading={loading}
diff --git a/apps/web/modules/ee/insights/components/insights-view.test.tsx b/apps/web/modules/ee/insights/components/insights-view.test.tsx
new file mode 100644
index 0000000000..9f41fb3c2e
--- /dev/null
+++ b/apps/web/modules/ee/insights/components/insights-view.test.tsx
@@ -0,0 +1,164 @@
+// InsightView.test.jsx
+import { fireEvent, render, screen } from "@testing-library/react";
+import { describe, expect, test, vi } from "vitest";
+import { TSurveyQuestionSummaryOpenText } from "@formbricks/types/surveys/types";
+import { TUserLocale } from "@formbricks/types/user";
+import { InsightView } from "./insights-view";
+
+// --- Mocks ---
+
+// Stub out the translation hook so that keys are returned as-is.
+vi.mock("@tolgee/react", () => ({
+ useTranslate: () => ({
+ t: (key) => key,
+ }),
+}));
+
+// Spy on formbricks.track
+vi.mock("@formbricks/js", () => ({
+ default: {
+ track: vi.fn(),
+ },
+}));
+
+// A simple implementation for classnames.
+vi.mock("@formbricks/lib/cn", () => ({
+ cn: (...classes) => classes.join(" "),
+}));
+
+// Mock CategoryBadge to render a simple button.
+vi.mock("../experience/components/category-select", () => ({
+ default: ({ category, insightId, onCategoryChange }) => (
+ onCategoryChange(insightId, category)}>
+ CategoryBadge: {category}
+
+ ),
+}));
+
+// Mock InsightSheet to display its open/closed state and the insight title.
+vi.mock("@/modules/ee/insights/components/insight-sheet", () => ({
+ InsightSheet: ({ isOpen, insight }) => (
+
+ {isOpen ? "InsightSheet Open" : "InsightSheet Closed"}
+ {insight && ` - ${insight.title}`}
+
+ ),
+}));
+
+// Create an array of 15 dummy insights.
+// Even-indexed insights will have the category "complaint"
+// and odd-indexed insights will have "praise".
+const dummyInsights = Array.from({ length: 15 }, (_, i) => ({
+ id: `insight-${i}`,
+ _count: { documentInsights: i },
+ title: `Insight Title ${i}`,
+ description: `Insight Description ${i}`,
+ category: i % 2 === 0 ? "complaint" : "praise",
+ updatedAt: new Date(),
+ createdAt: new Date(),
+ environmentId: "environment-1",
+})) as TSurveyQuestionSummaryOpenText["insights"];
+
+// Helper function to render the component with default props.
+const renderComponent = (props = {}) => {
+ const defaultProps = {
+ insights: dummyInsights,
+ questionId: "question-1",
+ surveyId: "survey-1",
+ documentsFilter: {},
+ isFetching: false,
+ documentsPerPage: 5,
+ locale: "en" as TUserLocale,
+ };
+
+ return render( );
+};
+
+// --- Tests ---
+describe("InsightView Component", () => {
+ test("renders table headers", () => {
+ renderComponent();
+ expect(screen.getByText("#")).toBeInTheDocument();
+ expect(screen.getByText("common.title")).toBeInTheDocument();
+ expect(screen.getByText("common.description")).toBeInTheDocument();
+ expect(screen.getByText("environments.experience.category")).toBeInTheDocument();
+ });
+
+ test('shows "no insights found" when insights array is empty', () => {
+ renderComponent({ insights: [] });
+ expect(screen.getByText("environments.experience.no_insights_found")).toBeInTheDocument();
+ });
+
+ test("does not render insights when isFetching is true", () => {
+ renderComponent({ isFetching: true, insights: [] });
+ expect(screen.getByText("environments.experience.no_insights_found")).toBeInTheDocument();
+ });
+
+ test("filters insights based on selected tab", async () => {
+ renderComponent();
+
+ // Click on the "complaint" tab.
+ const complaintTab = screen.getAllByText("environments.experience.complaint")[0];
+ fireEvent.click(complaintTab);
+
+ // Grab all table rows from the table body.
+ const rows = await screen.findAllByRole("row");
+
+ // Check that none of the rows include text from a "praise" insight.
+ rows.forEach((row) => {
+ expect(row.textContent).not.toEqual(/Insight Title 1/);
+ });
+ });
+
+ test("load more button increases visible insights count", () => {
+ renderComponent();
+ // Initially, "Insight Title 10" should not be visible because only 10 items are shown.
+ expect(screen.queryByText("Insight Title 10")).not.toBeInTheDocument();
+
+ // Get all buttons with the text "common.load_more" and filter for those that are visible.
+ const loadMoreButtons = screen.getAllByRole("button", { name: /common\.load_more/i });
+ expect(loadMoreButtons.length).toBeGreaterThan(0);
+
+ // Click the first visible "load more" button.
+ fireEvent.click(loadMoreButtons[0]);
+
+ // Now, "Insight Title 10" should be visible.
+ expect(screen.getByText("Insight Title 10")).toBeInTheDocument();
+ });
+
+ test("opens insight sheet when a row is clicked", () => {
+ renderComponent();
+ // Get all elements that display "Insight Title 0" and use the first one to find its table row
+ const cells = screen.getAllByText("Insight Title 0");
+ expect(cells.length).toBeGreaterThan(0);
+ const rowElement = cells[0].closest("tr");
+ expect(rowElement).not.toBeNull();
+ // Simulate a click on the table row
+ fireEvent.click(rowElement!);
+
+ // Get all instances of the InsightSheet component
+ const sheets = screen.getAllByTestId("insight-sheet");
+ // Filter for the one that contains the expected text
+ const matchingSheet = sheets.find((sheet) =>
+ sheet.textContent?.includes("InsightSheet Open - Insight Title 0")
+ );
+
+ expect(matchingSheet).toBeDefined();
+ expect(matchingSheet).toHaveTextContent("InsightSheet Open - Insight Title 0");
+ });
+
+ test("category badge calls onCategoryChange and updates the badge (even if value remains the same)", () => {
+ renderComponent();
+ // Get the first category badge. For index 0, the category is "complaint".
+ const categoryBadge = screen.getAllByTestId("category-badge")[0];
+
+ // It should display "complaint" initially.
+ expect(categoryBadge).toHaveTextContent("CategoryBadge: complaint");
+
+ // Click the category badge to trigger onCategoryChange.
+ fireEvent.click(categoryBadge);
+
+ // After clicking, the badge should still display "complaint" (since our mock simply passes the current value).
+ expect(categoryBadge).toHaveTextContent("CategoryBadge: complaint");
+ });
+});
diff --git a/apps/web/modules/ee/insights/experience/components/insight-view.test.tsx b/apps/web/modules/ee/insights/experience/components/insight-view.test.tsx
new file mode 100644
index 0000000000..0232660c80
--- /dev/null
+++ b/apps/web/modules/ee/insights/experience/components/insight-view.test.tsx
@@ -0,0 +1,215 @@
+import { TInsightWithDocumentCount } from "@/modules/ee/insights/experience/types/insights";
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
+import { beforeEach, describe, expect, test, vi } from "vitest";
+import { TUserLocale } from "@formbricks/types/user";
+import { InsightView } from "./insight-view";
+
+// Mock the translation hook to simply return the key.
+vi.mock("@tolgee/react", () => ({
+ useTranslate: () => ({
+ t: (key: string) => key,
+ }),
+}));
+
+// Mock the action that fetches insights.
+const mockGetEnvironmentInsightsAction = vi.fn();
+vi.mock("../actions", () => ({
+ getEnvironmentInsightsAction: (...args: any[]) => mockGetEnvironmentInsightsAction(...args),
+}));
+
+// Mock InsightSheet so we can assert on its open state.
+vi.mock("@/modules/ee/insights/components/insight-sheet", () => ({
+ InsightSheet: ({
+ isOpen,
+ insight,
+ }: {
+ isOpen: boolean;
+ insight: any;
+ setIsOpen: any;
+ handleFeedback: any;
+ documentsFilter: any;
+ documentsPerPage: number;
+ locale: string;
+ }) => (
+
+ {isOpen ? `InsightSheet Open${insight ? ` - ${insight.title}` : ""}` : "InsightSheet Closed"}
+
+ ),
+}));
+
+// Mock InsightLoading.
+vi.mock("./insight-loading", () => ({
+ InsightLoading: () => Loading...
,
+}));
+
+// For simplicity, we won’t mock CategoryBadge so it renders normally.
+// If needed, you can also mock it similar to InsightSheet.
+
+// --- Dummy Data ---
+const dummyInsight1 = {
+ id: "1",
+ title: "Insight 1",
+ description: "Description 1",
+ category: "featureRequest",
+ _count: { documentInsights: 5 },
+};
+const dummyInsight2 = {
+ id: "2",
+ title: "Insight 2",
+ description: "Description 2",
+ category: "featureRequest",
+ _count: { documentInsights: 3 },
+};
+const dummyInsightComplaint = {
+ id: "3",
+ title: "Complaint Insight",
+ description: "Complaint Description",
+ category: "complaint",
+ _count: { documentInsights: 10 },
+};
+const dummyInsightPraise = {
+ id: "4",
+ title: "Praise Insight",
+ description: "Praise Description",
+ category: "praise",
+ _count: { documentInsights: 8 },
+};
+
+// A helper to render the component with required props.
+const renderComponent = (props = {}) => {
+ const defaultProps = {
+ statsFrom: new Date("2023-01-01"),
+ environmentId: "env-1",
+ insightsPerPage: 2,
+ documentsPerPage: 5,
+ locale: "en-US" as TUserLocale,
+ };
+
+ return render( );
+};
+
+// --- Tests ---
+describe("InsightView Component", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ test('renders "no insights found" message when insights array is empty', async () => {
+ // Set up the mock to return an empty array.
+ mockGetEnvironmentInsightsAction.mockResolvedValueOnce({ data: [] });
+ renderComponent();
+ // Wait for the useEffect to complete.
+ await waitFor(() => {
+ expect(screen.getByText("environments.experience.no_insights_found")).toBeInTheDocument();
+ });
+ });
+
+ test("renders table rows when insights are fetched", async () => {
+ // Return two insights for the initial fetch.
+ mockGetEnvironmentInsightsAction.mockResolvedValueOnce({ data: [dummyInsight1, dummyInsight2] });
+ renderComponent();
+ // Wait until the insights are rendered.
+ await waitFor(() => {
+ expect(screen.getByText("Insight 1")).toBeInTheDocument();
+ expect(screen.getByText("Insight 2")).toBeInTheDocument();
+ });
+ });
+
+ test("opens insight sheet when a table row is clicked", async () => {
+ mockGetEnvironmentInsightsAction.mockResolvedValueOnce({ data: [dummyInsight1] });
+ renderComponent();
+ // Wait for the insight to appear.
+ await waitFor(() => {
+ expect(screen.getAllByText("Insight 1").length).toBeGreaterThan(0);
+ });
+
+ // Instead of grabbing the first "Insight 1" cell,
+ // get all table rows (they usually have role="row") and then find the row that contains "Insight 1".
+ const rows = screen.getAllByRole("row");
+ const targetRow = rows.find((row) => row.textContent?.includes("Insight 1"));
+
+ console.log(targetRow?.textContent);
+
+ expect(targetRow).toBeTruthy();
+
+ // Click the entire row.
+ fireEvent.click(targetRow!);
+
+ // Wait for the InsightSheet to update.
+ await waitFor(() => {
+ const sheet = screen.getAllByTestId("insight-sheet");
+
+ const matchingSheet = sheet.find((s) => s.textContent?.includes("InsightSheet Open - Insight 1"));
+ expect(matchingSheet).toBeInTheDocument();
+ });
+ });
+
+ test("clicking load more fetches next page of insights", async () => {
+ // First fetch returns two insights.
+ mockGetEnvironmentInsightsAction.mockResolvedValueOnce({ data: [dummyInsight1, dummyInsight2] });
+ // Second fetch returns one additional insight.
+ mockGetEnvironmentInsightsAction.mockResolvedValueOnce({ data: [dummyInsightPraise] });
+ renderComponent();
+
+ // Wait for the initial insights to be rendered.
+ await waitFor(() => {
+ expect(screen.getAllByText("Insight 1").length).toBeGreaterThan(0);
+ expect(screen.getAllByText("Insight 2").length).toBeGreaterThan(0);
+ });
+
+ // The load more button should be visible because hasMore is true.
+ const loadMoreButton = screen.getAllByText("common.load_more")[0];
+ fireEvent.click(loadMoreButton);
+
+ // Wait for the new insight to be appended.
+ await waitFor(() => {
+ expect(screen.getAllByText("Praise Insight").length).toBeGreaterThan(0);
+ });
+ });
+
+ test("changes filter tab and re-fetches insights", async () => {
+ // For initial active tab "featureRequest", return a featureRequest insight.
+ mockGetEnvironmentInsightsAction.mockResolvedValueOnce({ data: [dummyInsight1] });
+ renderComponent();
+ await waitFor(() => {
+ expect(screen.getAllByText("Insight 1")[0]).toBeInTheDocument();
+ });
+
+ mockGetEnvironmentInsightsAction.mockResolvedValueOnce({
+ data: [dummyInsightComplaint as TInsightWithDocumentCount],
+ });
+
+ renderComponent();
+
+ // Find the complaint tab and click it.
+ const complaintTab = screen.getAllByText("environments.experience.complaint")[0];
+ fireEvent.click(complaintTab);
+
+ // Wait until the new complaint insight is rendered.
+ await waitFor(() => {
+ expect(screen.getAllByText("Complaint Insight")[0]).toBeInTheDocument();
+ });
+ });
+
+ test("shows loading indicator when fetching insights", async () => {
+ // Make the mock return a promise that doesn't resolve immediately.
+ let resolveFetch: any;
+ const fetchPromise = new Promise((resolve) => {
+ resolveFetch = resolve;
+ });
+ mockGetEnvironmentInsightsAction.mockReturnValueOnce(fetchPromise);
+ renderComponent();
+
+ // While fetching, the loading indicator should be visible.
+ expect(screen.getByTestId("insight-loading")).toBeInTheDocument();
+
+ // Resolve the fetch.
+ resolveFetch({ data: [dummyInsight1] });
+ await waitFor(() => {
+ // After fetching, the loading indicator should disappear.
+ expect(screen.queryByTestId("insight-loading")).not.toBeInTheDocument();
+ // Instead of getByText, use getAllByText to assert at least one instance of "Insight 1" exists.
+ expect(screen.getAllByText("Insight 1").length).toBeGreaterThan(0);
+ });
+ });
+});
diff --git a/apps/web/modules/ee/role-management/lib/invite.ts b/apps/web/modules/ee/role-management/lib/invite.ts
index 4b5f0124ef..d00e63f3b1 100644
--- a/apps/web/modules/ee/role-management/lib/invite.ts
+++ b/apps/web/modules/ee/role-management/lib/invite.ts
@@ -2,6 +2,7 @@ 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 { PrismaErrorType } from "@formbricks/database/types/error";
import { ResourceNotFoundError } from "@formbricks/types/errors";
export const updateInvite = async (inviteId: string, data: TInviteUpdateInput): Promise => {
@@ -22,7 +23,10 @@ export const updateInvite = async (inviteId: string, data: TInviteUpdateInput):
return true;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.RecordDoesNotExist
+ ) {
throw new ResourceNotFoundError("Invite", inviteId);
} else {
throw error; // Re-throw any other errors
diff --git a/apps/web/modules/ee/role-management/lib/membership.ts b/apps/web/modules/ee/role-management/lib/membership.ts
index ee0803f21c..d631455cd0 100644
--- a/apps/web/modules/ee/role-management/lib/membership.ts
+++ b/apps/web/modules/ee/role-management/lib/membership.ts
@@ -3,6 +3,7 @@ import { membershipCache } from "@/lib/cache/membership";
import { teamCache } from "@/lib/cache/team";
import { Prisma } from "@prisma/client";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { organizationCache } from "@formbricks/lib/organization/cache";
import { projectCache } from "@formbricks/lib/project/cache";
import { validateInputs } from "@formbricks/lib/utils/validate";
@@ -91,7 +92,11 @@ export const updateMembership = async (
return membership;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ (error.code === PrismaErrorType.RecordDoesNotExist ||
+ error.code === PrismaErrorType.RelatedRecordDoesNotExist)
+ ) {
throw new ResourceNotFoundError("Membership", `userId: ${userId}, organizationId: ${organizationId}`);
}
diff --git a/apps/web/modules/ee/whitelabel/email-customization/lib/organization.ts b/apps/web/modules/ee/whitelabel/email-customization/lib/organization.ts
index 105be391f9..2fb163ec60 100644
--- a/apps/web/modules/ee/whitelabel/email-customization/lib/organization.ts
+++ b/apps/web/modules/ee/whitelabel/email-customization/lib/organization.ts
@@ -2,6 +2,7 @@ import "server-only";
import { Prisma } from "@prisma/client";
import { cache as reactCache } from "react";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { cache } from "@formbricks/lib/cache";
import { organizationCache } from "@formbricks/lib/organization/cache";
import { projectCache } from "@formbricks/lib/project/cache";
@@ -64,7 +65,10 @@ export const updateOrganizationEmailLogoUrl = async (
return true;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.RecordDoesNotExist
+ ) {
throw new ResourceNotFoundError("Organization", organizationId);
}
@@ -125,7 +129,10 @@ export const removeOrganizationEmailLogoUrl = async (organizationId: string): Pr
return true;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.RecordDoesNotExist
+ ) {
throw new ResourceNotFoundError("Organization", organizationId);
}
diff --git a/apps/web/modules/integrations/webhooks/lib/webhook.ts b/apps/web/modules/integrations/webhooks/lib/webhook.ts
index dcab09ce28..1eced5881b 100644
--- a/apps/web/modules/integrations/webhooks/lib/webhook.ts
+++ b/apps/web/modules/integrations/webhooks/lib/webhook.ts
@@ -2,6 +2,7 @@ import { webhookCache } from "@/lib/cache/webhook";
import { isDiscordWebhook } from "@/modules/integrations/webhooks/lib/utils";
import { Prisma, Webhook } from "@prisma/client";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { cache } from "@formbricks/lib/cache";
import { validateInputs } from "@formbricks/lib/utils/validate";
import { ZId } from "@formbricks/types/common";
@@ -62,7 +63,10 @@ export const deleteWebhook = async (id: string): Promise => {
return true;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2025") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.RelatedRecordDoesNotExist
+ ) {
throw new ResourceNotFoundError("Webhook", id);
}
throw new DatabaseError(`Database error when deleting webhook with ID ${id}`);
diff --git a/apps/web/modules/projects/settings/(setup)/components/setup-instructions.test.tsx b/apps/web/modules/projects/settings/(setup)/components/setup-instructions.test.tsx
new file mode 100644
index 0000000000..58aead244e
--- /dev/null
+++ b/apps/web/modules/projects/settings/(setup)/components/setup-instructions.test.tsx
@@ -0,0 +1,93 @@
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
+import React from "react";
+import { beforeEach, describe, expect, test, vi } from "vitest";
+import { SetupInstructions } from "./setup-instructions";
+
+// Mock the translation hook to simply return the key.
+vi.mock("@tolgee/react", () => ({
+ useTranslate: () => ({
+ t: (key: string) => key,
+ }),
+}));
+
+// Mock the TabBar component.
+vi.mock("@/modules/ui/components/tab-bar", () => ({
+ TabBar: ({ tabs, setActiveId }: any) => (
+
+ {tabs.map((tab: any) => (
+ setActiveId(tab.id)}>
+ {tab.label}
+
+ ))}
+
+ ),
+}));
+
+// Mock the CodeBlock component.
+vi.mock("@/modules/ui/components/code-block", () => ({
+ CodeBlock: ({ children }: { children: React.ReactNode; language?: string }) => (
+ {children}
+ ),
+}));
+
+// Mock Next.js Link to simply render an anchor.
+vi.mock("next/link", () => {
+ return {
+ default: ({ children, href }: { children: React.ReactNode; href: string }) => (
+ {children}
+ ),
+ };
+});
+
+describe("SetupInstructions Component", () => {
+ const environmentId = "env123";
+ const webAppUrl = "https://example.com";
+
+ beforeEach(() => {
+ // Optionally reset mocks if needed
+ vi.clearAllMocks();
+ });
+
+ test("renders npm instructions by default", () => {
+ render( );
+
+ // Verify that the npm tab is active by default by checking for a code block with npm install instructions.
+ expect(screen.getByText("pnpm install @formbricks/js")).toBeInTheDocument();
+
+ // Verify that the TabBar renders both "NPM" and "HTML" buttons.
+ expect(screen.getByRole("button", { name: /NPM/i })).toBeInTheDocument();
+ expect(screen.getByRole("button", { name: /HTML/i })).toBeInTheDocument();
+ });
+
+ test("switches to html tab and displays html instructions", async () => {
+ render( );
+
+ // Instead of getByRole (which finds multiple buttons), use getAllByRole and select the first HTML tab.
+ const htmlTabButtons = screen.getAllByRole("button", { name: /HTML/i });
+ expect(htmlTabButtons.length).toBeGreaterThan(0);
+ const htmlTabButton = htmlTabButtons[0];
+
+ fireEvent.click(htmlTabButton);
+
+ // Wait for the HTML instructions to appear.
+ await waitFor(() => {
+ expect(screen.getByText(//i)).toBeInTheDocument();
+ });
+ });
+
+ test("npm instructions code block contains environmentId and webAppUrl", async () => {
+ render( );
+
+ // The NPM tab is the default view.
+ // Find all code block elements.
+ const codeBlocks = screen.getAllByTestId("code-block");
+ // The setup code block (language "js") should include the environmentId and webAppUrl.
+ // We filter for the one containing 'formbricks.setup' and our environment values.
+ const setupCodeBlock = codeBlocks.find(
+ (block) => block.textContent?.includes("formbricks.setup") && block.textContent?.includes(environmentId)
+ );
+ expect(setupCodeBlock).toBeDefined();
+ expect(setupCodeBlock?.textContent).toContain(environmentId);
+ expect(setupCodeBlock?.textContent).toContain(webAppUrl);
+ });
+});
diff --git a/apps/web/modules/projects/settings/lib/project.ts b/apps/web/modules/projects/settings/lib/project.ts
index d0733b3d21..933a7c50d7 100644
--- a/apps/web/modules/projects/settings/lib/project.ts
+++ b/apps/web/modules/projects/settings/lib/project.ts
@@ -2,6 +2,7 @@ import "server-only";
import { Prisma } from "@prisma/client";
import { z } from "zod";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { isS3Configured } from "@formbricks/lib/constants";
import { environmentCache } from "@formbricks/lib/environment/cache";
import { createEnvironment } from "@formbricks/lib/environment/service";
@@ -140,12 +141,15 @@ export const createProject = async (
return updatedProject;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.UniqueConstraintViolation
+ ) {
throw new InvalidInputError("A project with this name already exists in your organization");
}
if (error instanceof Prisma.PrismaClientKnownRequestError) {
- if (error.code === "P2002") {
+ if (error.code === PrismaErrorType.UniqueConstraintViolation) {
throw new InvalidInputError("A project with this name already exists in this organization");
}
throw new DatabaseError(error.message);
diff --git a/apps/web/modules/survey/components/template-list/lib/user.ts b/apps/web/modules/survey/components/template-list/lib/user.ts
index 6cf63a4138..8719847c1a 100644
--- a/apps/web/modules/survey/components/template-list/lib/user.ts
+++ b/apps/web/modules/survey/components/template-list/lib/user.ts
@@ -1,9 +1,9 @@
import { Prisma } from "@prisma/client";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { userCache } from "@formbricks/lib/user/cache";
import { ResourceNotFoundError } from "@formbricks/types/errors";
-import { TUser } from "@formbricks/types/user";
-import { TUserUpdateInput } from "@formbricks/types/user";
+import { TUser, TUserUpdateInput } from "@formbricks/types/user";
// function to update a user's user
export const updateUser = async (personId: string, data: TUserUpdateInput): Promise => {
@@ -37,7 +37,10 @@ export const updateUser = async (personId: string, data: TUserUpdateInput): Prom
return updatedUser;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.RecordDoesNotExist
+ ) {
throw new ResourceNotFoundError("User", personId);
}
throw error; // Re-throw any other errors
diff --git a/apps/web/modules/survey/editor/lib/action-class.ts b/apps/web/modules/survey/editor/lib/action-class.ts
index e84e0e0535..0962aba29a 100644
--- a/apps/web/modules/survey/editor/lib/action-class.ts
+++ b/apps/web/modules/survey/editor/lib/action-class.ts
@@ -1,5 +1,6 @@
import { ActionClass, Prisma } from "@prisma/client";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { actionClassCache } from "@formbricks/lib/actionClass/cache";
import { TActionClassInput } from "@formbricks/types/action-classes";
import { DatabaseError } from "@formbricks/types/errors";
@@ -28,7 +29,10 @@ export const createActionClass = async (
return actionClassPrisma;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.UniqueConstraintViolation
+ ) {
throw new DatabaseError(
`Action with ${error.meta?.target?.[0]} ${actionClass[error.meta?.target?.[0]]} already exists`
);
diff --git a/apps/web/modules/survey/link/components/link-survey.test.tsx b/apps/web/modules/survey/link/components/link-survey.test.tsx
new file mode 100644
index 0000000000..5651be4964
--- /dev/null
+++ b/apps/web/modules/survey/link/components/link-survey.test.tsx
@@ -0,0 +1,217 @@
+import * as utils from "@/modules/survey/link/lib/utils";
+import { render, screen, waitFor } from "@testing-library/react";
+import * as navigation from "next/navigation";
+import React from "react";
+import { beforeEach, describe, expect, test, vi } from "vitest";
+import type { TResponseData } from "@formbricks/types/responses";
+import { TSurvey } from "@formbricks/types/surveys/types";
+import { LinkSurvey } from "./link-survey";
+
+// Allow tests to control search params via a module-level variable.
+let searchParamsValue = new URLSearchParams();
+vi.mock("next/navigation", () => ({
+ useSearchParams: () => searchParamsValue,
+}));
+
+// Stub getPrefillValue to return a dummy prefill value.
+vi.mock("@/modules/survey/link/lib/utils", () => ({
+ getPrefillValue: vi.fn(() => ({ prefilled: "dummy" })),
+}));
+
+// Mock LinkSurveyWrapper as a simple wrapper that renders its children.
+vi.mock("@/modules/survey/link/components/link-survey-wrapper", () => ({
+ LinkSurveyWrapper: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+}));
+
+// Mock SurveyLinkUsed to render a div with a test id.
+vi.mock("@/modules/survey/link/components/survey-link-used", () => ({
+ SurveyLinkUsed: ({ singleUseMessage }: { singleUseMessage: string }) => (
+ SurveyLinkUsed: {singleUseMessage}
+ ),
+}));
+
+// Mock VerifyEmail to render a div that indicates if it's an error or not.
+vi.mock("@/modules/survey/link/components/verify-email", () => ({
+ VerifyEmail: (props: any) => (
+ VerifyEmail {props.isErrorComponent ? "Error" : ""}
+ ),
+}));
+
+// Mock SurveyInline to display key props so we can inspect them.
+vi.mock("@/modules/ui/components/survey", () => ({
+ SurveyInline: (props: any) => (
+
+ SurveyInline {props.startAtQuestionId ? `StartAt:${props.startAtQuestionId}` : ""}
+ {props.autoFocus ? " AutoFocus" : ""}
+ {props.hiddenFieldsRecord ? ` HiddenFields:${JSON.stringify(props.hiddenFieldsRecord)}` : ""}
+ {props.prefillResponseData ? ` Prefill:${JSON.stringify(props.prefillResponseData)}` : ""}
+
+ ),
+}));
+
+// --- Dummy Data ---
+
+const dummySurvey = {
+ id: "survey1",
+ type: "link",
+ environmentId: "env1",
+ welcomeCard: { enabled: true },
+ questions: [{ id: "q1" }, { id: "q2" }],
+ isVerifyEmailEnabled: false,
+ hiddenFields: { fieldIds: ["hidden1"] },
+ singleUse: "Single Use Message",
+ styling: { overwriteThemeStyling: false },
+} as unknown as TSurvey;
+
+const dummyProject = {
+ styling: { allowStyleOverwrite: false },
+ logo: "logo.png",
+ linkSurveyBranding: true,
+};
+
+const dummySingleUseResponse = {
+ id: "r1",
+ finished: true,
+};
+
+// --- Helper to render the component with default props ---
+const renderComponent = (props: Partial> = {}) => {
+ // Reset search params to an empty state for each test.
+ searchParamsValue = new URLSearchParams("");
+ const defaultProps = {
+ survey: dummySurvey,
+ project: dummyProject,
+ emailVerificationStatus: "verified",
+ singleUseId: "single-use-123",
+ webAppUrl: "https://example.com",
+ responseCount: 0,
+ languageCode: "en",
+ isEmbed: false,
+ IMPRINT_URL: "https://example.com/imprint",
+ PRIVACY_URL: "https://example.com/privacy",
+ IS_FORMBRICKS_CLOUD: false,
+ locale: "en",
+ isPreview: false,
+ };
+ return render( );
+};
+
+// --- Test Suite ---
+describe("LinkSurvey Component", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ test("renders SurveyLinkUsed when singleUseResponse is finished", () => {
+ renderComponent({ singleUseResponse: dummySingleUseResponse });
+ expect(screen.getByTestId("survey-link-used")).toBeInTheDocument();
+ expect(screen.getByText(/SurveyLinkUsed:/)).toHaveTextContent("Single Use Message");
+ });
+
+ test("renders VerifyEmail error component when emailVerificationStatus is fishy", () => {
+ // Set up survey with email verification enabled.
+ const survey = { ...dummySurvey, isVerifyEmailEnabled: true };
+ renderComponent({ survey, emailVerificationStatus: "fishy" });
+ const verifyEmail = screen.getByTestId("verify-email");
+ expect(verifyEmail).toBeInTheDocument();
+ expect(verifyEmail).toHaveTextContent("Error");
+ });
+
+ test("renders VerifyEmail component when emailVerificationStatus is not-verified", () => {
+ const survey = { ...dummySurvey, isVerifyEmailEnabled: true };
+ renderComponent({ survey, emailVerificationStatus: "not-verified" });
+ // Get all rendered VerifyEmail components.
+ const verifyEmailElements = screen.getAllByTestId("verify-email");
+ // Filter out the ones that have "Error" in their text.
+ const nonErrorVerifyEmail = verifyEmailElements.filter((el) => !el.textContent?.includes("Error"));
+ expect(nonErrorVerifyEmail.length).toBeGreaterThan(0);
+ });
+
+ test("renders LinkSurveyWrapper and SurveyInline when conditions are met", async () => {
+ // Use a survey that does not require email verification and is not single-use finished.
+ // Also provide a startAt query param and a hidden field.
+
+ const mockUseSearchParams = vi.spyOn(navigation, "useSearchParams");
+ const mockGetPrefillValue = vi.spyOn(utils, "getPrefillValue");
+
+ mockUseSearchParams.mockReturnValue(
+ new URLSearchParams("startAt=q1&hidden1=value1") as unknown as navigation.ReadonlyURLSearchParams
+ );
+
+ mockGetPrefillValue.mockReturnValue({ prefilled: "dummy" });
+
+ renderComponent();
+ // Check that the LinkSurveyWrapper is rendered.
+ expect(screen.getByTestId("link-survey-wrapper")).toBeInTheDocument();
+ // Check that SurveyInline is rendered.
+ const surveyInline = screen.getByTestId("survey-inline");
+ expect(surveyInline).toBeInTheDocument();
+
+ // Verify that startAtQuestionId is passed when valid.
+ expect(surveyInline).toHaveTextContent("StartAt:q1");
+ // Verify that prefillResponseData is passed (from getPrefillValue mock).
+ expect(surveyInline).toHaveTextContent('Prefill:{"prefilled":"dummy"}');
+ // Verify that hiddenFieldsRecord includes the hidden field value.
+ expect(surveyInline).toHaveTextContent('HiddenFields:{"hidden1":"value1"}');
+ });
+
+ test("sets autoFocus to true when not in an iframe", async () => {
+ // In the test environment, window.self === window.top.
+ renderComponent();
+ const surveyInlineElements = screen.getAllByTestId("survey-inline");
+
+ await waitFor(() => {
+ surveyInlineElements.forEach((el) => {
+ expect(el).toHaveTextContent("AutoFocus");
+ });
+ });
+ });
+
+ test("includes verifiedEmail in hiddenFieldsRecord when survey verifies email", () => {
+ const survey = { ...dummySurvey, isVerifyEmailEnabled: true };
+ renderComponent({ survey, emailVerificationStatus: "verified", verifiedEmail: "test@example.com" });
+ const surveyInlineElements = screen.getAllByTestId("survey-inline");
+
+ // Find the instance that includes the verifiedEmail in its hiddenFieldsRecord
+ const withVerifiedEmail = surveyInlineElements.find((el) =>
+ el.textContent?.includes('"verifiedEmail":"test@example.com"')
+ );
+
+ expect(withVerifiedEmail).toBeDefined();
+ });
+
+ test("handleResetSurvey sets questionId and resets response data", () => {
+ // We will capture the functions that LinkSurvey passes via getSetQuestionId and getSetResponseData.
+ let capturedSetQuestionId: (value: string) => void = () => {};
+ let capturedSetResponseData: (value: TResponseData) => void = () => {};
+ // Override our SurveyInline mock to capture the props.
+ vi.doMock("@/modules/ui/components/survey", () => ({
+ SurveyInline: (props: any) => {
+ capturedSetQuestionId = props.getSetQuestionId;
+ capturedSetResponseData = props.getSetResponseData;
+ return (
+
+ SurveyInline {props.startAtQuestionId ? `StartAt:${props.startAtQuestionId}` : ""}
+
+ );
+ },
+ }));
+ // Re-import LinkSurvey to pick up the new mock (if necessary).
+ // For this example, assume our mock is used.
+
+ renderComponent();
+ // Simulate calling the captured functions by invoking the handleResetSurvey function indirectly.
+ // In the component, handleResetSurvey is passed to LinkSurveyWrapper.
+ // We can obtain it by accessing the LinkSurveyWrapper's props.
+ // For simplicity, call the captured functions directly:
+ capturedSetQuestionId("start");
+ capturedSetResponseData({});
+
+ // Now, verify that the captured functions work as expected.
+ // (In a real app, these functions would update state in LinkSurvey; here, we can only ensure they are callable.)
+ expect(typeof capturedSetQuestionId).toBe("function");
+ expect(typeof capturedSetResponseData).toBe("function");
+ });
+});
diff --git a/apps/web/modules/survey/link/lib/survey.ts b/apps/web/modules/survey/link/lib/survey.ts
index 7a54acd089..1185de0151 100644
--- a/apps/web/modules/survey/link/lib/survey.ts
+++ b/apps/web/modules/survey/link/lib/survey.ts
@@ -32,7 +32,7 @@ export const getSurveyMetadata = reactCache(async (surveyId: string) =>
return survey;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
- logger.error(error, "Error getting survey metadata");
+ logger.error(error);
throw new DatabaseError(error.message);
}
throw error;
diff --git a/apps/web/modules/ui/components/alert/index.test.tsx b/apps/web/modules/ui/components/alert/index.test.tsx
new file mode 100644
index 0000000000..08c218f24b
--- /dev/null
+++ b/apps/web/modules/ui/components/alert/index.test.tsx
@@ -0,0 +1,74 @@
+import { fireEvent, render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import { Alert, AlertButton, AlertDescription, AlertTitle } from "./index";
+
+describe("Alert", () => {
+ it("renders with default variant", () => {
+ render(
+
+ Test Title
+ Test Description
+
+ );
+
+ expect(screen.getByRole("alert")).toBeInTheDocument();
+ expect(screen.getByText("Test Title")).toBeInTheDocument();
+ expect(screen.getByText("Test Description")).toBeInTheDocument();
+ });
+
+ it("renders with different variants", () => {
+ const variants = ["default", "error", "warning", "info", "success"] as const;
+
+ variants.forEach((variant) => {
+ const { container } = render(
+
+ Test Title
+
+ );
+
+ expect(container.firstChild).toHaveClass(
+ variant === "default" ? "text-foreground" : `text-${variant}-foreground`
+ );
+ });
+ });
+
+ it("renders with different sizes", () => {
+ const sizes = ["default", "small"] as const;
+
+ sizes.forEach((size) => {
+ const { container } = render(
+
+ Test Title
+
+ );
+
+ expect(container.firstChild).toHaveClass(size === "default" ? "py-3" : "py-2");
+ });
+ });
+
+ it("renders with button and handles click", () => {
+ const handleClick = vi.fn();
+
+ render(
+
+ Test Title
+ Click me
+
+ );
+
+ const button = screen.getByText("Click me");
+ expect(button).toBeInTheDocument();
+ fireEvent.click(button);
+ expect(handleClick).toHaveBeenCalledTimes(1);
+ });
+
+ it("applies custom className", () => {
+ const { container } = render(
+
+ Test Title
+
+ );
+
+ expect(container.firstChild).toHaveClass("custom-class");
+ });
+});
diff --git a/apps/web/modules/ui/components/alert/index.tsx b/apps/web/modules/ui/components/alert/index.tsx
index d9e3c89c6e..c17f7f4b4e 100644
--- a/apps/web/modules/ui/components/alert/index.tsx
+++ b/apps/web/modules/ui/components/alert/index.tsx
@@ -1,49 +1,141 @@
-import { VariantProps, cva } from "class-variance-authority";
-import * as React from "react";
-import { cn } from "@formbricks/lib/cn";
+"use client";
-const alertVariants = cva(
- "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
- {
- variants: {
- variant: {
- default: "bg-background text-foreground",
- destructive:
- "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
- info: "text-slate-800 bg-brand/5",
- warning: "text-yellow-700 bg-yellow-50",
- error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
- },
+import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
+import * as React from "react";
+import { createContext, useContext } from "react";
+import { cn } from "@formbricks/lib/cn";
+import { Button, ButtonProps } from "../button";
+
+// Create a context to share variant and size with child components
+interface AlertContextValue {
+ variant?: "default" | "error" | "warning" | "info" | "success" | null;
+ size?: "default" | "small" | null;
+}
+
+const AlertContext = createContext({
+ variant: "default",
+ size: "default",
+});
+
+const useAlertContext = () => useContext(AlertContext);
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-lg border [&>svg]:size-4 [&>svg]:text-foreground", {
+ variants: {
+ variant: {
+ default: "text-foreground border-border",
+ error:
+ "text-error-foreground border-error/50 [&_button]:bg-error-background [&_button]:text-error-foreground [&_button:hover]:bg-error-background-muted",
+ warning:
+ "text-warning-foreground border-warning/50 [&_button]:bg-warning-background [&_button]:text-warning-foreground [&_button:hover]:bg-warning-background-muted",
+ info: "text-info-foreground border-info/50 [&_button]:bg-info-background [&_button]:text-info-foreground [&_button:hover]:bg-info-background-muted",
+ success:
+ "text-success-foreground border-success/50 [&_button]:bg-success-background [&_button]:text-success-foreground [&_button:hover]:bg-success-background-muted",
},
- defaultVariants: {
- variant: "default",
+ size: {
+ default:
+ "py-3 px-4 text-sm grid grid-cols-[1fr_auto] grid-rows-[auto_auto] gap-x-3 [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7",
+ small:
+ "px-3 py-2 text-xs flex items-center justify-between gap-2 [&>svg]:flex-shrink-0 [&_button]:text-xs [&_button]:bg-transparent [&_button:hover]:bg-transparent [&>svg~*]:pl-0",
},
- }
-);
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+});
+
+const alertVariantIcons: Record<"default" | "error" | "warning" | "info" | "success", React.ReactNode> = {
+ default: null,
+ error: ,
+ warning: ,
+ info: ,
+ success: ,
+};
const Alert = React.forwardRef<
HTMLDivElement,
- React.HTMLAttributes &
- VariantProps & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, variant, ...props }, ref) => (
-
-));
+ React.HTMLAttributes & VariantProps
+>(({ className, variant, size, ...props }, ref) => {
+ const variantIcon = variant ? (variant !== "default" ? alertVariantIcons[variant] : null) : null;
+
+ return (
+
+
+ {variantIcon}
+ {props.children}
+
+
+ );
+});
Alert.displayName = "Alert";
-const AlertTitle = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, ...props }, ref) => (
-
-));
+const AlertTitle = React.forwardRef>(
+ ({ className, ...props }, ref) => {
+ const { size } = useAlertContext();
+ return (
+
+ );
+ }
+);
+
AlertTitle.displayName = "AlertTitle";
-const AlertDescription = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, ...props }, ref) => (
-
-));
+const AlertDescription = React.forwardRef>(
+ ({ className, ...props }, ref) => {
+ const { size } = useAlertContext();
+
+ return (
+
+ );
+ }
+);
AlertDescription.displayName = "AlertDescription";
-export { Alert, AlertDescription, AlertTitle };
+const AlertButton = React.forwardRef(
+ ({ className, variant, size, children, ...props }, ref) => {
+ const { size: alertSize } = useAlertContext();
+
+ // Determine button styling based on alert context
+ const buttonVariant = variant || (alertSize === "small" ? "link" : "secondary");
+ const buttonSize = size || (alertSize === "small" ? "sm" : "default");
+
+ return (
+
+
+ {children}
+
+
+ );
+ }
+);
+
+AlertButton.displayName = "AlertButton";
+
+// Export the new component
+export { Alert, AlertTitle, AlertDescription, AlertButton };
diff --git a/apps/web/modules/ui/components/alert/stories.test.tsx b/apps/web/modules/ui/components/alert/stories.test.tsx
new file mode 100644
index 0000000000..af33eef348
--- /dev/null
+++ b/apps/web/modules/ui/components/alert/stories.test.tsx
@@ -0,0 +1,62 @@
+import { cleanup, render, screen } from "@testing-library/react";
+import { afterEach, describe, expect, it } from "vitest";
+import { Default, Error, Info, Small, Success, Warning, withButtonAndIcon } from "./stories";
+
+describe("Alert Stories", () => {
+ const renderStory = (Story: any) => {
+ return render(Story.render(Story.args));
+ };
+
+ afterEach(() => {
+ cleanup();
+ });
+
+ it("renders Default story", () => {
+ renderStory(Default);
+ expect(screen.getByText("Alert Title")).toBeInTheDocument();
+ expect(screen.getByText("This is an important notification.")).toBeInTheDocument();
+ expect(screen.queryByRole("button")).not.toBeInTheDocument();
+ });
+
+ it("renders Small story", () => {
+ renderStory(Small);
+ expect(screen.getByText("Information Alert")).toBeInTheDocument();
+ expect(screen.getByRole("button")).toBeInTheDocument();
+ expect(screen.getByText("Learn more")).toBeInTheDocument();
+ });
+
+ it("renders withButtonAndIcon story", () => {
+ renderStory(withButtonAndIcon);
+ expect(screen.getByText("Alert Title")).toBeInTheDocument();
+ expect(screen.getByRole("button")).toBeInTheDocument();
+ expect(screen.getByText("Learn more")).toBeInTheDocument();
+ });
+
+ it("renders Error story", () => {
+ renderStory(Error);
+ expect(screen.getByText("Error Alert")).toBeInTheDocument();
+ expect(screen.getByText("Your session has expired. Please log in again.")).toBeInTheDocument();
+ expect(screen.getByText("Log in")).toBeInTheDocument();
+ });
+
+ it("renders Warning story", () => {
+ renderStory(Warning);
+ expect(screen.getByText("Warning Alert")).toBeInTheDocument();
+ expect(screen.getByText("You are editing sensitive data. Be cautious")).toBeInTheDocument();
+ expect(screen.getByText("Proceed")).toBeInTheDocument();
+ });
+
+ it("renders Info story", () => {
+ renderStory(Info);
+ expect(screen.getByText("Info Alert")).toBeInTheDocument();
+ expect(screen.getByText("There was an update to your application.")).toBeInTheDocument();
+ expect(screen.getByText("Refresh")).toBeInTheDocument();
+ });
+
+ it("renders Success story", () => {
+ renderStory(Success);
+ expect(screen.getByText("Success Alert")).toBeInTheDocument();
+ expect(screen.getByText("This worked! Please proceed.")).toBeInTheDocument();
+ expect(screen.getByText("Close")).toBeInTheDocument();
+ });
+});
diff --git a/apps/web/modules/ui/components/alert/stories.tsx b/apps/web/modules/ui/components/alert/stories.tsx
index ff9017829b..4d45ed1edb 100644
--- a/apps/web/modules/ui/components/alert/stories.tsx
+++ b/apps/web/modules/ui/components/alert/stories.tsx
@@ -1,49 +1,252 @@
-import type { Meta, StoryObj } from "@storybook/react";
-import { AlertCircle } from "lucide-react";
-import { Alert, AlertDescription, AlertTitle } from "./index";
+import { Meta, StoryObj } from "@storybook/react";
+import { LightbulbIcon } from "lucide-react";
+import { Alert, AlertButton, AlertDescription, AlertTitle } from "./index";
-const meta = {
- title: "ui/Alert",
+// We'll define the story options separately from the component props
+interface StoryOptions {
+ title: string;
+ description: string;
+ showIcon: boolean;
+ showButton: boolean;
+ actionButtonText: string;
+}
+
+type StoryProps = React.ComponentProps & StoryOptions;
+
+const meta: Meta = {
+ title: "UI/Alert",
component: Alert,
tags: ["autodocs"],
- argTypes: {
- variant: {
- control: "radio",
- options: ["default", "error"],
+ parameters: {
+ controls: {
+ sort: "requiredFirst",
+ exclude: [],
},
},
- args: {
- variant: "default",
+ // These argTypes are for story controls, not component props
+ argTypes: {
+ variant: {
+ control: "select",
+ options: ["default", "error", "warning", "info", "success"],
+ description: "Style variant of the alert",
+ table: {
+ category: "Appearance",
+ type: { summary: "string" },
+ defaultValue: { summary: "default" },
+ },
+ order: 1,
+ },
+ size: {
+ control: "select",
+ options: ["default", "small"],
+ description: "Size of the alert component",
+ table: {
+ category: "Appearance",
+ type: { summary: "string" },
+ defaultValue: { summary: "default" },
+ },
+ order: 2,
+ },
+ showIcon: {
+ control: "boolean",
+ description: "Whether to show an icon",
+ table: {
+ category: "Appearance",
+ type: { summary: "boolean" },
+ },
+ order: 3,
+ },
+ showButton: {
+ control: "boolean",
+ description: "Whether to show action buttons",
+ table: {
+ category: "Appearance",
+ type: { summary: "boolean" },
+ },
+ order: 4,
+ },
+ title: {
+ control: "text",
+ description: "Alert title text",
+ table: {
+ category: "Content",
+ type: { summary: "string" },
+ },
+ order: 1,
+ },
+ description: {
+ control: "text",
+ description: "Alert description text",
+ table: {
+ category: "Content",
+ type: { summary: "string" },
+ },
+ order: 2,
+ },
+ actionButtonText: {
+ control: "text",
+ description: "Text for the action button",
+ table: {
+ category: "Content",
+ type: { summary: "string" },
+ },
+ order: 2,
+ },
},
- render: (args) => (
-
- This is an alert
- This is a description
-
- ),
-} satisfies Meta;
+};
export default meta;
-type Story = StoryObj;
+// Our story type just specifies Alert props plus our story options
+type Story = StoryObj & { args: StoryOptions };
-export const Default: Story = {};
+// Create a common render function to reduce duplication
+const renderAlert = (args: StoryProps) => {
+ // Extract component props
+ const { variant = "default", size = "default", className = "" } = args;
-export const Error: Story = {
- args: {
- variant: "error",
- },
-};
+ // Extract story content options
+ const {
+ title = "",
+ description = "",
+ showIcon = false,
+ showButton = false,
+ actionButtonText = "",
+ } = args as StoryOptions;
-export const WithIcon: Story = {
- args: {
- variant: "error",
- },
- render: (args) => (
-
-
- This is an alert
- This is a description
+ return (
+
+ {showIcon && }
+ {title}
+ {description && {description} }
+ {showButton && alert("Button clicked")}>{actionButtonText} }
- ),
+ );
+};
+
+// Basic example with direct props
+export const Default: Story = {
+ render: renderAlert,
+ args: {
+ variant: "default",
+ showIcon: false,
+ showButton: false,
+ title: "Alert Title",
+ description: "This is an important notification.",
+ actionButtonText: "Learn more",
+ },
+};
+
+// Small size example
+export const Small: Story = {
+ render: renderAlert,
+ args: {
+ variant: "default",
+ size: "small",
+ title: "Information Alert",
+ description: "This is an important notification.",
+ showIcon: false,
+ showButton: true,
+ actionButtonText: "Learn more",
+ },
+ parameters: {
+ docs: {
+ description: {
+ story: "Use if space is limited or the alert is not the main focus.",
+ },
+ },
+ },
+};
+
+// With custom icon
+export const withButtonAndIcon: Story = {
+ render: renderAlert,
+ args: {
+ variant: "default",
+ title: "Alert Title",
+ description: "This is an important notification.",
+ showIcon: true,
+ showButton: true,
+ actionButtonText: "Learn more",
+ },
+};
+
+// Error variant
+export const Error: Story = {
+ render: renderAlert,
+ args: {
+ variant: "error",
+ title: "Error Alert",
+ description: "Your session has expired. Please log in again.",
+ showIcon: false,
+ showButton: true,
+ actionButtonText: "Log in",
+ },
+ parameters: {
+ docs: {
+ description: {
+ story: "Only use if the user needs to take immediate action or there is a critical error.",
+ },
+ },
+ },
+};
+
+// Warning variant
+export const Warning: Story = {
+ render: renderAlert,
+ args: {
+ variant: "warning",
+ title: "Warning Alert",
+ description: "You are editing sensitive data. Be cautious",
+ showIcon: false,
+ showButton: true,
+ actionButtonText: "Proceed",
+ },
+ parameters: {
+ docs: {
+ description: {
+ story: "Use this to make the user aware of potential issues.",
+ },
+ },
+ },
+};
+
+// Info variant
+export const Info: Story = {
+ render: renderAlert,
+ args: {
+ variant: "info",
+ title: "Info Alert",
+ description: "There was an update to your application.",
+ showIcon: false,
+ showButton: true,
+ actionButtonText: "Refresh",
+ },
+ parameters: {
+ docs: {
+ description: {
+ story: "Use this to give contextual information and support the user.",
+ },
+ },
+ },
+};
+
+// Success variant
+export const Success: Story = {
+ render: renderAlert,
+ args: {
+ variant: "success",
+ title: "Success Alert",
+ description: "This worked! Please proceed.",
+ showIcon: false,
+ showButton: true,
+ actionButtonText: "Close",
+ },
+ parameters: {
+ docs: {
+ description: {
+ story: "Use this to give positive feedback.",
+ },
+ },
+ },
};
diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs
index eb1eed2cfa..7eb8d1a5f8 100644
--- a/apps/web/next.config.mjs
+++ b/apps/web/next.config.mjs
@@ -321,7 +321,7 @@ const sentryConfig = {
disableLogger: true,
};
-const exportConfig = process.env.NEXT_PUBLIC_SENTRY_DSN
+const exportConfig = process.env.SENTRY_DSN
? withSentryConfig(nextConfig, sentryOptions)
: nextConfig;
diff --git a/apps/web/package.json b/apps/web/package.json
index ed460a07de..d7fc3b61f8 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -155,7 +155,7 @@
"@types/qrcode": "1.5.5",
"@types/testing-library__react": "10.2.0",
"@vitest/coverage-v8": "2.1.8",
- "vite": "6.2.0",
+ "vite": "6.2.3",
"vite-tsconfig-paths": "5.1.4",
"vitest": "3.0.7",
"vitest-mock-extended": "2.0.2"
diff --git a/apps/web/playwright/api/constants.ts b/apps/web/playwright/api/constants.ts
index 6f59359be4..904500b1e6 100644
--- a/apps/web/playwright/api/constants.ts
+++ b/apps/web/playwright/api/constants.ts
@@ -1,2 +1,3 @@
export const RESPONSES_API_URL = `/api/v2/management/responses`;
export const SURVEYS_API_URL = `/api/v1/management/surveys`;
+export const WEBHOOKS_API_URL = `/api/v2/management/webhooks`;
diff --git a/apps/web/playwright/api/management/responses.spec.ts b/apps/web/playwright/api/management/response.spec.ts
similarity index 100%
rename from apps/web/playwright/api/management/responses.spec.ts
rename to apps/web/playwright/api/management/response.spec.ts
diff --git a/apps/web/playwright/api/management/webhook.spec.ts b/apps/web/playwright/api/management/webhook.spec.ts
new file mode 100644
index 0000000000..d0b86c7296
--- /dev/null
+++ b/apps/web/playwright/api/management/webhook.spec.ts
@@ -0,0 +1,137 @@
+import { SURVEYS_API_URL, WEBHOOKS_API_URL } from "@/playwright/api/constants";
+import { expect } from "@playwright/test";
+import { logger } from "@formbricks/logger";
+import { test } from "../../lib/fixtures";
+import { loginAndGetApiKey } from "../../lib/utils";
+
+test.describe("API Tests for Webhooks", () => {
+ test("Create, Retrieve, Update, and Delete Webhooks via API", async ({ page, users, request }) => {
+ let environmentId, apiKey;
+
+ try {
+ ({ environmentId, apiKey } = await loginAndGetApiKey(page, users));
+ } catch (error) {
+ logger.error(error, "Error during login and getting API key");
+ throw error;
+ }
+
+ let surveyId: string;
+
+ await test.step("Create Survey via API", async () => {
+ const surveyBody = {
+ environmentId: environmentId,
+ type: "link",
+ name: "My new Survey from API",
+ questions: [
+ {
+ id: "jpvm9b73u06xdrhzi11k2h76",
+ type: "openText",
+ headline: {
+ default: "What would you like to know?",
+ },
+ required: true,
+ inputType: "text",
+ subheader: {
+ default: "This is an example survey.",
+ },
+ placeholder: {
+ default: "Type your answer here...",
+ },
+ charLimit: {
+ enabled: false,
+ },
+ },
+ ],
+ };
+
+ const response = await request.post(SURVEYS_API_URL, {
+ headers: {
+ "Content-Type": "application/json",
+ "x-api-key": apiKey,
+ },
+ data: surveyBody,
+ });
+
+ expect(response.ok()).toBe(true);
+ const responseBody = await response.json();
+ expect(responseBody.data.name).toEqual("My new Survey from API");
+ surveyId = responseBody.data.id;
+ });
+
+ let createdWebhookId: string;
+
+ await test.step("Create Webhook via API", async () => {
+ const webhookBody = {
+ environmentId,
+ name: "New Webhook",
+ url: "https://examplewebhook.com",
+ source: "user",
+ triggers: ["responseFinished"],
+ surveyIds: [surveyId],
+ };
+
+ const response = await request.post(WEBHOOKS_API_URL, {
+ headers: {
+ "Content-Type": "application/json",
+ "x-api-key": apiKey,
+ },
+ data: webhookBody,
+ });
+
+ expect(response.ok()).toBe(true);
+ const responseBody = await response.json();
+ expect(responseBody.data.name).toEqual("New Webhook");
+ createdWebhookId = responseBody.data.id;
+ });
+
+ await test.step("Retrieve Webhooks via API", async () => {
+ const params = { limit: 10, skip: 0, sortBy: "createdAt", order: "desc" };
+ const response = await request.get(WEBHOOKS_API_URL, {
+ headers: {
+ "x-api-key": apiKey,
+ },
+ params,
+ });
+
+ expect(response.ok()).toBe(true);
+ const responseBody = await response.json();
+ const newlyCreated = responseBody.data.find((hook: any) => hook.id === createdWebhookId);
+ expect(newlyCreated).toBeTruthy();
+ expect(newlyCreated.name).toBe("New Webhook");
+ });
+
+ await test.step("Update Webhook by ID via API", async () => {
+ const updatedBody = {
+ environmentId,
+ name: "Updated Webhook",
+ url: "https://updated-webhook-url.com",
+ source: "zapier",
+ triggers: ["responseCreated"],
+ surveyIds: [surveyId],
+ };
+
+ const response = await request.put(`${WEBHOOKS_API_URL}/${createdWebhookId}`, {
+ headers: {
+ "x-api-key": apiKey,
+ "Content-Type": "application/json",
+ },
+ data: updatedBody,
+ });
+
+ expect(response.ok()).toBe(true);
+ const responseJson = await response.json();
+ expect(responseJson.data.name).toBe("Updated Webhook");
+ expect(responseJson.data.source).toBe("zapier");
+ });
+
+ await test.step("Delete Webhook via API", async () => {
+ const deleteResponse = await request.delete(`${WEBHOOKS_API_URL}/${createdWebhookId}`, {
+ headers: {
+ "x-api-key": apiKey,
+ },
+ });
+
+ expect(deleteResponse.ok()).toBe(true);
+ });
+ });
+});
diff --git a/apps/web/scripts/merge-client-endpoints.ts b/apps/web/scripts/merge-client-endpoints.ts
index 2a19276301..6b6dad0ea9 100644
--- a/apps/web/scripts/merge-client-endpoints.ts
+++ b/apps/web/scripts/merge-client-endpoints.ts
@@ -294,6 +294,341 @@ const v1ClientEndpoints = {
],
},
},
+ "/{environmentId}/storage": {
+ post: {
+ summary: "Upload Private File",
+ description:
+ "API endpoint for uploading private files. Uploaded files are kept private so that only users with access to the specified environment can retrieve them. The endpoint validates the survey ID, file name, and file type from the request body, and returns a signed URL for S3 uploads along with a local upload URL.",
+ tags: ["Client API > File Upload"],
+ parameters: [
+ {
+ in: "path",
+ name: "environmentId",
+ required: true,
+ schema: {
+ type: "string",
+ },
+ description: "The ID of the environment.",
+ },
+ ],
+ requestBody: {
+ required: true,
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ surveyId: {
+ type: "string",
+ description: "The ID of the survey associated with the file.",
+ },
+ fileName: {
+ type: "string",
+ description: "The name of the file to be uploaded.",
+ },
+ fileType: {
+ type: "string",
+ description: "The MIME type of the file.",
+ },
+ },
+ required: ["surveyId", "fileName", "fileType"],
+ example: {
+ surveyId: "cm7pr0x2y004o192zmit8cjvb",
+ fileName: "example.jpg",
+ fileType: "image/jpeg",
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ "200": {
+ description: "OK - Returns the signed URL, signing data, updated file name, and file URL.",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ data: {
+ type: "object",
+ properties: {
+ signedUrl: {
+ type: "string",
+ description: "Signed URL for uploading the file to local storage.",
+ },
+ signingData: {
+ type: "object",
+ properties: {
+ signature: {
+ type: "string",
+ description: "Signature for verifying the upload.",
+ },
+ timestamp: {
+ type: "number",
+ description: "Timestamp used in the signature.",
+ },
+ uuid: {
+ type: "string",
+ description: "Unique identifier for the signed upload.",
+ },
+ },
+ },
+ updatedFileName: {
+ type: "string",
+ description: "The updated file name after processing.",
+ },
+ fileUrl: {
+ type: "string",
+ description: "URL where the uploaded file can be accessed.",
+ },
+ },
+ },
+ },
+ example: {
+ data: {
+ signedUrl: "http://localhost:3000/api/v1/client/cm1ubebtj000614kqe4hs3c67/storage/local",
+ signingData: {
+ signature: "3e51c6f441e646a0c9a47fdcdd25eee9bfac26d5506461d811b9c55cbdd90914",
+ timestamp: 1741693207760,
+ uuid: "f48bcb1aad904f574069a253388024af",
+ },
+ updatedFileName: "halle--fid--b153ba3e-6602-4bb3-bed9-211b5b1ae463.jpg",
+ fileUrl:
+ "http://localhost:3000/storage/cm1ubebtj000614kqe4hs3c67/private/halle--fid--b153ba3e-6602-4bb3-bed9-211b5b1ae463.jpg",
+ },
+ },
+ },
+ },
+ },
+ },
+ "400": {
+ description: "Bad Request - One or more required fields are missing.",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ error: {
+ type: "string",
+ description: "Detailed error message.",
+ },
+ },
+ example: {
+ error: "fileName is required",
+ },
+ },
+ },
+ },
+ },
+ "404": {
+ description: "Not Found - The specified survey or organization does not exist.",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ error: {
+ type: "string",
+ description: "Detailed error message.",
+ },
+ },
+ example: {
+ error: "Survey survey123 not found",
+ },
+ },
+ },
+ },
+ },
+ },
+ servers: [
+ {
+ url: "https://app.formbricks.com/api/v2/client",
+ description: "Formbricks API Server",
+ },
+ ],
+ },
+ },
+ "/{environmentId}/storage/local": {
+ post: {
+ summary: "Upload Private File to Local Storage",
+ description:
+ 'API endpoint for uploading private files to local storage. The request must include a valid signature, UUID, and timestamp to verify the upload. The file is provided as a Base64 encoded string in the request body. The "Content-Type" header must be set to a valid MIME type, and the file data must be a valid file object (buffer).',
+ tags: ["Client API > File Upload"],
+ parameters: [
+ {
+ in: "path",
+ name: "environmentId",
+ required: true,
+ schema: {
+ type: "string",
+ },
+ description: "The ID of the environment.",
+ },
+ ],
+ requestBody: {
+ required: true,
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ surveyId: {
+ type: "string",
+ description: "The ID of the survey associated with the file.",
+ },
+ fileName: {
+ type: "string",
+ description: "The URI encoded file name.",
+ },
+ fileType: {
+ type: "string",
+ description: "The MIME type of the file.",
+ },
+ signature: {
+ type: "string",
+ description: "Signed signature for verifying the file upload.",
+ },
+ uuid: {
+ type: "string",
+ description: "Unique identifier used in the signature validation.",
+ },
+ timestamp: {
+ type: "string",
+ description: "Timestamp used in the signature validation.",
+ },
+ fileBase64String: {
+ type: "string",
+ description:
+ 'Base64 encoded string of the file. It should include data type information, e.g. "data:;base64,".',
+ },
+ },
+ required: [
+ "surveyId",
+ "fileName",
+ "fileType",
+ "signature",
+ "uuid",
+ "timestamp",
+ "fileBase64String",
+ ],
+ example: {
+ surveyId: "survey123",
+ fileName: "example.jpg",
+ fileType: "image/jpeg",
+ signature: "signedSignatureValue",
+ uuid: "uniqueUuidValue",
+ timestamp: "1627891234567",
+ fileBase64String: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/...",
+ },
+ },
+ },
+ },
+ },
+ responses: {
+ "200": {
+ description: "OK - File uploaded successfully.",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ message: {
+ type: "string",
+ description: "Success message.",
+ },
+ },
+ example: {
+ message: "File uploaded successfully",
+ },
+ },
+ },
+ },
+ },
+ "400": {
+ description: "Bad Request - One or more required fields are missing or the file is too large.",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ error: {
+ type: "string",
+ description: "Detailed error message.",
+ },
+ },
+ example: {
+ error: "fileName is required",
+ },
+ },
+ },
+ },
+ },
+ "401": {
+ description: "Unauthorized - Signature validation failed or required signature fields are missing.",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ error: {
+ type: "string",
+ description: "Detailed error message.",
+ },
+ },
+ example: {
+ error: "Unauthorized",
+ },
+ },
+ },
+ },
+ },
+ "404": {
+ description: "Not Found - The specified survey or organization does not exist.",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ error: {
+ type: "string",
+ description: "Detailed error message.",
+ },
+ },
+ example: {
+ error: "Survey survey123 not found",
+ },
+ },
+ },
+ },
+ },
+ "500": {
+ description: "Internal Server Error - File upload failed.",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ error: {
+ type: "string",
+ description: "Detailed error message.",
+ },
+ },
+ example: {
+ error: "File upload failed",
+ },
+ },
+ },
+ },
+ },
+ },
+ servers: [
+ {
+ url: "https://app.formbricks.com/api/v2",
+ description: "Formbricks API Server",
+ },
+ ],
+ },
+ },
};
// Read the generated openapi.yml file
diff --git a/apps/web/sentry.client.config.ts b/apps/web/sentry.client.config.ts
deleted file mode 100644
index b5765e8e3c..0000000000
--- a/apps/web/sentry.client.config.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-// This file configures the initialization of Sentry on the client.
-// The config you add here will be used whenever a users loads a page in their browser.
-// https://docs.sentry.io/platforms/javascript/guides/nextjs/
-import * as Sentry from "@sentry/nextjs";
-
-Sentry.init({
- dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
-
- // Adjust this value in production, or use tracesSampler for greater control
- tracesSampleRate: 1,
-
- // Setting this option to true will print useful information to the console while you're setting up Sentry.
- debug: false,
-
- replaysOnErrorSampleRate: 1.0,
-
- // This sets the sample rate to be 10%. You may want this to be 100% while
- // in development and sample at a lower rate in production
- replaysSessionSampleRate: 0.1,
-
- // You can remove this option if you're not planning to use the Sentry Session Replay feature:
- integrations: [
- Sentry.replayIntegration({
- // Additional Replay configuration goes in here, for example:
- maskAllText: true,
- blockAllMedia: true,
- }),
- ],
-
- beforeSend(event, hint) {
- const error = hint.originalException as Error;
-
- // @ts-expect-error
- if (error && error.digest === "NEXT_NOT_FOUND") {
- return null;
- }
-
- return event;
- },
-});
diff --git a/apps/web/sentry.edge.config.ts b/apps/web/sentry.edge.config.ts
index 45a2a3a210..c882e14790 100644
--- a/apps/web/sentry.edge.config.ts
+++ b/apps/web/sentry.edge.config.ts
@@ -3,13 +3,20 @@
// Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
+import { SENTRY_DSN } from "@formbricks/lib/constants";
-Sentry.init({
- dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
+if (SENTRY_DSN) {
+ console.log("Sentry DSN found, enabling Sentry on the edge");
- // Adjust this value in production, or use tracesSampler for greater control
- tracesSampleRate: 1,
+ Sentry.init({
+ dsn: SENTRY_DSN,
- // Setting this option to true will print useful information to the console while you're setting up Sentry.
- debug: false,
-});
+ // Adjust this value in production, or use tracesSampler for greater control
+ tracesSampleRate: 1,
+
+ // Setting this option to true will print useful information to the console while you're setting up Sentry.
+ debug: false,
+ });
+} else {
+ console.warn("Sentry DSN not found, Sentry will be disabled on the edge");
+}
diff --git a/apps/web/sentry.server.config.ts b/apps/web/sentry.server.config.ts
index f91917df8a..c7a59cf053 100644
--- a/apps/web/sentry.server.config.ts
+++ b/apps/web/sentry.server.config.ts
@@ -2,27 +2,34 @@
// The config you add here will be used whenever the server handles a request.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
+import { SENTRY_DSN } from "@formbricks/lib/constants";
-Sentry.init({
- dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
+if (SENTRY_DSN) {
+ console.log("Sentry DSN found, enabling Sentry on the server");
- // Adjust this value in production, or use tracesSampler for greater control
- tracesSampleRate: 1,
+ Sentry.init({
+ dsn: SENTRY_DSN,
- // Setting this option to true will print useful information to the console while you're setting up Sentry.
- debug: false,
+ // Adjust this value in production, or use tracesSampler for greater control
+ tracesSampleRate: 1,
- // uncomment the line below to enable Spotlight (https://spotlightjs.com)
- // spotlight: process.env.NODE_ENV === 'development',
+ // Setting this option to true will print useful information to the console while you're setting up Sentry.
+ debug: false,
- beforeSend(event, hint) {
- const error = hint.originalException as Error;
+ // uncomment the line below to enable Spotlight (https://spotlightjs.com)
+ // spotlight: process.env.NODE_ENV === 'development',
- // @ts-expect-error
- if (error && error.digest === "NEXT_NOT_FOUND") {
- return null;
- }
+ beforeSend(event, hint) {
+ const error = hint.originalException as Error;
- return event;
- },
-});
+ // @ts-expect-error
+ if (error && error.digest === "NEXT_NOT_FOUND") {
+ return null;
+ }
+
+ return event;
+ },
+ });
+} else {
+ console.warn("Sentry DSN not found, Sentry will be disabled on the server");
+}
diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js
index 774c90f9e0..120265f69d 100644
--- a/apps/web/tailwind.config.js
+++ b/apps/web/tailwind.config.js
@@ -1,3 +1,5 @@
+const colors = require("tailwindcss/colors");
+
module.exports = {
content: [
// app content
@@ -39,7 +41,7 @@ module.exports = {
dark: "#00C4B8",
},
focus: "var(--formbricks-focus, #1982fc)",
- error: "rgb(from var(--formbricks-error) r g b / )",
+ // error: "rgb(from var(--formbricks-error) r g b / )",
brandnew: "var(--formbricks-brand, #038178)",
primary: {
DEFAULT: "#0f172a",
@@ -57,6 +59,34 @@ module.exports = {
DEFAULT: "#f4f6f8", // light gray background
foreground: "#0f172a", // same as primary default for consistency
},
+ info: {
+ DEFAULT: colors.blue[600],
+ foreground: colors.blue[900],
+ muted: colors.blue[700],
+ background: colors.blue[50],
+ "background-muted": colors.blue[100],
+ },
+ warning: {
+ DEFAULT: colors.amber[500],
+ foreground: colors.amber[900],
+ muted: colors.amber[700],
+ background: colors.amber[50],
+ "background-muted": colors.amber[100],
+ },
+ success: {
+ DEFAULT: colors.green[600],
+ foreground: colors.green[900],
+ muted: colors.green[700],
+ background: colors.green[50],
+ "background-muted": colors.green[100],
+ },
+ error: {
+ DEFAULT: colors.red[600],
+ foreground: colors.red[900],
+ muted: colors.red[700],
+ background: colors.red[50],
+ "background-muted": colors.red[100],
+ },
},
keyframes: {
fadeIn: {
diff --git a/apps/web/test-results/.last-run.json b/apps/web/test-results/.last-run.json
new file mode 100644
index 0000000000..f341f7f7cf
--- /dev/null
+++ b/apps/web/test-results/.last-run.json
@@ -0,0 +1,4 @@
+{
+ "failedTests": [],
+ "status": "failed"
+}
diff --git a/apps/web/vite.config.mts b/apps/web/vite.config.mts
index babfb0ac96..8800bff259 100644
--- a/apps/web/vite.config.mts
+++ b/apps/web/vite.config.mts
@@ -26,19 +26,21 @@ export default defineConfig({
"modules/email/components/email-template.tsx",
"modules/email/emails/survey/follow-up.tsx",
"modules/ui/components/post-hog-client/*.tsx",
+ "modules/ui/components/alert/*.tsx",
"app/(app)/environments/**/layout.tsx",
"app/(app)/environments/**/settings/(organization)/general/page.tsx",
"app/(app)/environments/**/components/PosthogIdentify.tsx",
"app/(app)/(onboarding)/organizations/**/layout.tsx",
"app/(app)/(survey-editor)/environments/**/layout.tsx",
- "modules/ee/sso/lib/**/*.ts",
- "modules/ee/contacts/lib/**/*.ts",
- "modules/survey/link/lib/**/*.ts",
"app/(auth)/layout.tsx",
"app/(app)/layout.tsx",
"app/layout.tsx",
- "app/(app)/environments/**/surveys/**/(analysis)/summary/components/SurveyAnalysisCTA.tsx",
"app/intercom/*.tsx",
+ "app/sentry/*.tsx",
+ "app/(app)/environments/**/surveys/**/(analysis)/summary/components/SurveyAnalysisCTA.tsx",
+ "modules/ee/sso/lib/**/*.ts",
+ "app/lib/**/*.ts",
+ "app/api/(internal)/insights/lib/**/*.ts",
"modules/ee/role-management/*.ts",
"modules/organization/settings/teams/actions.ts",
"modules/survey/hooks/*.tsx",
diff --git a/docs/api-reference/management-api->-storage/upload-public-file-local-storage.mdx b/docs/api-reference/management-api->-storage/upload-public-file-local-storage.mdx
new file mode 100644
index 0000000000..0442559d82
--- /dev/null
+++ b/docs/api-reference/management-api->-storage/upload-public-file-local-storage.mdx
@@ -0,0 +1,3 @@
+---
+openapi: post /api/v1/management/storage/local
+---
diff --git a/docs/api-reference/management-api->-storage/upload-public-file.mdx b/docs/api-reference/management-api->-storage/upload-public-file.mdx
new file mode 100644
index 0000000000..9d1890a1b9
--- /dev/null
+++ b/docs/api-reference/management-api->-storage/upload-public-file.mdx
@@ -0,0 +1,3 @@
+---
+openapi: post /api/v1/management/storage
+---
diff --git a/docs/api-reference/openapi.json b/docs/api-reference/openapi.json
index 379982f927..d1f02a3d02 100644
--- a/docs/api-reference/openapi.json
+++ b/docs/api-reference/openapi.json
@@ -5672,6 +5672,343 @@
"tags": ["Management API > Response"]
}
},
+ "/api/v1/management/storage": {
+ "post": {
+ "description": "API endpoint for uploading public files. Uploaded files are public and accessible by anyone. This endpoint requires authentication. It accepts a JSON body with fileName, fileType, environmentId, and optionally allowedFileExtensions to restrict file types. On success, it returns a signed URL for uploading the file to S3 along with a local upload URL.",
+ "parameters": [
+ {
+ "example": "{{apiKey}}",
+ "in": "header",
+ "name": "x-api-key",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "example": {
+ "allowedFileExtensions": ["png", "jpg", "jpeg"],
+ "environmentId": "env123",
+ "fileName": "profile.png",
+ "fileType": "image/png"
+ },
+ "schema": {
+ "properties": {
+ "allowedFileExtensions": {
+ "description": "Optional. List of allowed file extensions.",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "environmentId": {
+ "description": "The ID of the environment.",
+ "type": "string"
+ },
+ "fileName": {
+ "description": "The name of the file to be uploaded.",
+ "type": "string"
+ },
+ "fileType": {
+ "description": "The MIME type of the file.",
+ "type": "string"
+ }
+ },
+ "required": ["fileName", "fileType", "environmentId"],
+ "type": "object"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "example": {
+ "data": {
+ "fileUrl": "http://localhost:3000/storage/cm1ubebtj000614kqe4hs3c67/public/profile--fid--abc123.png",
+ "localUrl": "http://localhost:3000/storage/cm1ubebtj000614kqe4hs3c67/public/profile.png",
+ "signedUrl": "http://localhost:3000/api/v1/client/cm1ubebtj000614kqe4hs3c67/storage/public",
+ "updatedFileName": "profile--fid--abc123.png"
+ }
+ },
+ "schema": {
+ "properties": {
+ "data": {
+ "properties": {
+ "fileUrl": {
+ "description": "URL where the uploaded file can be accessed.",
+ "type": "string"
+ },
+ "localUrl": {
+ "description": "URL for uploading the file to local storage.",
+ "type": "string"
+ },
+ "signedUrl": {
+ "description": "Signed URL for uploading the file to S3.",
+ "type": "string"
+ },
+ "updatedFileName": {
+ "description": "The updated file name after processing.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+ }
+ }
+ },
+ "description": "OK - Returns the signed URL, updated file name, and file URL."
+ },
+ "400": {
+ "content": {
+ "application/json": {
+ "example": {
+ "error": "fileName is required"
+ },
+ "schema": {
+ "properties": {
+ "error": {
+ "description": "Detailed error message.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ }
+ },
+ "description": "Bad Request - Missing required fields or invalid file extension."
+ },
+ "401": {
+ "content": {
+ "application/json": {
+ "example": {
+ "error": "Not authenticated"
+ },
+ "schema": {
+ "properties": {
+ "error": {
+ "description": "Detailed error message.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ }
+ },
+ "description": "Unauthorized - Authentication failed or user not logged in."
+ },
+ "403": {
+ "content": {
+ "application/json": {
+ "example": {
+ "error": "User does not have access to environment env123"
+ },
+ "schema": {
+ "properties": {
+ "error": {
+ "description": "Detailed error message.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ }
+ },
+ "description": "Forbidden - User does not have access to the specified environment."
+ }
+ },
+ "summary": "Upload Public File",
+ "tags": ["Management API > Storage"]
+ }
+ },
+ "/api/v1/management/storage/local": {
+ "post": {
+ "description": "Management API endpoint for uploading public files to local storage. This endpoint requires authentication. File metadata is provided via headers (X-File-Type, X-File-Name, X-Environment-ID, X-Signature, X-UUID, X-Timestamp) and the file is provided as a multipart/form-data file field named \"file\". The \"Content-Type\" header must be set to a valid MIME type.",
+ "parameters": [
+ {
+ "example": "{{apiKey}}",
+ "in": "header",
+ "name": "x-api-key",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "description": "MIME type of the file. Must be a valid MIME type.",
+ "in": "header",
+ "name": "X-File-Type",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "description": "URI encoded file name.",
+ "in": "header",
+ "name": "X-File-Name",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "description": "ID of the environment.",
+ "in": "header",
+ "name": "X-Environment-ID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "description": "Signature for verifying the request.",
+ "in": "header",
+ "name": "X-Signature",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "description": "Unique identifier for the signed upload.",
+ "in": "header",
+ "name": "X-UUID",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "description": "Timestamp used for the signature.",
+ "in": "header",
+ "name": "X-Timestamp",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "properties": {
+ "file": {
+ "description": "The file to be uploaded as a valid file object (buffer).",
+ "format": "binary",
+ "type": "string"
+ }
+ },
+ "required": ["file"],
+ "type": "object"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/json": {
+ "example": {
+ "data": {
+ "message": "File uploaded successfully"
+ }
+ },
+ "schema": {
+ "properties": {
+ "data": {
+ "properties": {
+ "message": {
+ "description": "Success message.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+ }
+ }
+ },
+ "description": "OK - File uploaded successfully."
+ },
+ "400": {
+ "content": {
+ "application/json": {
+ "example": {
+ "error": "fileType is required"
+ },
+ "schema": {
+ "properties": {
+ "error": {
+ "description": "Detailed error message.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ }
+ },
+ "description": "Bad Request - Missing required fields, invalid header values, or file issues."
+ },
+ "401": {
+ "content": {
+ "application/json": {
+ "example": {
+ "error": "Not authenticated"
+ },
+ "schema": {
+ "properties": {
+ "error": {
+ "description": "Detailed error message.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ }
+ },
+ "description": "Unauthorized - Authentication failed, invalid signature, or user not authorized."
+ },
+ "500": {
+ "content": {
+ "application/json": {
+ "example": {
+ "error": "File upload failed"
+ },
+ "schema": {
+ "properties": {
+ "error": {
+ "description": "Detailed error message.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ }
+ },
+ "description": "Internal Server Error - File upload failed due to server error."
+ }
+ },
+ "servers": [
+ {
+ "description": "Formbricks API Server",
+ "url": "https://app.formbricks.com/api/v1"
+ }
+ ],
+ "summary": "Upload Public File to Local Storage",
+ "tags": ["Management API > Storage"]
+ }
+ },
"/api/v1/management/surveys": {
"get": {
"description": "Fetches all existing surveys",
diff --git a/docs/api-v2-reference/openapi.yml b/docs/api-v2-reference/openapi.yml
index 39f1d842f3..7ef49a39fd 100644
--- a/docs/api-v2-reference/openapi.yml
+++ b/docs/api-v2-reference/openapi.yml
@@ -17,22 +17,14 @@ tags:
description: Operations for managing contact attributes keys.
- name: Management API > Surveys
description: Operations for managing surveys.
- - name: Management API > Storage
- description: Operations for managing storage.
- - name: Client API > File Upload
- description: Operations for uploading files.
-
+ - name: Management API > Webhooks
+ description: Operations for managing webhooks.
paths:
- /{environmentId}/responses/{responseId}:
+ /responses/{responseId}:
put:
description: Update an existing response for example when you want to mark a
response as finished or you want to change an existing response's value.
parameters:
- - in: path
- name: environmentId
- required: true
- schema:
- type: string
- in: path
name: responseId
required: true
@@ -291,8 +283,7 @@ paths:
/{environmentId}/storage:
post:
summary: Upload Private File
- description: >
- API endpoint for uploading private files. Uploaded files are kept
+ description: API endpoint for uploading private files. Uploaded files are kept
private so that only users with access to the specified environment can
retrieve them. The endpoint validates the survey ID, file name, and file
type from the request body, and returns a signed URL for S3 uploads
@@ -332,7 +323,8 @@ paths:
fileType: image/jpeg
responses:
"200":
- description: OK - Returns the signed URL, signing data, updated file name, and file URL.
+ description: OK - Returns the signed URL, signing data, updated file name, and
+ file URL.
content:
application/json:
schema:
@@ -364,13 +356,13 @@ paths:
description: URL where the uploaded file can be accessed.
example:
data:
- signedUrl: "http://localhost:3000/api/v1/client/cm1ubebtj000614kqe4hs3c67/storage/local"
+ signedUrl: http://localhost:3000/api/v1/client/cm1ubebtj000614kqe4hs3c67/storage/local
signingData:
- signature: "3e51c6f441e646a0c9a47fdcdd25eee9bfac26d5506461d811b9c55cbdd90914"
+ signature: 3e51c6f441e646a0c9a47fdcdd25eee9bfac26d5506461d811b9c55cbdd90914
timestamp: 1741693207760
- uuid: "f48bcb1aad904f574069a253388024af"
- updatedFileName: "halle--fid--b153ba3e-6602-4bb3-bed9-211b5b1ae463.jpg"
- fileUrl: "http://localhost:3000/storage/cm1ubebtj000614kqe4hs3c67/private/halle--fid--b153ba3e-6602-4bb3-bed9-211b5b1ae463.jpg"
+ uuid: f48bcb1aad904f574069a253388024af
+ updatedFileName: halle--fid--b153ba3e-6602-4bb3-bed9-211b5b1ae463.jpg
+ fileUrl: http://localhost:3000/storage/cm1ubebtj000614kqe4hs3c67/private/halle--fid--b153ba3e-6602-4bb3-bed9-211b5b1ae463.jpg
"400":
description: Bad Request - One or more required fields are missing.
content:
@@ -401,10 +393,11 @@ paths:
/{environmentId}/storage/local:
post:
summary: Upload Private File to Local Storage
- description: >
- API endpoint for uploading private files to local storage. The request must include a valid signature,
- UUID, and timestamp to verify the upload. The file is provided as a Base64 encoded string in the request body.
- The "Content-Type" header must be set to a valid MIME type, and the file data must be a valid file object (buffer).
+ description: API endpoint for uploading private files to local storage. The
+ request must include a valid signature, UUID, and timestamp to verify
+ the upload. The file is provided as a Base64 encoded string in the
+ request body. The "Content-Type" header must be set to a valid MIME
+ type, and the file data must be a valid file object (buffer).
tags:
- Client API > File Upload
parameters:
@@ -441,9 +434,9 @@ paths:
description: Timestamp used in the signature validation.
fileBase64String:
type: string
- description: >
- Base64 encoded string of the file. It should include data type information,
- e.g. "data:;base64,".
+ description: Base64 encoded string of the file. It should include data type
+ information, e.g.
+ "data:;base64,".
required:
- surveyId
- fileName
@@ -452,14 +445,14 @@ paths:
- uuid
- timestamp
- fileBase64String
- example:
- surveyId: "survey123"
- fileName: "example.jpg"
- fileType: "image/jpeg"
- signature: "signedSignatureValue"
- uuid: "uniqueUuidValue"
- timestamp: "1627891234567"
- fileBase64String: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/..."
+ example:
+ surveyId: survey123
+ fileName: example.jpg
+ fileType: image/jpeg
+ signature: signedSignatureValue
+ uuid: uniqueUuidValue
+ timestamp: "1627891234567"
+ fileBase64String: data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/...
responses:
"200":
description: OK - File uploaded successfully.
@@ -471,10 +464,11 @@ paths:
message:
type: string
description: Success message.
- example:
- message: "File uploaded successfully"
+ example:
+ message: File uploaded successfully
"400":
- description: Bad Request - One or more required fields are missing or the file is too large.
+ description: Bad Request - One or more required fields are missing or the file
+ is too large.
content:
application/json:
schema:
@@ -483,10 +477,11 @@ paths:
error:
type: string
description: Detailed error message.
- example:
- error: "fileName is required"
+ example:
+ error: fileName is required
"401":
- description: Unauthorized - Signature validation failed or required signature fields are missing.
+ description: Unauthorized - Signature validation failed or required signature
+ fields are missing.
content:
application/json:
schema:
@@ -495,8 +490,8 @@ paths:
error:
type: string
description: Detailed error message.
- example:
- error: "Unauthorized"
+ example:
+ error: Unauthorized
"404":
description: Not Found - The specified survey or organization does not exist.
content:
@@ -507,8 +502,8 @@ paths:
error:
type: string
description: Detailed error message.
- example:
- error: "Survey survey123 not found"
+ example:
+ error: Survey survey123 not found
"500":
description: Internal Server Error - File upload failed.
content:
@@ -519,8 +514,8 @@ paths:
error:
type: string
description: Detailed error message.
- example:
- error: "File upload failed"
+ example:
+ error: File upload failed
servers:
- url: https://app.formbricks.com/api/v2
description: Formbricks API Server
@@ -591,7 +586,149 @@ paths:
schema:
type: array
items:
- $ref: "#/components/schemas/response"
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ type: object
+ properties:
+ id:
+ type: string
+ description: The ID of the response
+ createdAt:
+ type: string
+ description: The date and time the response was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
+ type: string
+ description: The date and time the response was last updated
+ example: 2021-01-01T00:00:00.000Z
+ finished:
+ type: boolean
+ description: Whether the response is finished
+ example: true
+ surveyId:
+ type: string
+ description: The ID of the survey
+ contactId:
+ type:
+ - string
+ - "null"
+ description: The ID of the contact
+ endingId:
+ type:
+ - string
+ - "null"
+ description: The ID of the ending
+ data:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ - type: array
+ items:
+ type: string
+ - type: object
+ additionalProperties:
+ type: string
+ description: The data of the response
+ example: &a1
+ question1: answer1
+ question2: 2
+ question3:
+ - answer3
+ - answer4
+ question4:
+ subquestion1: answer5
+ variables:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ description: The variables of the response
+ example: &a2
+ variable1: answer1
+ variable2: 2
+ ttc:
+ type: object
+ additionalProperties:
+ type: number
+ description: The TTC of the response
+ example: &a3
+ question1: 10
+ question2: 20
+ meta:
+ type: object
+ properties:
+ source:
+ type: string
+ description: The source of the response
+ example: https://example.com
+ url:
+ type: string
+ description: The URL of the response
+ example: https://example.com
+ userAgent:
+ type: object
+ properties:
+ browser:
+ type: string
+ os:
+ type: string
+ device:
+ type: string
+ country:
+ type: string
+ action:
+ type: string
+ description: The meta data of the response
+ example: &a4
+ source: https://example.com
+ url: https://example.com
+ userAgent:
+ browser: Chrome
+ os: Windows
+ device: Desktop
+ country: US
+ action: click
+ contactAttributes:
+ type:
+ - object
+ - "null"
+ additionalProperties:
+ type: string
+ description: The attributes of the contact
+ example: &a5
+ attribute1: value1
+ attribute2: value2
+ singleUseId:
+ type:
+ - string
+ - "null"
+ description: The single use ID of the response
+ language:
+ type:
+ - string
+ - "null"
+ description: The language of the response
+ example: en
+ displayId:
+ type:
+ - string
+ - "null"
+ description: The display ID of the response
+ meta:
+ type: object
+ properties:
+ total:
+ type: number
+ limit:
+ type: number
+ offset:
+ type: number
post:
security:
- apiKeyAuth: []
@@ -606,14 +743,216 @@ paths:
content:
application/json:
schema:
- $ref: "#/components/schemas/responseCreate"
+ type: object
+ properties:
+ createdAt:
+ type: string
+ description: The date and time the response was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
+ type: string
+ description: The date and time the response was last updated
+ example: 2021-01-01T00:00:00.000Z
+ surveyId:
+ type: string
+ description: The ID of the survey
+ displayId:
+ type:
+ - string
+ - "null"
+ description: The display ID of the response
+ singleUseId:
+ type:
+ - string
+ - "null"
+ description: The single use ID of the response
+ finished:
+ type: boolean
+ description: Whether the response is finished
+ example: true
+ endingId:
+ type:
+ - string
+ - "null"
+ description: The ID of the ending
+ language:
+ type:
+ - string
+ - "null"
+ description: The language of the response
+ example: en
+ data:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ - type: array
+ items:
+ type: string
+ - type: object
+ additionalProperties:
+ type: string
+ description: The data of the response
+ example: *a1
+ variables:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ description: The variables of the response
+ example: *a2
+ ttc:
+ type: object
+ additionalProperties:
+ type: number
+ description: The TTC of the response
+ example: *a3
+ meta:
+ type: object
+ properties:
+ source:
+ type: string
+ description: The source of the response
+ example: https://example.com
+ url:
+ type: string
+ description: The URL of the response
+ example: https://example.com
+ userAgent:
+ type: object
+ properties:
+ browser:
+ type: string
+ os:
+ type: string
+ device:
+ type: string
+ country:
+ type: string
+ action:
+ type: string
+ description: The meta data of the response
+ example: *a4
+ required:
+ - surveyId
+ - finished
+ - data
responses:
"201":
description: Response created successfully.
content:
application/json:
schema:
- $ref: "#/components/schemas/response"
+ type: object
+ properties:
+ id:
+ type: string
+ description: The ID of the response
+ createdAt:
+ type: string
+ description: The date and time the response was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
+ type: string
+ description: The date and time the response was last updated
+ example: 2021-01-01T00:00:00.000Z
+ finished:
+ type: boolean
+ description: Whether the response is finished
+ example: true
+ surveyId:
+ type: string
+ description: The ID of the survey
+ contactId:
+ type:
+ - string
+ - "null"
+ description: The ID of the contact
+ endingId:
+ type:
+ - string
+ - "null"
+ description: The ID of the ending
+ data:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ - type: array
+ items:
+ type: string
+ - type: object
+ additionalProperties:
+ type: string
+ description: The data of the response
+ example: *a1
+ variables:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ description: The variables of the response
+ example: *a2
+ ttc:
+ type: object
+ additionalProperties:
+ type: number
+ description: The TTC of the response
+ example: *a3
+ meta:
+ type: object
+ properties:
+ source:
+ type: string
+ description: The source of the response
+ example: https://example.com
+ url:
+ type: string
+ description: The URL of the response
+ example: https://example.com
+ userAgent:
+ type: object
+ properties:
+ browser:
+ type: string
+ os:
+ type: string
+ device:
+ type: string
+ country:
+ type: string
+ action:
+ type: string
+ description: The meta data of the response
+ example: *a4
+ contactAttributes:
+ type:
+ - object
+ - "null"
+ additionalProperties:
+ type: string
+ description: The attributes of the contact
+ example: *a5
+ singleUseId:
+ type:
+ - string
+ - "null"
+ description: The single use ID of the response
+ language:
+ type:
+ - string
+ - "null"
+ description: The language of the response
+ example: en
+ displayId:
+ type:
+ - string
+ - "null"
+ description: The display ID of the response
/responses/{id}:
get:
security:
@@ -636,7 +975,114 @@ paths:
content:
application/json:
schema:
- $ref: "#/components/schemas/response"
+ type: object
+ properties:
+ id:
+ type: string
+ description: The ID of the response
+ createdAt:
+ type: string
+ description: The date and time the response was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
+ type: string
+ description: The date and time the response was last updated
+ example: 2021-01-01T00:00:00.000Z
+ finished:
+ type: boolean
+ description: Whether the response is finished
+ example: true
+ surveyId:
+ type: string
+ description: The ID of the survey
+ contactId:
+ type:
+ - string
+ - "null"
+ description: The ID of the contact
+ endingId:
+ type:
+ - string
+ - "null"
+ description: The ID of the ending
+ data:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ - type: array
+ items:
+ type: string
+ - type: object
+ additionalProperties:
+ type: string
+ description: The data of the response
+ example: *a1
+ variables:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ description: The variables of the response
+ example: *a2
+ ttc:
+ type: object
+ additionalProperties:
+ type: number
+ description: The TTC of the response
+ example: *a3
+ meta:
+ type: object
+ properties:
+ source:
+ type: string
+ description: The source of the response
+ example: https://example.com
+ url:
+ type: string
+ description: The URL of the response
+ example: https://example.com
+ userAgent:
+ type: object
+ properties:
+ browser:
+ type: string
+ os:
+ type: string
+ device:
+ type: string
+ country:
+ type: string
+ action:
+ type: string
+ description: The meta data of the response
+ example: *a4
+ contactAttributes:
+ type:
+ - object
+ - "null"
+ additionalProperties:
+ type: string
+ description: The attributes of the contact
+ example: *a5
+ singleUseId:
+ type:
+ - string
+ - "null"
+ description: The single use ID of the response
+ language:
+ type:
+ - string
+ - "null"
+ description: The language of the response
+ example: en
+ displayId:
+ type:
+ - string
+ - "null"
+ description: The display ID of the response
put:
security:
- apiKeyAuth: []
@@ -741,7 +1187,114 @@ paths:
content:
application/json:
schema:
- $ref: "#/components/schemas/response"
+ type: object
+ properties:
+ id:
+ type: string
+ description: The ID of the response
+ createdAt:
+ type: string
+ description: The date and time the response was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
+ type: string
+ description: The date and time the response was last updated
+ example: 2021-01-01T00:00:00.000Z
+ finished:
+ type: boolean
+ description: Whether the response is finished
+ example: true
+ surveyId:
+ type: string
+ description: The ID of the survey
+ contactId:
+ type:
+ - string
+ - "null"
+ description: The ID of the contact
+ endingId:
+ type:
+ - string
+ - "null"
+ description: The ID of the ending
+ data:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ - type: array
+ items:
+ type: string
+ - type: object
+ additionalProperties:
+ type: string
+ description: The data of the response
+ example: *a1
+ variables:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ description: The variables of the response
+ example: *a2
+ ttc:
+ type: object
+ additionalProperties:
+ type: number
+ description: The TTC of the response
+ example: *a3
+ meta:
+ type: object
+ properties:
+ source:
+ type: string
+ description: The source of the response
+ example: https://example.com
+ url:
+ type: string
+ description: The URL of the response
+ example: https://example.com
+ userAgent:
+ type: object
+ properties:
+ browser:
+ type: string
+ os:
+ type: string
+ device:
+ type: string
+ country:
+ type: string
+ action:
+ type: string
+ description: The meta data of the response
+ example: *a4
+ contactAttributes:
+ type:
+ - object
+ - "null"
+ additionalProperties:
+ type: string
+ description: The attributes of the contact
+ example: *a5
+ singleUseId:
+ type:
+ - string
+ - "null"
+ description: The single use ID of the response
+ language:
+ type:
+ - string
+ - "null"
+ description: The language of the response
+ example: en
+ displayId:
+ type:
+ - string
+ - "null"
+ description: The display ID of the response
delete:
security:
- apiKeyAuth: []
@@ -763,7 +1316,114 @@ paths:
content:
application/json:
schema:
- $ref: "#/components/schemas/response"
+ type: object
+ properties:
+ id:
+ type: string
+ description: The ID of the response
+ createdAt:
+ type: string
+ description: The date and time the response was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
+ type: string
+ description: The date and time the response was last updated
+ example: 2021-01-01T00:00:00.000Z
+ finished:
+ type: boolean
+ description: Whether the response is finished
+ example: true
+ surveyId:
+ type: string
+ description: The ID of the survey
+ contactId:
+ type:
+ - string
+ - "null"
+ description: The ID of the contact
+ endingId:
+ type:
+ - string
+ - "null"
+ description: The ID of the ending
+ data:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ - type: array
+ items:
+ type: string
+ - type: object
+ additionalProperties:
+ type: string
+ description: The data of the response
+ example: *a1
+ variables:
+ type: object
+ additionalProperties:
+ anyOf:
+ - type: string
+ - type: number
+ description: The variables of the response
+ example: *a2
+ ttc:
+ type: object
+ additionalProperties:
+ type: number
+ description: The TTC of the response
+ example: *a3
+ meta:
+ type: object
+ properties:
+ source:
+ type: string
+ description: The source of the response
+ example: https://example.com
+ url:
+ type: string
+ description: The URL of the response
+ example: https://example.com
+ userAgent:
+ type: object
+ properties:
+ browser:
+ type: string
+ os:
+ type: string
+ device:
+ type: string
+ country:
+ type: string
+ action:
+ type: string
+ description: The meta data of the response
+ example: *a4
+ contactAttributes:
+ type:
+ - object
+ - "null"
+ additionalProperties:
+ type: string
+ description: The attributes of the contact
+ example: *a5
+ singleUseId:
+ type:
+ - string
+ - "null"
+ description: The single use ID of the response
+ language:
+ type:
+ - string
+ - "null"
+ description: The language of the response
+ example: en
+ displayId:
+ type:
+ - string
+ - "null"
+ description: The display ID of the response
/contacts:
get:
security:
@@ -968,7 +1628,27 @@ paths:
schema:
type: array
items:
- $ref: "#/components/schemas/contactAttribute"
+ type: object
+ properties:
+ id:
+ type: string
+ createdAt:
+ type: string
+ updatedAt:
+ type: string
+ attributeKeyId:
+ type: string
+ contactId:
+ type: string
+ value:
+ type: string
+ required:
+ - id
+ - createdAt
+ - updatedAt
+ - attributeKeyId
+ - contactId
+ - value
post:
security:
- apiKeyAuth: []
@@ -1113,7 +1793,44 @@ paths:
schema:
type: array
items:
- $ref: "#/components/schemas/contactAttributeKey"
+ type: object
+ properties:
+ id:
+ type: string
+ createdAt:
+ type: string
+ updatedAt:
+ type: string
+ isUnique:
+ type: boolean
+ default: false
+ key:
+ type: string
+ name:
+ type:
+ - string
+ - "null"
+ description:
+ type:
+ - string
+ - "null"
+ type:
+ type: string
+ enum:
+ - default
+ - custom
+ environmentId:
+ type: string
+ required:
+ - id
+ - createdAt
+ - updatedAt
+ - isUnique
+ - key
+ - name
+ - description
+ - type
+ - environmentId
post:
security:
- apiKeyAuth: []
@@ -1372,230 +2089,453 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/survey"
- /api/v2/management/storage:
+ /webhooks:
+ get:
+ security:
+ - apiKeyAuth: []
+ operationId: getWebhooks
+ summary: Get webhooks
+ description: Gets webhooks from the database.
+ tags:
+ - Management API > Webhooks
+ parameters:
+ - in: query
+ name: limit
+ schema:
+ type: number
+ minimum: 1
+ maximum: 100
+ default: 10
+ - in: query
+ name: skip
+ schema:
+ type: number
+ minimum: 0
+ default: 0
+ - in: query
+ name: sortBy
+ schema:
+ type: string
+ enum:
+ - createdAt
+ - updatedAt
+ default: createdAt
+ - in: query
+ name: order
+ schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ default: desc
+ - in: query
+ name: startDate
+ schema:
+ type: string
+ required: true
+ - in: query
+ name: endDate
+ schema:
+ type: string
+ required: true
+ - in: query
+ name: surveyIds
+ schema:
+ type: array
+ items:
+ type: string
+ required: true
+ responses:
+ "200":
+ description: Webhooks retrieved successfully.
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ data:
+ type: array
+ items:
+ type: object
+ properties:
+ id:
+ type: string
+ description: The ID of the webhook
+ name:
+ type:
+ - string
+ - "null"
+ description: The name of the webhook
+ createdAt:
+ type: string
+ description: The date and time the webhook was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
+ type: string
+ description: The date and time the webhook was last updated
+ example: 2021-01-01T00:00:00.000Z
+ url:
+ type: string
+ format: uri
+ description: The URL of the webhook
+ source:
+ type: string
+ enum: &a6
+ - user
+ - zapier
+ - make
+ - n8n
+ description: The source of the webhook
+ environmentId:
+ type: string
+ description: The ID of the environment
+ triggers:
+ type: array
+ items:
+ type: string
+ enum: &a7
+ - responseFinished
+ - responseCreated
+ - responseUpdated
+ description: The triggers of the webhook
+ surveyIds:
+ type: array
+ items:
+ type: string
+ description: "The IDs of the surveys "
+ meta:
+ type: object
+ properties:
+ total:
+ type: number
+ limit:
+ type: number
+ offset:
+ type: number
post:
security:
- apiKeyAuth: []
- summary: Upload Public File
- description: >
- API endpoint for uploading public files. Uploaded files are public and accessible by anyone.
- This endpoint requires authentication. It accepts a JSON body with fileName, fileType, environmentId,
- and optionally allowedFileExtensions to restrict file types. On success, it returns a signed URL for uploading
- the file to S3 along with a local upload URL.
+ operationId: createWebhook
+ summary: Create a webhook
+ description: Creates a webhook in the database.
tags:
- - Management API > Storage
+ - Management API > Webhooks
requestBody:
required: true
+ description: The webhook to create
content:
application/json:
schema:
type: object
properties:
- fileName:
+ name:
+ type:
+ - string
+ - "null"
+ description: The name of the webhook
+ url:
type: string
- description: The name of the file to be uploaded.
- fileType:
+ format: uri
+ description: The URL of the webhook
+ source:
type: string
- description: The MIME type of the file.
+ enum: *a6
+ description: The source of the webhook
environmentId:
type: string
- description: The ID of the environment.
- allowedFileExtensions:
+ description: The ID of the environment
+ triggers:
type: array
items:
type: string
- description: Optional. List of allowed file extensions.
+ enum: *a7
+ description: The triggers of the webhook
+ surveyIds:
+ type: array
+ items:
+ type: string
+ description: "The IDs of the surveys "
required:
- - fileName
- - fileType
+ - name
+ - url
+ - source
- environmentId
- example:
- fileName: "profile.png"
- fileType: "image/png"
- environmentId: "env123"
- allowedFileExtensions: ["png", "jpg", "jpeg"]
+ - triggers
+ - surveyIds
responses:
- "200":
- description: OK - Returns the signed URL, updated file name, and file URL.
+ "201":
+ description: Webhook created successfully.
content:
application/json:
schema:
type: object
properties:
- data:
- type: object
- properties:
- signedUrl:
- type: string
- description: Signed URL for uploading the file to S3.
- localUrl:
- type: string
- description: URL for uploading the file to local storage.
- updatedFileName:
- type: string
- description: The updated file name after processing.
- fileUrl:
- type: string
- description: URL where the uploaded file can be accessed.
- example:
- data:
- signedUrl: "http://localhost:3000/api/v1/client/cm1ubebtj000614kqe4hs3c67/storage/public"
- localUrl: "http://localhost:3000/storage/cm1ubebtj000614kqe4hs3c67/public/profile.png"
- updatedFileName: "profile--fid--abc123.png"
- fileUrl: "http://localhost:3000/storage/cm1ubebtj000614kqe4hs3c67/public/profile--fid--abc123.png"
- "400":
- description: Bad Request - Missing required fields or invalid file extension.
- content:
- application/json:
- schema:
- type: object
- properties:
- error:
+ id:
type: string
- description: Detailed error message.
- example:
- error: "fileName is required"
- "401":
- description: Unauthorized - Authentication failed or user not logged in.
- content:
- application/json:
- schema:
- type: object
- properties:
- error:
+ description: The ID of the webhook
+ name:
+ type:
+ - string
+ - "null"
+ description: The name of the webhook
+ createdAt:
type: string
- description: Detailed error message.
- example:
- error: "Not authenticated"
- "403":
- description: Forbidden - User does not have access to the specified environment.
- content:
- application/json:
- schema:
- type: object
- properties:
- error:
+ description: The date and time the webhook was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
type: string
- description: Detailed error message.
- example:
- error: "User does not have access to environment env123"
- /api/v2/management/storage/local:
- put:
- summary: Upload Public File to Local Storage
+ description: The date and time the webhook was last updated
+ example: 2021-01-01T00:00:00.000Z
+ url:
+ type: string
+ format: uri
+ description: The URL of the webhook
+ source:
+ type: string
+ enum: *a6
+ description: The source of the webhook
+ environmentId:
+ type: string
+ description: The ID of the environment
+ triggers:
+ type: array
+ items:
+ type: string
+ enum: *a7
+ description: The triggers of the webhook
+ surveyIds:
+ type: array
+ items:
+ type: string
+ description: "The IDs of the surveys "
+ /webhooks/{webhookId}:
+ get:
security:
- apiKeyAuth: []
- description: >
- Management API endpoint for uploading public files to local storage. This endpoint requires authentication.
- File metadata is provided via headers (X-File-Type, X-File-Name, X-Environment-ID, X-Signature, X-UUID, X-Timestamp)
- and the file is provided as a multipart/form-data file field named "file". The "Content-Type" header must be set to a valid MIME type.
+ operationId: getWebhook
+ summary: Get a webhook
+ description: Gets a webhook from the database.
tags:
- - Management API > Storage
+ - Management API > Webhooks
parameters:
- - in: header
- name: X-File-Type
- required: true
+ - in: path
+ name: id
+ description: The ID of the webhook
schema:
- type: string
- description: "MIME type of the file. Must be a valid MIME type."
- - in: header
- name: X-File-Name
+ $ref: "#/components/schemas/webhookId"
required: true
+ responses:
+ "200":
+ description: Webhook retrieved successfully.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ id:
+ type: string
+ description: The ID of the webhook
+ name:
+ type:
+ - string
+ - "null"
+ description: The name of the webhook
+ createdAt:
+ type: string
+ description: The date and time the webhook was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
+ type: string
+ description: The date and time the webhook was last updated
+ example: 2021-01-01T00:00:00.000Z
+ url:
+ type: string
+ format: uri
+ description: The URL of the webhook
+ source:
+ type: string
+ enum: *a6
+ description: The source of the webhook
+ environmentId:
+ type: string
+ description: The ID of the environment
+ triggers:
+ type: array
+ items:
+ type: string
+ enum: *a7
+ description: The triggers of the webhook
+ surveyIds:
+ type: array
+ items:
+ type: string
+ description: "The IDs of the surveys "
+ put:
+ security:
+ - apiKeyAuth: []
+ operationId: updateWebhook
+ summary: Update a webhook
+ description: Updates a webhook in the database.
+ tags:
+ - Management API > Webhooks
+ parameters:
+ - in: path
+ name: id
+ description: The ID of the webhook
schema:
- type: string
- description: "URI encoded file name."
- - in: header
- name: X-Environment-ID
+ $ref: "#/components/schemas/webhookId"
required: true
- schema:
- type: string
- description: "ID of the environment."
- - in: header
- name: X-Signature
- required: true
- schema:
- type: string
- description: "Signature for verifying the request."
- - in: header
- name: X-UUID
- required: true
- schema:
- type: string
- description: "Unique identifier for the signed upload."
- - in: header
- name: X-Timestamp
- required: true
- schema:
- type: string
- description: "Timestamp used for the signature."
requestBody:
required: true
+ description: The webhook to update
content:
- multipart/form-data:
+ application/json:
schema:
type: object
properties:
- file:
+ name:
+ type:
+ - string
+ - "null"
+ description: The name of the webhook
+ url:
type: string
- format: binary
- description: "The file to be uploaded as a valid file object (buffer)."
+ format: uri
+ description: The URL of the webhook
+ source:
+ type: string
+ enum: *a6
+ description: The source of the webhook
+ environmentId:
+ type: string
+ description: The ID of the environment
+ triggers:
+ type: array
+ items:
+ type: string
+ enum: *a7
+ description: The triggers of the webhook
+ surveyIds:
+ type: array
+ items:
+ type: string
+ description: "The IDs of the surveys "
required:
- - file
+ - name
+ - url
+ - source
+ - environmentId
+ - triggers
+ - surveyIds
responses:
"200":
- description: OK - File uploaded successfully.
+ description: Webhook updated successfully.
content:
application/json:
schema:
type: object
properties:
- data:
- type: object
- properties:
- message:
- type: string
- description: Success message.
- example:
- data:
- message: "File uploaded successfully"
- "400":
- description: Bad Request - Missing required fields, invalid header values, or file issues.
- content:
- application/json:
- schema:
- type: object
- properties:
- error:
+ id:
type: string
- description: Detailed error message.
- example:
- error: "fileType is required"
- "401":
- description: Unauthorized - Authentication failed, invalid signature, or user not authorized.
+ description: The ID of the webhook
+ name:
+ type:
+ - string
+ - "null"
+ description: The name of the webhook
+ createdAt:
+ type: string
+ description: The date and time the webhook was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
+ type: string
+ description: The date and time the webhook was last updated
+ example: 2021-01-01T00:00:00.000Z
+ url:
+ type: string
+ format: uri
+ description: The URL of the webhook
+ source:
+ type: string
+ enum: *a6
+ description: The source of the webhook
+ environmentId:
+ type: string
+ description: The ID of the environment
+ triggers:
+ type: array
+ items:
+ type: string
+ enum: *a7
+ description: The triggers of the webhook
+ surveyIds:
+ type: array
+ items:
+ type: string
+ description: "The IDs of the surveys "
+ delete:
+ security:
+ - apiKeyAuth: []
+ operationId: deleteWebhook
+ summary: Delete a webhook
+ description: Deletes a webhook from the database.
+ tags:
+ - Management API > Webhooks
+ parameters:
+ - in: path
+ name: id
+ description: The ID of the webhook
+ schema:
+ $ref: "#/components/schemas/webhookId"
+ required: true
+ responses:
+ "200":
+ description: Webhook deleted successfully.
content:
application/json:
schema:
type: object
properties:
- error:
+ id:
type: string
- description: Detailed error message.
- example:
- error: "Not authenticated"
- "500":
- description: Internal Server Error - File upload failed due to server error.
- content:
- application/json:
- schema:
- type: object
- properties:
- error:
+ description: The ID of the webhook
+ name:
+ type:
+ - string
+ - "null"
+ description: The name of the webhook
+ createdAt:
type: string
- description: Detailed error message.
- example:
- error: "File upload failed"
- servers:
- - url: https://app.formbricks.com/api/v2
- description: Formbricks API Server
+ description: The date and time the webhook was created
+ example: 2021-01-01T00:00:00.000Z
+ updatedAt:
+ type: string
+ description: The date and time the webhook was last updated
+ example: 2021-01-01T00:00:00.000Z
+ url:
+ type: string
+ format: uri
+ description: The URL of the webhook
+ source:
+ type: string
+ enum: *a6
+ description: The source of the webhook
+ environmentId:
+ type: string
+ description: The ID of the environment
+ triggers:
+ type: array
+ items:
+ type: string
+ enum: *a7
+ description: The triggers of the webhook
+ surveyIds:
+ type: array
+ items:
+ type: string
+ description: "The IDs of the surveys "
components:
securitySchemes:
apiKeyAuth:
@@ -1648,14 +2588,7 @@ components:
additionalProperties:
type: string
description: The data of the response
- example: &a2
- question1: answer1
- question2: 2
- question3:
- - answer3
- - answer4
- question4:
- subquestion1: answer5
+ example: *a1
variables:
type: object
additionalProperties:
@@ -1663,17 +2596,13 @@ components:
- type: string
- type: number
description: The variables of the response
- example: &a3
- variable1: answer1
- variable2: 2
+ example: *a2
ttc:
type: object
additionalProperties:
type: number
description: The TTC of the response
- example: &a4
- question1: 10
- question2: 20
+ example: *a3
meta:
type: object
properties:
@@ -1699,15 +2628,7 @@ components:
action:
type: string
description: The meta data of the response
- example: &a5
- source: https://example.com
- url: https://example.com
- userAgent:
- browser: Chrome
- os: Windows
- device: Desktop
- country: US
- action: click
+ example: *a4
contactAttributes:
type:
- object
@@ -1715,9 +2636,7 @@ components:
additionalProperties:
type: string
description: The attributes of the contact
- example:
- attribute1: value1
- attribute2: value2
+ example: *a5
singleUseId:
type:
- string
@@ -2018,7 +2937,7 @@ components:
required:
- id
- type
- default: &a6 []
+ default: &a9 []
description: The endings of the survey
thankYouCard:
type:
@@ -2086,7 +3005,7 @@ components:
description: Survey variables
displayOption:
type: string
- enum: &a7
+ enum: &a10
- displayOnce
- displayMultiple
- displaySome
@@ -2165,7 +3084,7 @@ components:
type:
- string
- "null"
- enum: &a9
+ enum: &a12
- bottomLeft
- bottomRight
- topLeft
@@ -2320,13 +3239,13 @@ components:
properties:
linkSurveys:
type: string
- enum: &a1
+ enum: &a8
- casual
- straight
- simple
appSurveys:
type: string
- enum: *a1
+ enum: *a8
required:
- linkSurveys
- appSurveys
@@ -2343,7 +3262,7 @@ components:
type:
- string
- "null"
- enum: &a8
+ enum: &a11
- animation
- color
- image
@@ -2448,104 +3367,57 @@ components:
- verifyEmail
- displayPercentage
- questions
- responseCreate:
+ webhook:
type: object
properties:
+ id:
+ type: string
+ description: The ID of the webhook
+ name:
+ type:
+ - string
+ - "null"
+ description: The name of the webhook
createdAt:
type: string
- description: The date and time the response was created
+ description: The date and time the webhook was created
example: 2021-01-01T00:00:00.000Z
updatedAt:
type: string
- description: The date and time the response was last updated
+ description: The date and time the webhook was last updated
example: 2021-01-01T00:00:00.000Z
- surveyId:
+ url:
type: string
- description: The ID of the survey
- displayId:
- type:
- - string
- - "null"
- description: The display ID of the response
- singleUseId:
- type:
- - string
- - "null"
- description: The single use ID of the response
- finished:
- type: boolean
- description: Whether the response is finished
- example: true
- endingId:
- type:
- - string
- - "null"
- description: The ID of the ending
- language:
- type:
- - string
- - "null"
- description: The language of the response
- example: en
- data:
- type: object
- additionalProperties:
- anyOf:
- - type: string
- - type: number
- - type: array
- items:
- type: string
- - type: object
- additionalProperties:
- type: string
- description: The data of the response
- example: *a2
- variables:
- type: object
- additionalProperties:
- anyOf:
- - type: string
- - type: number
- description: The variables of the response
- example: *a3
- ttc:
- type: object
- additionalProperties:
- type: number
- description: The TTC of the response
- example: *a4
- meta:
- type: object
- properties:
- source:
- type: string
- description: The source of the response
- example: https://example.com
- url:
- type: string
- description: The URL of the response
- example: https://example.com
- userAgent:
- type: object
- properties:
- browser:
- type: string
- os:
- type: string
- device:
- type: string
- country:
- type: string
- action:
- type: string
- description: The meta data of the response
- example: *a5
+ format: uri
+ description: The URL of the webhook
+ source:
+ type: string
+ enum: *a6
+ description: The source of the webhook
+ environmentId:
+ type: string
+ description: The ID of the environment
+ triggers:
+ type: array
+ items:
+ type: string
+ enum: *a7
+ description: The triggers of the webhook
+ surveyIds:
+ type: array
+ items:
+ type: string
+ description: "The IDs of the surveys "
required:
- - surveyId
- - finished
- - data
- description: A response to create
+ - id
+ - name
+ - createdAt
+ - updatedAt
+ - url
+ - source
+ - environmentId
+ - triggers
+ - surveyIds
responseId:
type: string
description: The ID of the response
@@ -2691,7 +3563,7 @@ components:
required:
- id
- type
- default: *a6
+ default: *a9
description: The endings of the survey
thankYouCard:
type:
@@ -2757,7 +3629,7 @@ components:
description: Survey variables
displayOption:
type: string
- enum: *a7
+ enum: *a10
description: Display options for the survey
recontactDays:
type:
@@ -3017,10 +3889,10 @@ components:
properties:
linkSurveys:
type: string
- enum: *a1
+ enum: *a8
appSurveys:
type: string
- enum: *a1
+ enum: *a8
required:
- linkSurveys
- appSurveys
@@ -3037,7 +3909,7 @@ components:
type:
- string
- "null"
- enum: *a8
+ enum: *a11
brightness:
type:
- number
@@ -3070,7 +3942,7 @@ components:
type:
- string
- "null"
- enum: *a9
+ enum: *a12
clickOutsideClose:
type:
- boolean
@@ -3101,3 +3973,6 @@ components:
surveyId:
type: string
description: The ID of the survey
+ webhookId:
+ type: string
+ description: The ID of the webhook
diff --git a/docs/self-hosting/advanced/license.mdx b/docs/self-hosting/advanced/license.mdx
index 4bdaba2e8f..0712ffbd95 100644
--- a/docs/self-hosting/advanced/license.mdx
+++ b/docs/self-hosting/advanced/license.mdx
@@ -1,5 +1,5 @@
---
-title: 'License'
+title: "License"
description: "License for Formbricks"
icon: "file-certificate"
---
@@ -7,7 +7,9 @@ icon: "file-certificate"
The Formbricks core source code is licensed under AGPLv3 and available on GitHub. Additionally, we offer features for bigger organisations & enterprises under a separate, paid Enterprise License. This assures the long-term sustainability of the open source project. All free features are listed [below](#what-features-are-free).
- Want to get your hands on the Enterprise Edition? [Request a free 60-day Enterprise Edition Trial](https://formbricks.com/enterprise-license?source=docs) License to build a fully functioning Proof of Concept.
+ Want to get your hands on the Enterprise Edition? [Request a free 60-day Enterprise Edition
+ Trial](https://formbricks.com/enterprise-license?source=docs) License to build a fully functioning Proof of
+ Concept.
## Enterprise Edition
@@ -18,20 +20,19 @@ Additional to the AGPLv3 licensed Formbricks core, the Formbricks repository con
| | Community Edition | Enterprise License |
| ------------------------------------------------------------- | ----------------- | ------------------ |
-| Self-host for commercial purposes | ✅ | No license needed |
-| Fork codebase, make changes, release under AGPLv3 | ✅ | No license needed |
-| Fork codebase, make changes, **keep private** | ❌ | ✅ |
-| Unlimited responses | ✅ | No license needed |
-| Unlimited surveys | ✅ | No license needed |
-| Unlimited users | ✅ | No license needed |
+| Self-host for commercial purposes | ✅ | No license needed |
+| Fork codebase, make changes, release under AGPLv3 | ✅ | No license needed |
+| Fork codebase, make changes, **keep private** | ❌ | ✅ |
+| Unlimited responses | ✅ | No license needed |
+| Unlimited surveys | ✅ | No license needed |
+| Unlimited users | ✅ | No license needed |
| Projects | 3 | Unlimited |
-| Use any of the other [free features](#what-features-are-free) | ✅ | No license needed |
-| Remove branding | ❌ | ✅ |
-| SSO | ❌ | ✅ |
-| Contacts & Targeting | ❌ | ✅ |
-| Teams & access roles | ❌ | ✅ |
-| Cluster support | ❌ | ✅ |
-| Use any of the [paid features](#what-features-are-free) | ❌ | ✅ |
+| Use any of the other [free features](#what-features-are-free) | ✅ | No license needed |
+| Remove branding | ❌ | ✅ |
+| SSO | ❌ | ✅ |
+| Contacts & Targeting | ❌ | ✅ |
+| Teams & access roles | ❌ | ✅ |
+| Use any of the [paid features](#what-features-are-free) | ❌ | ✅ |
## Open Core Licensing
@@ -44,7 +45,9 @@ The Formbricks core application is licensed under the [AGPLv3 Open Source Licens
Additional to the AGPL licensed Formbricks core, this repository contains code licensed under an Enterprise license. The [code](https://github.com/formbricks/formbricks/tree/main/apps/web/modules/ee) and [license](https://github.com/formbricks/formbricks/blob/main/apps/web/modules/ee/LICENSE) for the enterprise functionality can be found in the `/apps/web/modules/ee` folder of this repository. This additional functionality is not part of the AGPLv3 licensed Formbricks core and is designed to meet the needs of larger teams and enterprises. This advanced functionality is already included in the Docker images, but you need an [Enterprise License Key](https://formbricks.com/enterprise-license?source=docs) to unlock it.
- Want to get your hands on the Enterprise Edition? [Request a free 60-day Enterprise Edition Trial](https://formbricks.com/enterprise-license?source=docs) License to build a fully functioning Proof of Concept.
+ Want to get your hands on the Enterprise Edition? [Request a free 60-day Enterprise Edition
+ Trial](https://formbricks.com/enterprise-license?source=docs) License to build a fully functioning Proof of
+ Concept.
## White-Labeling Formbricks and Other Licensing Needs
@@ -59,35 +62,35 @@ The Enterprise Edition allows us to fund the development of Formbricks sustainab
| Feature | Community Edition | Enterprise Edition |
| ---------------------------------------------- | ----------------- | ------------------ |
-| Unlimited surveys | ✅ | ✅ |
-| Website & App surveys | ✅ | ✅ |
-| Link surveys | ✅ | ✅ |
-| Email embedded surveys | ✅ | ✅ |
-| Advanced logic | ✅ | ✅ |
-| Custom styling | ✅ | ✅ |
-| Custom URL | ✅ | ✅ |
-| Recall information | ✅ | ✅ |
-| All question types | ✅ | ✅ |
-| Multi-media backgrounds | ✅ | ✅ |
-| Partial responses | ✅ | ✅ |
-| File upload | ✅ | ✅ |
-| Hidden fields | ✅ | ✅ |
-| Single-use links | ✅ | ✅ |
-| Pin-protected surveys | ✅ | ✅ |
-| Full API Access | ✅ | ✅ |
-| All SDKs | ✅ | ✅ |
-| Webhooks | ✅ | ✅ |
-| Email follow-ups | ✅ | ✅ |
-| Multi-language UI | ✅ | ✅ |
-| All integrations (Slack, Zapier, Notion, etc.) | ✅ | ✅ |
-| Hide "Powered by Formbricks" | ❌ | ✅ |
-| Whitelabel email follow-ups | ❌ | ✅ |
-| Teams & access roles | ❌ | ✅ |
-| Contact management & segments | ❌ | ✅ |
-| Multi-language surveys | ❌ | ✅ |
-| OAuth SSO (AzureAD, Google, OpenID) | ❌ | ✅ |
-| SAML SSO | ❌ | ✅ |
-| White-glove onboarding | ❌ | ✅ |
-| Support SLAs | ❌ | ✅ |
+| Unlimited surveys | ✅ | ✅ |
+| Website & App surveys | ✅ | ✅ |
+| Link surveys | ✅ | ✅ |
+| Email embedded surveys | ✅ | ✅ |
+| Advanced logic | ✅ | ✅ |
+| Custom styling | ✅ | ✅ |
+| Custom URL | ✅ | ✅ |
+| Recall information | ✅ | ✅ |
+| All question types | ✅ | ✅ |
+| Multi-media backgrounds | ✅ | ✅ |
+| Partial responses | ✅ | ✅ |
+| File upload | ✅ | ✅ |
+| Hidden fields | ✅ | ✅ |
+| Single-use links | ✅ | ✅ |
+| Pin-protected surveys | ✅ | ✅ |
+| Full API Access | ✅ | ✅ |
+| All SDKs | ✅ | ✅ |
+| Webhooks | ✅ | ✅ |
+| Email follow-ups | ✅ | ✅ |
+| Multi-language UI | ✅ | ✅ |
+| All integrations (Slack, Zapier, Notion, etc.) | ✅ | ✅ |
+| Hide "Powered by Formbricks" | ❌ | ✅ |
+| Whitelabel email follow-ups | ❌ | ✅ |
+| Teams & access roles | ❌ | ✅ |
+| Contact management & segments | ❌ | ✅ |
+| Multi-language surveys | ❌ | ✅ |
+| OAuth SSO (AzureAD, Google, OpenID) | ❌ | ✅ |
+| SAML SSO | ❌ | ✅ |
+| White-glove onboarding | ❌ | ✅ |
+| Support SLAs | ❌ | ✅ |
-**Any more questions?** [Send us an email](mailto:johannes@formbricks.com) or [book a call with us.](https://cal.com/johannes/license)
\ No newline at end of file
+**Any more questions?** [Send us an email](mailto:johannes@formbricks.com) or [book a call with us.](https://cal.com/johannes/license)
diff --git a/docs/self-hosting/setup/kubernetes.mdx b/docs/self-hosting/setup/kubernetes.mdx
index 1b3d337b3f..97a862cd1b 100644
--- a/docs/self-hosting/setup/kubernetes.mdx
+++ b/docs/self-hosting/setup/kubernetes.mdx
@@ -19,17 +19,12 @@ Ensure you have the following before proceeding:
## 1. Installation Steps
-
-```sh
-git clone https://github.com/formbricks/formbricks
-cd helm-chart
-```
-
-
```sh
-helm install formbricks ./ -n formbricks --create-namespace
+helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace
```
+> **Note:** To specify specific version use `--version` flag. E.g., `--version 1.0.0`. Starting from 3.5.0, the chart is available on the GitHub Container Registry (GHCR).
+
By default:
- PostgreSQL and Redis are deployed within the cluster.
- Secrets are dynamically generated and stored as Kubernetes Secrets.
@@ -37,7 +32,7 @@ By default:
```sh
-helm install formbricks ./ -n formbricks --create-namespace --set enterprise.licenseKey="YOUR_LICENSE_KEY"
+helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace --set enterprise.licenseKey="YOUR_LICENSE_KEY"
```
@@ -107,7 +102,7 @@ externalSecret:
Install with:
```sh
-helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
+helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace -f values.yaml
```
---
@@ -129,7 +124,7 @@ redis:
```
Install with:
```sh
-helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
+helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace -f values.yaml
```
---
@@ -147,7 +142,7 @@ redis:
```
Apply with:
```sh
-helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
+helm install formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --create-namespace -f values.yaml
```
---
@@ -155,17 +150,17 @@ helm install formbricks ./ -n formbricks --create-namespace -f values.yaml
## 4. Upgrading the Deployment
To apply changes:
```sh
-helm upgrade formbricks ./ -n formbricks
+helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks
```
### Scaling Resources
```sh
-helm upgrade formbricks ./ -n formbricks --set deployment.resources.limits.cpu=2 --set deployment.resources.limits.memory=4Gi
+helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --set deployment.resources.limits.cpu=2 --set deployment.resources.limits.memory=4Gi
```
### Enabling Autoscaling
```sh
-helm upgrade formbricks ./ -n formbricks --set autoscaling.enabled=true --set autoscaling.minReplicas=3 --set autoscaling.maxReplicas=10
+helm upgrade formbricks oci://ghcr.io/formbricks/helm-charts/formbricks -n formbricks --set autoscaling.enabled=true --set autoscaling.minReplicas=3 --set autoscaling.maxReplicas=10
```
---
diff --git a/docs/xm-and-surveys/surveys/general-features/hidden-fields.mdx b/docs/xm-and-surveys/surveys/general-features/hidden-fields.mdx
index 061dfd5a5f..8f5832ef4d 100644
--- a/docs/xm-and-surveys/surveys/general-features/hidden-fields.mdx
+++ b/docs/xm-and-surveys/surveys/general-features/hidden-fields.mdx
@@ -20,9 +20,7 @@ icon: "eye-slash"

-## Set Hidden Field in Responses
-
-### Link Surveys
+### Set Hidden Field in Link Surveys
Single Hidden Field:
@@ -36,20 +34,8 @@ Multiple Hidden Fields:
sh https://formbricks.com/clin3dxja02k8l80hpwmx4bjy?screen=landing_page&job=Founder
```
-### App & Website Surveys
-
-For in-product surveys, you can set hidden fields in the response by adding them to the `formbricks.track` call:
-
-
- ```JS action.js
- formbricks.track("my event", {
- hiddenFields: {
- screen: "landing_page",
- job: "Founder"
- },
- });
- ```
-
+### Website & App Surveys
+We're reworking our approach to setting hidden fields in Website & App Surveys.
## View Hidden Fields in Responses
diff --git a/helm-chart/templates/deployment.yaml b/helm-chart/templates/deployment.yaml
index 4ae6473200..d82d71025c 100644
--- a/helm-chart/templates/deployment.yaml
+++ b/helm-chart/templates/deployment.yaml
@@ -79,7 +79,7 @@ spec:
terminationGracePeriodSeconds: {{ .Values.deployment.terminationGracePeriodSeconds | default 30 }}
containers:
- name: {{ template "formbricks.name" . }}
- image: "{{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag | default .Chart.AppVersion }}"
+ image: {{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag | default .Chart.AppVersion | default "latest" }}
imagePullPolicy: {{ .Values.deployment.image.pullPolicy }}
{{- if .Values.deployment.command }}
command:
diff --git a/helm-chart/templates/servicemonitor.yaml b/helm-chart/templates/servicemonitor.yaml
new file mode 100644
index 0000000000..3424c43dc2
--- /dev/null
+++ b/helm-chart/templates/servicemonitor.yaml
@@ -0,0 +1,28 @@
+{{- if and (.Values.serviceMonitor).enabled (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1") }}
+---
+apiVersion: "monitoring.coreos.com/v1"
+kind: ServiceMonitor
+metadata:
+ name: {{ template "formbricks.name" . }}-svc-monitor
+ namespace: {{ include "formbricks.namespace" . }}
+ labels:
+ {{- include "formbricks.labels" $ | nindent 4 }}
+{{- if .Values.serviceMonitor.additionalLabels }}
+{{ toYaml .Values.serviceMonitor.additionalLabels | indent 4 }}
+{{- end }}
+{{- if .Values.serviceMonitor.annotations }}
+ annotations:
+{{- end }}
+{{- if or .Values.serviceMonitor.annotations }}
+{{ toYaml .Values.serviceMonitor.annotations | indent 4 }}
+{{- end }}
+spec:
+ selector:
+ matchLabels:
+{{ include "formbricks.labels" $ | indent 6 }}
+ namespaceSelector:
+ matchNames:
+ - {{ include "formbricks.namespace" . }}
+ endpoints:
+{{ toYaml .Values.serviceMonitor.endpoints | indent 4 }}
+{{- end }}
diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml
index c33e1469f6..def45c5aa5 100644
--- a/helm-chart/values.yaml
+++ b/helm-chart/values.yaml
@@ -296,4 +296,4 @@ postgresql:
containerSecurityContext:
enabled: true
runAsUser: 1001
- readOnlyRootFilesystem: false
\ No newline at end of file
+ readOnlyRootFilesystem: false
diff --git a/infra/formbricks-cloud-helm/values.yaml.gotmpl b/infra/formbricks-cloud-helm/values.yaml.gotmpl
index ec5e571ddf..eb57fcec7b 100644
--- a/infra/formbricks-cloud-helm/values.yaml.gotmpl
+++ b/infra/formbricks-cloud-helm/values.yaml.gotmpl
@@ -73,13 +73,17 @@ cronJob:
## Deployment & Autoscaling
deployment:
+ resources:
+ limits:
+ memory: 2Gi
+ requests:
+ cpu: 500m
+ memory: 512Mi
env:
DOCKER_CRON_ENABLED:
value: "0"
RATE_LIMITING_DISABLED:
value: "1"
- S3_BUCKET_NAME:
- value: {{ requiredEnv "FORMBRICKS_S3_BUCKET" }}
envFrom:
app-env:
nameSuffix: app-env
@@ -89,7 +93,7 @@ deployment:
reloadOnChange: true
autoscaling:
enabled: true
- maxReplicas: 10
+ maxReplicas: 95
minReplicas: 3
metrics:
- resource:
diff --git a/infra/terraform/cloudwatch.tf b/infra/terraform/cloudwatch.tf
index ecfe681245..10f591b8ae 100644
--- a/infra/terraform/cloudwatch.tf
+++ b/infra/terraform/cloudwatch.tf
@@ -27,50 +27,63 @@ module "cloudwatch_cis-alarms" {
}
locals {
+ alb_id = "app/k8s-formbricks-21ab9ecd60/342ed65d128ce4cb"
alarms = {
ALB_HTTPCode_Target_5XX_Count = {
alarm_description = "Average API 5XX target group error code count is too high"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 5
- threshold = 1
- period = 60
+ threshold = 5
+ period = 600
unit = "Count"
namespace = "AWS/ApplicationELB"
metric_name = "HTTPCode_Target_5XX_Count"
statistic = "Sum"
+ dimensions = {
+ LoadBalancer = local.alb_id
+ }
}
ALB_HTTPCode_ELB_5XX_Count = {
alarm_description = "Average API 5XX load balancer error code count is too high"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 5
- threshold = 1
- period = 60
+ threshold = 5
+ period = 600
unit = "Count"
namespace = "AWS/ApplicationELB"
metric_name = "HTTPCode_ELB_5XX_Count"
statistic = "Sum"
+ dimensions = {
+ LoadBalancer = local.alb_id
+ }
}
ALB_TargetResponseTime = {
- alarm_description = format("Average API response time is greater than %s", 0.05)
+ alarm_description = format("Average API response time is greater than %s", 5)
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 5
- threshold = 0.05
+ threshold = 5
period = 60
unit = "Seconds"
namespace = "AWS/ApplicationELB"
metric_name = "TargetResponseTime"
statistic = "Average"
+ dimensions = {
+ LoadBalancer = local.alb_id
+ }
}
ALB_UnHealthyHostCount = {
- alarm_description = format("Unhealthy host count is greater than %s", 1)
+ alarm_description = format("Unhealthy host count is greater than %s", 2)
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 5
- threshold = 1
+ threshold = 2
period = 60
unit = "Count"
namespace = "AWS/ApplicationELB"
metric_name = "UnHealthyHostCount"
statistic = "Minimum"
+ dimensions = {
+ LoadBalancer = local.alb_id
+ }
}
RDS_CPUUtilization = {
alarm_description = format("Average RDS CPU utilization is greater than %s", 80)
@@ -82,6 +95,9 @@ locals {
namespace = "AWS/RDS"
metric_name = "CPUUtilization"
statistic = "Average"
+ dimensions = {
+ DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
+ }
}
RDS_FreeStorageSpace = {
alarm_description = format("Average RDS free storage space is less than %s", 5)
@@ -93,6 +109,9 @@ locals {
namespace = "AWS/RDS"
metric_name = "FreeStorageSpace"
statistic = "Average"
+ dimensions = {
+ DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
+ }
}
RDS_FreeableMemory = {
alarm_description = format("Average RDS freeable memory is less than %s", 100)
@@ -104,6 +123,9 @@ locals {
namespace = "AWS/RDS"
metric_name = "FreeableMemory"
statistic = "Average"
+ dimensions = {
+ DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
+ }
}
RDS_DiskQueueDepth = {
alarm_description = format("Average RDS disk queue depth is greater than %s", 1)
@@ -115,6 +137,9 @@ locals {
namespace = "AWS/RDS"
metric_name = "DiskQueueDepth"
statistic = "Average"
+ dimensions = {
+ DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
+ }
}
RDS_ReadIOPS = {
alarm_description = format("Average RDS read IOPS is greater than %s", 1000)
@@ -126,6 +151,9 @@ locals {
namespace = "AWS/RDS"
metric_name = "ReadIOPS"
statistic = "Average"
+ dimensions = {
+ DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
+ }
}
RDS_WriteIOPS = {
alarm_description = format("Average RDS write IOPS is greater than %s", 1000)
@@ -137,6 +165,9 @@ locals {
namespace = "AWS/RDS"
metric_name = "WriteIOPS"
statistic = "Average"
+ dimensions = {
+ DBInstanceIdentifier = module.rds-aurora.cluster_instances["one"].id
+ }
}
SQS_ApproximateAgeOfOldestMessage = {
alarm_description = format("Average SQS approximate age of oldest message is greater than %s", 300)
@@ -148,6 +179,9 @@ locals {
namespace = "AWS/SQS"
metric_name = "ApproximateAgeOfOldestMessage"
statistic = "Maximum"
+ dimensions = {
+ QueueName = module.karpenter.queue_name
+ }
}
DynamoDB_ConsumedReadCapacityUnits = {
alarm_description = format("Average DynamoDB consumed read capacity units is greater than %s", 90)
@@ -159,6 +193,23 @@ locals {
namespace = "AWS/DynamoDB"
metric_name = "ConsumedReadCapacityUnits"
statistic = "Average"
+ dimensions = {
+ TableName = "terraform-lock"
+ }
+ }
+ DynamoDB_ConsumedWriteCapacityUnits = {
+ alarm_description = format("Average DynamoDB consumed write capacity units is greater than %s", 90)
+ comparison_operator = "GreaterThanThreshold"
+ evaluation_periods = 5
+ threshold = 90
+ period = 60
+ unit = "Count"
+ namespace = "AWS/DynamoDB"
+ metric_name = "ConsumedWriteCapacityUnits"
+ statistic = "Average"
+ dimensions = {
+ TableName = "terraform-lock"
+ }
}
Lambda_Errors = {
alarm_description = format("Average Lambda errors is greater than %s", 1)
@@ -170,6 +221,9 @@ locals {
namespace = "AWS/Lambda"
metric_name = "Errors"
statistic = "Sum"
+ dimensions = {
+ FunctionName = module.notify-slack.notify_slack_lambda_function_name
+ }
}
}
}
@@ -178,18 +232,21 @@ module "metric_alarm" {
source = "terraform-aws-modules/cloudwatch/aws//modules/metric-alarm"
version = "5.7.1"
- for_each = local.alarms
- alarm_name = each.key
- alarm_description = each.value.alarm_description
- comparison_operator = each.value.comparison_operator
- evaluation_periods = each.value.evaluation_periods
- threshold = each.value.threshold
- period = each.value.period
- unit = each.value.unit
+ for_each = local.alarms
+ alarm_name = each.key
+ alarm_description = each.value.alarm_description
+ comparison_operator = each.value.comparison_operator
+ evaluation_periods = each.value.evaluation_periods
+ threshold = each.value.threshold
+ period = each.value.period
+ unit = each.value.unit
+ insufficient_data_actions = []
namespace = each.value.namespace
metric_name = each.value.metric_name
statistic = each.value.statistic
+ dimensions = each.value.dimensions
+
alarm_actions = [module.notify-slack.slack_topic_arn]
}
diff --git a/infra/terraform/elasticache.tf b/infra/terraform/elasticache.tf
index 4243d475cb..7426c02ca5 100644
--- a/infra/terraform/elasticache.tf
+++ b/infra/terraform/elasticache.tf
@@ -1,6 +1,10 @@
################################################################################
# ElastiCache Module
################################################################################
+locals {
+ valkey_major_version = 8
+}
+
resource "random_password" "valkey" {
length = 20
special = false
@@ -46,21 +50,67 @@ module "elasticache_user_group" {
})
}
+module "valkey" {
+ source = "terraform-aws-modules/elasticache/aws"
+ version = "1.4.1"
+
+ replication_group_id = "${local.name}-valkey"
+
+ engine = "valkey"
+ engine_version = "8.0"
+ node_type = "cache.m7g.large"
+
+ transit_encryption_enabled = true
+ auth_token = random_password.valkey.result
+ maintenance_window = "sun:05:00-sun:09:00"
+ apply_immediately = true
+
+ # Security Group
+ vpc_id = module.vpc.vpc_id
+ security_group_rules = {
+ ingress_vpc = {
+ # Default type is `ingress`
+ # Default port is based on the default engine port
+ description = "VPC traffic"
+ cidr_ipv4 = module.vpc.vpc_cidr_block
+ }
+ }
+
+ log_delivery_configuration = {
+ slow-log = {
+ destination_type = "cloudwatch-logs"
+ log_format = "json"
+ cloudwatch_log_group_retention_in_days = 365
+ }
+ }
+
+ # Subnet Group
+ subnet_group_name = "${local.name}-valkey"
+ subnet_group_description = "${title(local.name)} subnet group"
+ subnet_ids = module.vpc.database_subnets
+
+ # Parameter Group
+ create_parameter_group = true
+ parameter_group_name = "${local.name}-valkey-${local.valkey_major_version}"
+ parameter_group_family = "valkey8"
+ parameter_group_description = "${title(local.name)} parameter group"
+ parameters = [
+ {
+ name = "latency-tracking"
+ value = "yes"
+ }
+ ]
+
+ tags = local.tags
+}
+
module "valkey_serverless" {
source = "terraform-aws-modules/elasticache/aws//modules/serverless-cache"
version = "1.4.1"
- engine = "valkey"
- cache_name = "${local.name}-valkey-serverless"
- cache_usage_limits = {
- data_storage = {
- maximum = 2
- }
- ecpu_per_second = {
- maximum = 1000
- }
- }
- major_engine_version = 7
+ engine = "valkey"
+ cache_name = "${local.name}-valkey-serverless"
+ major_engine_version = 8
subnet_ids = module.vpc.database_subnets
security_group_ids = [
diff --git a/infra/terraform/main.tf b/infra/terraform/main.tf
index f0a4ade31a..774cd66aa9 100644
--- a/infra/terraform/main.tf
+++ b/infra/terraform/main.tf
@@ -120,6 +120,7 @@ module "eks" {
enable_cluster_creator_admin_permissions = false
cluster_endpoint_public_access = true
+ cloudwatch_log_group_retention_in_days = 365
cluster_addons = {
coredns = {
@@ -352,7 +353,7 @@ resource "kubernetes_manifest" "node_pool" {
}
}
limits = {
- cpu = 100
+ cpu = 1000
}
disruption = {
consolidationPolicy = "WhenEmpty"
@@ -412,19 +413,73 @@ module "eks_blueprints_addons" {
}
### Formbricks App
-module "s3-bucket" {
+data "aws_iam_policy_document" "replication_bucket_policy" {
+ statement {
+ sid = "Set-permissions-for-objects"
+ effect = "Allow"
+
+ principals {
+ type = "AWS"
+ identifiers = [
+ "arn:aws:iam::050559574035:role/service-role/s3crr_role_for_formbricks-cloud-uploads"
+ ]
+ }
+
+ actions = [
+ "s3:ReplicateObject",
+ "s3:ReplicateDelete"
+ ]
+
+ resources = [
+ "arn:aws:s3:::formbricks-cloud-eks/*"
+ ]
+ }
+
+ statement {
+ sid = "Set permissions on bucket"
+ effect = "Allow"
+
+ principals {
+ type = "AWS"
+ identifiers = [
+ "arn:aws:iam::050559574035:role/service-role/s3crr_role_for_formbricks-cloud-uploads"
+ ]
+ }
+
+ actions = [
+ "s3:GetBucketVersioning",
+ "s3:PutBucketVersioning"
+ ]
+
+ resources = [
+ "arn:aws:s3:::formbricks-cloud-eks"
+ ]
+ }
+}
+
+module "formbricks_s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "4.6.0"
- bucket_prefix = "formbricks-"
+ bucket = "formbricks-cloud-eks"
force_destroy = true
control_object_ownership = true
object_ownership = "BucketOwnerPreferred"
-
+ versioning = {
+ enabled = true
+ }
+ policy = data.aws_iam_policy_document.replication_bucket_policy.json
+ cors_rule = [
+ {
+ allowed_methods = ["POST"]
+ allowed_origins = ["https://*"]
+ allowed_headers = ["*"]
+ expose_headers = []
+ }
+ ]
}
-
-module "iam_policy" {
+module "formbricks_app_iam_policy" {
source = "terraform-aws-modules/iam/aws//modules/iam-policy"
version = "5.53.0"
@@ -441,8 +496,8 @@ module "iam_policy" {
"s3:*",
]
Resource = [
- module.s3-bucket.s3_bucket_arn,
- "${module.s3-bucket.s3_bucket_arn}/*",
+ module.formbricks_s3_bucket.s3_bucket_arn,
+ "${module.formbricks_s3_bucket.s3_bucket_arn}/*",
"arn:aws:s3:::formbricks-cloud-uploads",
"arn:aws:s3:::formbricks-cloud-uploads/*"
]
@@ -451,14 +506,14 @@ module "iam_policy" {
})
}
-module "formkey-aws-access" {
+module "formbricks_app_iam_role" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
version = "5.53.0"
role_name_prefix = "formbricks-"
role_policy_arns = {
- "formbricks" = module.iam_policy.arn
+ "formbricks" = module.formbricks_app_iam_policy.arn
}
assume_role_condition_test = "StringLike"
@@ -469,148 +524,3 @@ module "formkey-aws-access" {
}
}
}
-
-
-resource "helm_release" "formbricks" {
- name = "formbricks"
- namespace = "formbricks"
- repository = "oci://ghcr.io/formbricks/helm-charts"
- chart = "formbricks"
- version = "3.5.1"
- max_history = 5
-
- values = [
- <<-EOT
- postgresql:
- enabled: false
- redis:
- enabled: false
- ingress:
- enabled: true
- ingressClassName: alb
- hosts:
- - host: "app.${local.domain}"
- paths:
- - path: /
- pathType: "Prefix"
- serviceName: "formbricks"
- annotations:
- alb.ingress.kubernetes.io/scheme: internet-facing
- alb.ingress.kubernetes.io/target-type: ip
- alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
- alb.ingress.kubernetes.io/ssl-redirect: "443"
- alb.ingress.kubernetes.io/certificate-arn: ${data.aws_acm_certificate.formbricks.arn}
- alb.ingress.kubernetes.io/healthcheck-path: "/health"
- alb.ingress.kubernetes.io/group.name: formbricks
- alb.ingress.kubernetes.io/ssl-policy: "ELBSecurityPolicy-TLS13-1-2-2021-06"
- secret:
- enabled: false
- rbac:
- enabled: true
- serviceAccount:
- enabled: true
- name: formbricks
- annotations:
- eks.amazonaws.com/role-arn: ${module.formkey-aws-access.iam_role_arn}
- serviceMonitor:
- enabled: true
- deployment:
- reloadOnChange: true
- nodeSelector:
- karpenter.sh/capacity-type: "on-demand"
- env:
- S3_BUCKET_NAME:
- value: "formbricks-cloud-uploads"
- RATE_LIMITING_DISABLED:
- value: "1"
- envFrom:
- app-env:
- type: secret
- nameSuffix: app-env
- externalSecret:
- enabled: true # Enable/disable ExternalSecrets
- secretStore:
- name: aws-secrets-manager
- kind: ClusterSecretStore
- refreshInterval: "1m"
- files:
- app-env:
- dataFrom:
- key: "prod/formbricks/environment"
- app-secrets:
- dataFrom:
- key: "prod/formbricks/secrets"
- cronJob:
- enabled: true
- jobs:
- survey-status:
- schedule: "0 0 * * *"
- successfulJobsHistoryLimit: 0
- env:
- CRON_SECRET:
- valueFrom:
- secretKeyRef:
- name: "formbricks-app-env"
- key: "CRON_SECRET"
- WEBAPP_URL:
- valueFrom:
- secretKeyRef:
- name: "formbricks-app-env"
- key: "WEBAPP_URL"
- image:
- repository: curlimages/curl
- tag: latest
- imagePullPolicy: IfNotPresent
- args:
- - "/bin/sh"
- - "-c"
- - 'curl -X POST -H "content-type: application/json" -H "x-api-key: $CRON_SECRET" "$WEBAPP_URL/api/cron/survey-status"'
- weekely-summary:
- schedule: "0 8 * * 1"
- successfulJobsHistoryLimit: 0
- env:
- CRON_SECRET:
- valueFrom:
- secretKeyRef:
- name: "formbricks-app-env"
- key: "CRON_SECRET"
- WEBAPP_URL:
- valueFrom:
- secretKeyRef:
- name: "formbricks-app-env"
- key: "WEBAPP_URL"
- image:
- repository: curlimages/curl
- tag: latest
- imagePullPolicy: IfNotPresent
- args:
- - "/bin/sh"
- - "-c"
- - 'curl -X POST -H "content-type: application/json" -H "x-api-key: $CRON_SECRET" "$WEBAPP_URL/api/cron/weekly-summary"'
- ping:
- schedule: "0 9 * * *"
- successfulJobsHistoryLimit: 0
- env:
- CRON_SECRET:
- valueFrom:
- secretKeyRef:
- name: "formbricks-app-env"
- key: "CRON_SECRET"
- WEBAPP_URL:
- valueFrom:
- secretKeyRef:
- name: "formbricks-app-env"
- key: "WEBAPP_URL"
- image:
- repository: curlimages/curl
- tag: latest
- imagePullPolicy: IfNotPresent
- args:
- - "/bin/sh"
- - "-c"
- - 'curl -X POST -H "content-type: application/json" -H "x-api-key: $CRON_SECRET" "$WEBAPP_URL/api/cron/ping"'
- EOT
- ]
-}
-
-# secrets password/keys
diff --git a/infra/terraform/rds.tf b/infra/terraform/rds.tf
index 2455b737b0..daaab8b76d 100644
--- a/infra/terraform/rds.tf
+++ b/infra/terraform/rds.tf
@@ -15,14 +15,14 @@ module "rds-aurora" {
source = "terraform-aws-modules/rds-aurora/aws"
version = "9.12.0"
- name = "${local.name}-postgres"
- engine = data.aws_rds_engine_version.postgresql.engine
- engine_mode = "provisioned"
- engine_version = data.aws_rds_engine_version.postgresql.version
- storage_encrypted = true
- master_username = "formbricks"
- master_password = random_password.postgres.result
- manage_master_user_password = false
+ name = "${local.name}-postgres"
+ engine = data.aws_rds_engine_version.postgresql.engine
+ engine_mode = "provisioned"
+ engine_version = data.aws_rds_engine_version.postgresql.version
+ storage_encrypted = true
+ master_username = "formbricks"
+ master_password = random_password.postgres.result
+ manage_master_user_password = false
create_db_cluster_parameter_group = true
db_cluster_parameter_group_family = data.aws_rds_engine_version.postgresql.parameter_group_family
db_cluster_parameter_group_parameters = [
@@ -42,14 +42,17 @@ module "rds-aurora" {
}
performance_insights_enabled = true
- apply_immediately = true
- skip_final_snapshot = true
+ backup_retention_period = 7
+ apply_immediately = true
+ skip_final_snapshot = false
+
+ deletion_protection = true
enable_http_endpoint = true
serverlessv2_scaling_configuration = {
min_capacity = 0
- max_capacity = 10
+ max_capacity = 50
seconds_until_auto_pause = 3600
}
diff --git a/infra/terraform/secrets.tf b/infra/terraform/secrets.tf
index 540c98aa00..6f49794d2d 100644
--- a/infra/terraform/secrets.tf
+++ b/infra/terraform/secrets.tf
@@ -3,10 +3,23 @@ resource "aws_secretsmanager_secret" "formbricks_app_secrets" {
name = "prod/formbricks/secrets"
}
+resource "aws_secretsmanager_secret" "formbricks_app_secrets_temp" {
+ name = "prod/formbricks/secrets_temp"
+}
+
resource "aws_secretsmanager_secret_version" "formbricks_app_secrets" {
secret_id = aws_secretsmanager_secret.formbricks_app_secrets.id
secret_string = jsonencode({
-# DATABASE_URL = "postgres://formbricks:${random_password.postgres.result}@${module.rds-aurora.cluster_endpoint}/formbricks"
- REDIS_URL = "rediss://formbricks:${random_password.valkey.result}@${module.valkey_serverless.serverless_cache_endpoint[0].address}:6379"
+ # DATABASE_URL = "postgres://formbricks:${random_password.postgres.result}@${module.rds-aurora.cluster_endpoint}/formbricks"
+ REDIS_URL = "rediss://:${random_password.valkey.result}@${module.valkey.replication_group_primary_endpoint_address}:6379"
+ # REDIS_URL = "rediss://formbricks:${random_password.valkey.result}@${module.valkey_serverless.serverless_cache_endpoint[0].address}:6379"
+ })
+}
+
+resource "aws_secretsmanager_secret_version" "formbricks_app_secrets_temp" {
+ secret_id = aws_secretsmanager_secret.formbricks_app_secrets_temp.id
+ secret_string = jsonencode({
+ DATABASE_URL = "postgres://formbricks:${random_password.postgres.result}@${module.rds-aurora.cluster_endpoint}/formbricks"
+ # REDIS_URL = "rediss://formbricks:${random_password.valkey.result}@${module.valkey_serverless.serverless_cache_endpoint[0].address}:6379"
})
}
diff --git a/packages/api/package.json b/packages/api/package.json
index d46c836cec..844d2323c4 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -40,7 +40,7 @@
"@rollup/plugin-inject": "5.0.5",
"buffer": "6.0.3",
"terser": "5.37.0",
- "vite": "6.0.9",
+ "vite": "6.0.12",
"vite-plugin-dts": "4.3.0",
"vite-plugin-node-polyfills": "0.22.0"
}
diff --git a/packages/database/migration/20250324134815_set_webhook_updated_at_default/migration.sql b/packages/database/migration/20250324134815_set_webhook_updated_at_default/migration.sql
new file mode 100644
index 0000000000..5b14772915
--- /dev/null
+++ b/packages/database/migration/20250324134815_set_webhook_updated_at_default/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "Webhook" ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP;
diff --git a/packages/database/schema.prisma b/packages/database/schema.prisma
index d27fb82741..d0992cb188 100644
--- a/packages/database/schema.prisma
+++ b/packages/database/schema.prisma
@@ -48,7 +48,7 @@ model Webhook {
id String @id @default(cuid())
name String?
createdAt DateTime @default(now()) @map(name: "created_at")
- updatedAt DateTime @updatedAt @map(name: "updated_at")
+ updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at")
url String
source WebhookSource @default(user)
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
diff --git a/packages/database/types/error.ts b/packages/database/types/error.ts
new file mode 100644
index 0000000000..c9a98773a9
--- /dev/null
+++ b/packages/database/types/error.ts
@@ -0,0 +1,5 @@
+export enum PrismaErrorType {
+ UniqueConstraintViolation = "P2002",
+ RecordDoesNotExist = "P2015",
+ RelatedRecordDoesNotExist = "P2025",
+}
diff --git a/packages/database/zod/organizations.ts b/packages/database/zod/organizations.ts
new file mode 100644
index 0000000000..a194d9a63f
--- /dev/null
+++ b/packages/database/zod/organizations.ts
@@ -0,0 +1,41 @@
+import type { Organization } from "@prisma/client";
+import { z } from "zod";
+import { extendZodWithOpenApi } from "zod-openapi";
+
+extendZodWithOpenApi(z);
+
+export const ZOrganizationWhiteLabel = z.object({
+ logoUrl: z.string().nullable(),
+});
+
+export const ZOrganizationBilling = z.object({
+ stripeCustomerId: z.string().nullable(),
+ plan: z.enum(["free", "startup", "scale", "enterprise"]).default("free"),
+ period: z.enum(["monthly", "yearly"]).default("monthly"),
+ limits: z
+ .object({
+ projects: z.number().nullable(),
+ monthly: z.object({
+ responses: z.number().nullable(),
+ miu: z.number().nullable(),
+ }),
+ })
+ .default({
+ projects: 3,
+ monthly: {
+ responses: 1500,
+ miu: 2000,
+ },
+ }),
+ periodStart: z.coerce.date().nullable(),
+});
+
+export const ZOrganization = z.object({
+ id: z.string().cuid2(),
+ createdAt: z.coerce.date(),
+ updatedAt: z.coerce.date(),
+ name: z.string(),
+ whitelabel: ZOrganizationWhiteLabel,
+ billing: ZOrganizationBilling as z.ZodType,
+ isAIEnabled: z.boolean().default(false) as z.ZodType,
+}) satisfies z.ZodType;
diff --git a/packages/database/zod/webhooks.ts b/packages/database/zod/webhooks.ts
index 959a3fd409..32950d6854 100644
--- a/packages/database/zod/webhooks.ts
+++ b/packages/database/zod/webhooks.ts
@@ -1,14 +1,42 @@
import type { Webhook } from "@prisma/client";
import { z } from "zod";
+import { extendZodWithOpenApi } from "zod-openapi";
+
+extendZodWithOpenApi(z);
export const ZWebhook = z.object({
- id: z.string().cuid2(),
- name: z.string().nullable(),
- createdAt: z.date(),
- updatedAt: z.date(),
- url: z.string().url(),
- source: z.enum(["user", "zapier", "make", "n8n"]),
- environmentId: z.string().cuid2(),
- triggers: z.array(z.enum(["responseFinished", "responseCreated", "responseUpdated"])),
- surveyIds: z.array(z.string().cuid2()),
+ id: z.string().cuid2().openapi({
+ description: "The ID of the webhook",
+ }),
+ name: z.string().nullable().openapi({
+ description: "The name of the webhook",
+ }),
+ createdAt: z.date().openapi({
+ description: "The date and time the webhook was created",
+ example: "2021-01-01T00:00:00.000Z",
+ }),
+ updatedAt: z.date().openapi({
+ description: "The date and time the webhook was last updated",
+ example: "2021-01-01T00:00:00.000Z",
+ }),
+ url: z.string().url().openapi({
+ description: "The URL of the webhook",
+ }),
+ source: z.enum(["user", "zapier", "make", "n8n"]).openapi({
+ description: "The source of the webhook",
+ }),
+ environmentId: z.string().cuid2().openapi({
+ description: "The ID of the environment",
+ }),
+ triggers: z.array(z.enum(["responseFinished", "responseCreated", "responseUpdated"])).openapi({
+ description: "The triggers of the webhook",
+ }),
+ surveyIds: z.array(z.string().cuid2()).openapi({
+ description: "The IDs of the surveys ",
+ }),
}) satisfies z.ZodType;
+
+ZWebhook.openapi({
+ ref: "webhook",
+ description: "A webhook",
+});
diff --git a/packages/js-core/package.json b/packages/js-core/package.json
index 9567464d8c..d929e164a8 100644
--- a/packages/js-core/package.json
+++ b/packages/js-core/package.json
@@ -48,7 +48,7 @@
"@formbricks/eslint-config": "workspace:*",
"@vitest/coverage-v8": "3.0.7",
"terser": "5.37.0",
- "vite": "6.0.9",
+ "vite": "6.0.12",
"vite-plugin-dts": "4.3.0",
"vitest": "3.0.6"
}
diff --git a/packages/js/package.json b/packages/js/package.json
index ed4d8808f2..7f1f4038b1 100644
--- a/packages/js/package.json
+++ b/packages/js/package.json
@@ -44,7 +44,7 @@
"@formbricks/config-typescript": "workspace:*",
"@formbricks/eslint-config": "workspace:*",
"terser": "5.37.0",
- "vite": "6.0.9",
+ "vite": "6.0.12",
"vite-plugin-dts": "4.3.0"
}
}
diff --git a/packages/lib/actionClass/service.ts b/packages/lib/actionClass/service.ts
index ec2ed407cf..50c6c87972 100644
--- a/packages/lib/actionClass/service.ts
+++ b/packages/lib/actionClass/service.ts
@@ -4,9 +4,9 @@ import "server-only";
import { ActionClass, Prisma } from "@prisma/client";
import { cache as reactCache } from "react";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { TActionClass, TActionClassInput, ZActionClassInput } from "@formbricks/types/action-classes";
-import { ZOptionalNumber, ZString } from "@formbricks/types/common";
-import { ZId } from "@formbricks/types/common";
+import { ZId, ZOptionalNumber, ZString } from "@formbricks/types/common";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { cache } from "../cache";
import { ITEMS_PER_PAGE } from "../constants";
@@ -163,7 +163,10 @@ export const createActionClass = async (
return actionClassPrisma;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.UniqueConstraintViolation
+ ) {
throw new DatabaseError(
`Action with ${error.meta?.target?.[0]} ${actionClass[error.meta?.target?.[0]]} already exists`
);
@@ -219,7 +222,10 @@ export const updateActionClass = async (
return result;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.UniqueConstraintViolation
+ ) {
throw new DatabaseError(
`Action with ${error.meta?.target?.[0]} ${inputActionClass[error.meta?.target?.[0]]} already exists`
);
diff --git a/packages/lib/constants.ts b/packages/lib/constants.ts
index 23e218c78d..3601858249 100644
--- a/packages/lib/constants.ts
+++ b/packages/lib/constants.ts
@@ -289,3 +289,7 @@ export const IS_TURNSTILE_CONFIGURED = Boolean(env.NEXT_PUBLIC_TURNSTILE_SITE_KE
export const IS_PRODUCTION = env.NODE_ENV === "production";
export const IS_DEVELOPMENT = env.NODE_ENV === "development";
+
+export const SENTRY_DSN = env.SENTRY_DSN;
+
+export const PROMETHEUS_ENABLED = env.PROMETHEUS_ENABLED === "1";
diff --git a/packages/lib/display/tests/display.test.ts b/packages/lib/display/tests/display.test.ts
index 94816accf2..bf3b15a279 100644
--- a/packages/lib/display/tests/display.test.ts
+++ b/packages/lib/display/tests/display.test.ts
@@ -10,6 +10,7 @@ import {
import { Prisma } from "@prisma/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { testInputValidation } from "vitestSetup";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { DatabaseError } from "@formbricks/types/errors";
import { createDisplay } from "../../../../apps/web/app/api/v1/client/[environmentId]/displays/lib/display";
import { deleteDisplay } from "../service";
@@ -52,7 +53,7 @@ describe("Tests for createDisplay service", () => {
const mockErrorMessage = "Mock error message";
prisma.environment.findUnique.mockResolvedValue(mockEnvironment);
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
@@ -83,7 +84,7 @@ describe("Tests for delete display service", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError occurrence", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
diff --git a/packages/lib/env.ts b/packages/lib/env.ts
index b620579af8..e3318e6bd9 100644
--- a/packages/lib/env.ts
+++ b/packages/lib/env.ts
@@ -17,7 +17,7 @@ export const env = createEnv({
AZUREAD_CLIENT_ID: z.string().optional(),
AZUREAD_CLIENT_SECRET: z.string().optional(),
AZUREAD_TENANT_ID: z.string().optional(),
- CRON_SECRET: z.string().min(10),
+ CRON_SECRET: z.string().optional(),
BREVO_API_KEY: z.string().optional(),
BREVO_LIST_ID: z.string().optional(),
DATABASE_URL: z.string().url(),
@@ -28,9 +28,9 @@ export const env = createEnv({
E2E_TESTING: z.enum(["1", "0"]).optional(),
EMAIL_AUTH_DISABLED: z.enum(["1", "0"]).optional(),
EMAIL_VERIFICATION_DISABLED: z.enum(["1", "0"]).optional(),
- ENCRYPTION_KEY: z.string().length(64).or(z.string().length(32)),
+ ENCRYPTION_KEY: z.string(),
ENTERPRISE_LICENSE_KEY: z.string().optional(),
- FORMBRICKS_ENCRYPTION_KEY: z.string().length(24).or(z.string().length(0)).optional(),
+ FORMBRICKS_ENCRYPTION_KEY: z.string().optional(),
GITHUB_ID: z.string().optional(),
GITHUB_SECRET: z.string().optional(),
GOOGLE_CLIENT_ID: z.string().optional(),
@@ -52,8 +52,8 @@ export const env = createEnv({
IS_FORMBRICKS_CLOUD: z.enum(["1", "0"]).optional(),
LOG_LEVEL: z.enum(["debug", "info", "warn", "error", "fatal"]).optional(),
MAIL_FROM: z.string().email().optional(),
+ NEXTAUTH_SECRET: z.string().optional(),
MAIL_FROM_NAME: z.string().optional(),
- NEXTAUTH_SECRET: z.string().min(1),
NOTION_OAUTH_CLIENT_ID: z.string().optional(),
NOTION_OAUTH_CLIENT_SECRET: z.string().optional(),
OIDC_CLIENT_ID: z.string().optional(),
@@ -81,6 +81,7 @@ export const env = createEnv({
S3_ENDPOINT_URL: z.string().optional(),
S3_FORCE_PATH_STYLE: z.enum(["1", "0"]).optional(),
SAML_DATABASE_URL: z.string().optional(),
+ SENTRY_DSN: z.string().optional(),
SIGNUP_DISABLED: z.enum(["1", "0"]).optional(),
SLACK_CLIENT_ID: z.string().optional(),
SLACK_CLIENT_SECRET: z.string().optional(),
@@ -126,7 +127,6 @@ export const env = createEnv({
.or(z.string().refine((str) => str === "")),
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID: z.string().optional(),
NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID: z.string().optional(),
- NEXT_PUBLIC_SENTRY_DSN: z.string().optional(),
NEXT_PUBLIC_TURNSTILE_SITE_KEY: z.string().optional(),
},
/*
@@ -184,9 +184,9 @@ export const env = createEnv({
NEXT_PUBLIC_FORMBRICKS_API_HOST: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID: process.env.NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID,
+ SENTRY_DSN: process.env.SENTRY_DSN,
POSTHOG_API_KEY: process.env.POSTHOG_API_KEY,
POSTHOG_API_HOST: process.env.POSTHOG_API_HOST,
- NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
NEXT_PUBLIC_TURNSTILE_SITE_KEY: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY,
OPENTELEMETRY_LISTENER_URL: process.env.OPENTELEMETRY_LISTENER_URL,
INTERCOM_APP_ID: process.env.INTERCOM_APP_ID,
diff --git a/packages/lib/jwt.ts b/packages/lib/jwt.ts
index 235d263ea0..07af577b13 100644
--- a/packages/lib/jwt.ts
+++ b/packages/lib/jwt.ts
@@ -5,20 +5,44 @@ import { symmetricDecrypt, symmetricEncrypt } from "./crypto";
import { env } from "./env";
export const createToken = (userId: string, userEmail: string, options = {}): string => {
+ if (!env.ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
+
const encryptedUserId = symmetricEncrypt(userId, env.ENCRYPTION_KEY);
return jwt.sign({ id: encryptedUserId }, env.NEXTAUTH_SECRET + userEmail, options);
};
export const createTokenForLinkSurvey = (surveyId: string, userEmail: string): string => {
+ if (!env.ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
+
const encryptedEmail = symmetricEncrypt(userEmail, env.ENCRYPTION_KEY);
return jwt.sign({ email: encryptedEmail }, env.NEXTAUTH_SECRET + surveyId);
};
export const createEmailToken = (email: string): string => {
+ if (!env.ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
+
+ if (!env.NEXTAUTH_SECRET) {
+ throw new Error("NEXTAUTH_SECRET is not set");
+ }
+
const encryptedEmail = symmetricEncrypt(email, env.ENCRYPTION_KEY);
return jwt.sign({ email: encryptedEmail }, env.NEXTAUTH_SECRET);
};
export const getEmailFromEmailToken = (token: string): string => {
+ if (!env.ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
+
+ if (!env.NEXTAUTH_SECRET) {
+ throw new Error("NEXTAUTH_SECRET is not set");
+ }
+
const payload = jwt.verify(token, env.NEXTAUTH_SECRET) as JwtPayload;
try {
// Try to decrypt first (for newer tokens)
@@ -31,6 +55,13 @@ export const getEmailFromEmailToken = (token: string): string => {
};
export const createInviteToken = (inviteId: string, email: string, options = {}): string => {
+ if (!env.ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
+
+ if (!env.NEXTAUTH_SECRET) {
+ throw new Error("NEXTAUTH_SECRET is not set");
+ }
const encryptedInviteId = symmetricEncrypt(inviteId, env.ENCRYPTION_KEY);
const encryptedEmail = symmetricEncrypt(email, env.ENCRYPTION_KEY);
return jwt.sign({ inviteId: encryptedInviteId, email: encryptedEmail }, env.NEXTAUTH_SECRET, options);
@@ -41,6 +72,9 @@ export const verifyTokenForLinkSurvey = (token: string, surveyId: string): strin
const { email } = jwt.verify(token, env.NEXTAUTH_SECRET + surveyId) as JwtPayload;
try {
// Try to decrypt first (for newer tokens)
+ if (!env.ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
const decryptedEmail = symmetricDecrypt(email, env.ENCRYPTION_KEY);
return decryptedEmail;
} catch {
@@ -53,6 +87,9 @@ export const verifyTokenForLinkSurvey = (token: string, surveyId: string): strin
};
export const verifyToken = async (token: string): Promise => {
+ if (!env.ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
// First decode to get the ID
const decoded = jwt.decode(token);
const payload: JwtPayload = decoded as JwtPayload;
@@ -90,6 +127,10 @@ export const verifyToken = async (token: string): Promise => {
export const verifyInviteToken = (token: string): { inviteId: string; email: string } => {
try {
+ if (!env.ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
+
const decoded = jwt.decode(token);
const payload: JwtPayload = decoded as JwtPayload;
diff --git a/packages/lib/language/tests/language.unit.ts b/packages/lib/language/tests/language.unit.ts
index 438f528aef..d678c173c2 100644
--- a/packages/lib/language/tests/language.unit.ts
+++ b/packages/lib/language/tests/language.unit.ts
@@ -9,6 +9,7 @@ import {
} from "./__mocks__/data.mock";
import { Prisma } from "@prisma/client";
import { prismaMock } from "@formbricks/database/src/jestClient";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { DatabaseError, ValidationError } from "@formbricks/types/errors";
import { createLanguage, deleteLanguage, updateLanguage } from "../service";
@@ -34,7 +35,7 @@ describe("Tests for createLanguage service", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError occurrence", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
@@ -72,7 +73,7 @@ describe("Tests for updateLanguage Service", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
@@ -109,7 +110,7 @@ describe("Tests for deleteLanguage", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError occurrence", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
diff --git a/packages/lib/messages/de-DE.json b/packages/lib/messages/de-DE.json
index d0d44a15a0..b89826998a 100644
--- a/packages/lib/messages/de-DE.json
+++ b/packages/lib/messages/de-DE.json
@@ -1029,6 +1029,8 @@
"monthly": "Monatlich",
"monthly_identified_users": "Monatlich identifizierte Nutzer",
"multi_language_surveys": "Mehrsprachige Umfragen",
+ "per_month": "pro Monat",
+ "per_year": "pro Jahr",
"plan_upgraded_successfully": "Plan erfolgreich aktualisiert",
"premium_support_with_slas": "Premium-Support mit SLAs",
"priority_support": "Priorisierter Support",
@@ -1039,7 +1041,7 @@
"startup": "Start-up",
"startup_description": "Alles in 'Free' mit zusätzlichen Funktionen.",
"switch_plan": "Plan wechseln",
- "switch_plan_confirmation_text": "Bist du sicher, dass du zum {plan}-Plan wechseln möchtest? Dir werden {price} pro Monat berechnet.",
+ "switch_plan_confirmation_text": "Bist du sicher, dass du zum {plan}-Plan wechseln möchtest? Dir werden {price} {period} berechnet.",
"team_access_roles": "Rollen für Teammitglieder",
"technical_onboarding": "Technische Einführung",
"unable_to_upgrade_plan": "Plan kann nicht aktualisiert werden",
diff --git a/packages/lib/messages/en-US.json b/packages/lib/messages/en-US.json
index a5af8745e2..0ebab246cb 100644
--- a/packages/lib/messages/en-US.json
+++ b/packages/lib/messages/en-US.json
@@ -1029,6 +1029,8 @@
"monthly": "Monthly",
"monthly_identified_users": "Monthly Identified Users",
"multi_language_surveys": "Multi-Language Surveys",
+ "per_month": "per month",
+ "per_year": "per year",
"plan_upgraded_successfully": "Plan upgraded successfully",
"premium_support_with_slas": "Premium support with SLAs",
"priority_support": "Priority Support",
@@ -1039,7 +1041,7 @@
"startup": "Startup",
"startup_description": "Everything in Free with additional features.",
"switch_plan": "Switch Plan",
- "switch_plan_confirmation_text": "Are you sure you want to switch to the {plan} plan? You will be charged {price} per month.",
+ "switch_plan_confirmation_text": "Are you sure you want to switch to the {plan} plan? You will be charged {price} {period}.",
"team_access_roles": "Team Access Roles",
"technical_onboarding": "Technical Onboarding",
"unable_to_upgrade_plan": "Unable to upgrade plan",
diff --git a/packages/lib/messages/fr-FR.json b/packages/lib/messages/fr-FR.json
index 60318da172..88750c2443 100644
--- a/packages/lib/messages/fr-FR.json
+++ b/packages/lib/messages/fr-FR.json
@@ -1029,6 +1029,8 @@
"monthly": "Mensuel",
"monthly_identified_users": "Utilisateurs Identifiés Mensuels",
"multi_language_surveys": "Sondages multilingues",
+ "per_month": "par mois",
+ "per_year": "par an",
"plan_upgraded_successfully": "Plan mis à jour avec succès",
"premium_support_with_slas": "Soutien premium avec SLA",
"priority_support": "Soutien Prioritaire",
@@ -1039,7 +1041,7 @@
"startup": "Startup",
"startup_description": "Tout est gratuit avec des fonctionnalités supplémentaires.",
"switch_plan": "Changer de plan",
- "switch_plan_confirmation_text": "Êtes-vous sûr de vouloir passer au plan {plan} ? Vous serez facturé {price} par mois.",
+ "switch_plan_confirmation_text": "Êtes-vous sûr de vouloir passer au plan {plan} ? Vous serez facturé {price} {period}.",
"team_access_roles": "Rôles d'accès d'équipe",
"technical_onboarding": "Intégration technique",
"unable_to_upgrade_plan": "Impossible de mettre à niveau le plan",
diff --git a/packages/lib/messages/pt-BR.json b/packages/lib/messages/pt-BR.json
index 6d44aeda41..e9aa823ab2 100644
--- a/packages/lib/messages/pt-BR.json
+++ b/packages/lib/messages/pt-BR.json
@@ -1029,6 +1029,8 @@
"monthly": "mensal",
"monthly_identified_users": "Usuários Identificados Mensalmente",
"multi_language_surveys": "Pesquisas Multilíngues",
+ "per_month": "por mês",
+ "per_year": "por ano",
"plan_upgraded_successfully": "Plano atualizado com sucesso",
"premium_support_with_slas": "Suporte premium com SLAs",
"priority_support": "Suporte Prioritário",
@@ -1039,7 +1041,7 @@
"startup": "startup",
"startup_description": "Tudo no Grátis com recursos adicionais.",
"switch_plan": "Mudar Plano",
- "switch_plan_confirmation_text": "Tem certeza de que deseja mudar para o plano {plan}? Você será cobrado {price} por mês.",
+ "switch_plan_confirmation_text": "Tem certeza de que deseja mudar para o plano {plan}? Você será cobrado {price} {period}.",
"team_access_roles": "Funções de Acesso da Equipe",
"technical_onboarding": "Integração Técnica",
"unable_to_upgrade_plan": "Não foi possível atualizar o plano",
diff --git a/packages/lib/messages/pt-PT.json b/packages/lib/messages/pt-PT.json
index c6cae13ce3..98860b6f64 100644
--- a/packages/lib/messages/pt-PT.json
+++ b/packages/lib/messages/pt-PT.json
@@ -1029,6 +1029,8 @@
"monthly": "Mensal",
"monthly_identified_users": "Utilizadores Identificados Mensalmente",
"multi_language_surveys": "Inquéritos Multilingues",
+ "per_month": "por mês",
+ "per_year": "por ano",
"plan_upgraded_successfully": "Plano atualizado com sucesso",
"premium_support_with_slas": "Suporte premium com SLAs",
"priority_support": "Suporte Prioritário",
@@ -1039,7 +1041,7 @@
"startup": "Inicialização",
"startup_description": "Tudo no plano Gratuito com funcionalidades adicionais.",
"switch_plan": "Mudar Plano",
- "switch_plan_confirmation_text": "Tem a certeza de que deseja mudar para o plano {plan}? Ser-lhe-á cobrado {price} por mês.",
+ "switch_plan_confirmation_text": "Tem a certeza de que deseja mudar para o plano {plan}? Ser-lhe-á cobrado {price} {period}.",
"team_access_roles": "Funções de Acesso da Equipa",
"technical_onboarding": "Integração Técnica",
"unable_to_upgrade_plan": "Não é possível atualizar o plano",
diff --git a/packages/lib/messages/zh-Hant-TW.json b/packages/lib/messages/zh-Hant-TW.json
index 019008000d..7a155c79a2 100644
--- a/packages/lib/messages/zh-Hant-TW.json
+++ b/packages/lib/messages/zh-Hant-TW.json
@@ -1029,6 +1029,8 @@
"monthly": "每月",
"monthly_identified_users": "每月識別使用者",
"multi_language_surveys": "多語言問卷",
+ "per_month": "每月",
+ "per_year": "每年",
"plan_upgraded_successfully": "方案已成功升級",
"premium_support_with_slas": "具有 SLA 的頂級支援",
"priority_support": "優先支援",
@@ -1039,7 +1041,7 @@
"startup": "啟動版",
"startup_description": "免費方案中的所有功能以及其他功能。",
"switch_plan": "切換方案",
- "switch_plan_confirmation_text": "您確定要切換至 '{'plan'}' 方案嗎?您將每月被收取 '{'price'}'。",
+ "switch_plan_confirmation_text": "您確定要切換到 {plan} 計劃嗎?您將被收取 {price} {period}。",
"team_access_roles": "團隊存取角色",
"technical_onboarding": "技術新手上路",
"unable_to_upgrade_plan": "無法升級方案",
diff --git a/packages/lib/organization/service.ts b/packages/lib/organization/service.ts
index 31698d0d0c..7d42053fff 100644
--- a/packages/lib/organization/service.ts
+++ b/packages/lib/organization/service.ts
@@ -2,9 +2,9 @@ import "server-only";
import { Prisma } from "@prisma/client";
import { cache as reactCache } from "react";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { logger } from "@formbricks/logger";
-import { ZOptionalNumber, ZString } from "@formbricks/types/common";
-import { ZId } from "@formbricks/types/common";
+import { ZId, ZOptionalNumber, ZString } from "@formbricks/types/common";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import {
TOrganization,
@@ -224,7 +224,10 @@ export const updateOrganization = async (
return organization;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.RecordDoesNotExist
+ ) {
throw new ResourceNotFoundError("Organization", organizationId);
}
throw error; // Re-throw any other errors
diff --git a/packages/lib/response/tests/response.test.ts b/packages/lib/response/tests/response.test.ts
index 492dd6e74d..b64b3b5d85 100644
--- a/packages/lib/response/tests/response.test.ts
+++ b/packages/lib/response/tests/response.test.ts
@@ -1,27 +1,23 @@
import { prisma } from "../../__mocks__/database";
import {
- // getFilteredMockResponses,
getMockUpdateResponseInput,
mockContact,
mockDisplay,
mockEnvironmentId,
- mockMeta,
mockResponse,
mockResponseData,
mockResponseNote,
- // mockResponseWithMockPerson,
mockSingleUseId,
- // mockSurvey,
mockSurveyId,
mockSurveySummaryOutput,
mockTags,
- mockUserId,
} from "./__mocks__/data.mock";
import { Prisma } from "@prisma/client";
import { beforeEach, describe, expect, it } from "vitest";
import { testInputValidation } from "vitestSetup";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
-import { TResponse, TResponseInput } from "@formbricks/types/responses";
+import { TResponse } from "@formbricks/types/responses";
import { TTag } from "@formbricks/types/tags";
import { getSurveySummary } from "../../../../apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/surveySummary";
import {
@@ -35,20 +31,9 @@ import {
getResponseBySingleUseId,
getResponseCountBySurveyId,
getResponseDownloadUrl,
- getResponses,
- getResponsesByContactId,
getResponsesByEnvironmentId,
updateResponse,
} from "../service";
-import { buildWhereClause } from "../utils";
-import { constantsForTests, mockEnvironment } from "./constants";
-
-// vitest.mock("../../organization/service", async (methods) => {
-// return {
-// ...methods,
-// getOrganizationByEnvironmentId: vitest.fn(),
-// };
-// });
const expectedResponseWithoutPerson: TResponse = {
...mockResponse,
@@ -56,26 +41,6 @@ const expectedResponseWithoutPerson: TResponse = {
tags: mockTags.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
};
-const expectedResponseWithPerson: TResponse = {
- ...mockResponse,
- contact: mockContact,
- tags: mockTags?.map((tagPrisma: { tag: TTag }) => tagPrisma.tag),
-};
-
-const mockResponseInputWithoutUserId: TResponseInput = {
- environmentId: mockEnvironmentId,
- surveyId: mockSurveyId,
- singleUseId: mockSingleUseId,
- finished: constantsForTests.boolean,
- data: {},
- meta: mockMeta,
-};
-
-const mockResponseInputWithUserId: TResponseInput = {
- ...mockResponseInputWithoutUserId,
- userId: mockUserId,
-};
-
beforeEach(() => {
// @ts-expect-error
prisma.response.create.mockImplementation(async (args) => {
@@ -126,47 +91,6 @@ beforeEach(() => {
prisma.response.aggregate.mockResolvedValue({ _count: { id: 1 } });
});
-// describe("Tests for getResponsesByPersonId", () => {
-// describe("Happy Path", () => {
-// it("Returns all responses associated with a given person ID", async () => {
-// prisma.response.findMany.mockResolvedValue([mockResponseWithMockPerson]);
-
-// const responses = await getResponsesByContactId(mockContact.id);
-// expect(responses).toEqual([expectedResponseWithPerson]);
-// });
-
-// it("Returns an empty array when no responses are found for the given person ID", async () => {
-// prisma.response.findMany.mockResolvedValue([]);
-
-// const responses = await getResponsesByContactId(mockContact.id);
-// expect(responses).toEqual([]);
-// });
-// });
-
-// describe("Sad Path", () => {
-// testInputValidation(getResponsesByContactId, "123#", 1);
-
-// it("Throws a DatabaseError error if there is a PrismaClientKnownRequestError", async () => {
-// const mockErrorMessage = "Mock error message";
-// const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
-// code: "P2002",
-// clientVersion: "0.0.1",
-// });
-
-// prisma.response.findMany.mockRejectedValue(errToThrow);
-
-// await expect(getResponsesByContactId(mockContact.id)).rejects.toThrow(DatabaseError);
-// });
-
-// it("Throws a generic Error for unexpected exceptions", async () => {
-// const mockErrorMessage = "Mock error message";
-// prisma.response.findMany.mockRejectedValue(new Error(mockErrorMessage));
-
-// await expect(getResponsesByContactId(mockContact.id)).rejects.toThrow(Error);
-// });
-// });
-// });
-
describe("Tests for getResponsesBySingleUseId", () => {
describe("Happy Path", () => {
it("Retrieves responses linked to a specific single-use ID", async () => {
@@ -181,7 +105,7 @@ describe("Tests for getResponsesBySingleUseId", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError occurrence", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
@@ -219,7 +143,7 @@ describe("Tests for getResponse service", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
@@ -237,121 +161,6 @@ describe("Tests for getResponse service", () => {
});
});
-// describe("Tests for getResponses service", () => {
-// describe("Happy Path", () => {
-// it("Fetches first 10 responses for a given survey ID", async () => {
-// prisma.survey.findUnique.mockResolvedValue(mockSurveyOutput);
-
-// const response = await getResponses(mockSurveyId, 1, 10);
-// expect(response).toEqual([expectedResponseWithoutPerson]);
-// });
-// });
-
-// describe("Tests for getResponses service with filters", () => {
-// describe("Happy Path", () => {
-// // it("Fetches all responses for a given survey ID with basic filters", async () => {
-// // const whereClause = buildWhereClause(mockSurvey, { finished: true });
-// // let expectedWhereClause: Prisma.ResponseWhereInput | undefined = {};
-
-// // // @ts-expect-error
-// // prisma.response.findMany.mockImplementation(async (args) => {
-// // expectedWhereClause = args?.where;
-// // return getFilteredMockResponses({ finished: true }, false);
-// // });
-
-// // prisma.survey.findUnique.mockResolvedValue(mockSurveyOutput);
-// // const response = await getResponses(mockSurveyId, 1, undefined, { finished: true });
-
-// // expect(expectedWhereClause).toEqual({ surveyId: mockSurveyId, ...whereClause });
-// // expect(response).toEqual(getFilteredMockResponses({ finished: true }));
-// // });
-
-// it("Fetches all responses for a given survey ID with complex filters", async () => {
-// const criteria: TResponseFilterCriteria = {
-// finished: false,
-// data: {
-// hagrboqlnynmxh3obl1wvmtl: {
-// op: "equals",
-// value: "Google Search",
-// },
-// uvy0fa96e1xpd10nrj1je662: {
-// op: "includesOne",
-// value: ["Sun ☀️"],
-// },
-// },
-// tags: {
-// applied: ["tag1"],
-// notApplied: ["tag4"],
-// },
-// contactAttributes: {
-// "Init Attribute 2": {
-// op: "equals",
-// value: "four",
-// },
-// },
-// };
-// const whereClause = buildWhereClause(mockSurvey, criteria);
-// let expectedWhereClause: Prisma.ResponseWhereInput | undefined = {};
-
-// // @ts-expect-error
-// prisma.response.findMany.mockImplementation(async (args) => {
-// expectedWhereClause = args?.where;
-// return getFilteredMockResponses(criteria, false);
-// });
-// prisma.survey.findUnique.mockResolvedValue(mockSurveyOutput);
-// const response = await getResponses(mockSurveyId, 1, undefined, criteria);
-
-// expect(expectedWhereClause).toEqual({ surveyId: mockSurveyId, ...whereClause });
-// expect(response).toEqual(getFilteredMockResponses(criteria));
-// });
-// });
-
-// describe("Sad Path", () => {
-// it("Throws an error when the where clause is different and the data is matched when filters are different.", async () => {
-// const whereClause = buildWhereClause(mockSurvey, { finished: true });
-// let expectedWhereClause: Prisma.ResponseWhereInput | undefined = {};
-
-// // @ts-expect-error
-// prisma.response.findMany.mockImplementation(async (args) => {
-// expectedWhereClause = args?.where;
-
-// return getFilteredMockResponses({ finished: true });
-// });
-// prisma.survey.findUnique.mockResolvedValue(mockSurveyOutput);
-// const response = await getResponses(mockSurveyId, 1, undefined, { finished: true });
-
-// expect(expectedWhereClause).not.toEqual(whereClause);
-// expect(response).not.toEqual(getFilteredMockResponses({ finished: false }));
-// });
-// });
-// });
-
-// describe("Sad Path", () => {
-// testInputValidation(getResponses, mockSurveyId, "1");
-
-// it("Throws DatabaseError on PrismaClientKnownRequestError", async () => {
-// const mockErrorMessage = "Mock error message";
-// const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
-// code: "P2002",
-// clientVersion: "0.0.1",
-// });
-
-// prisma.response.findMany.mockRejectedValue(errToThrow);
-// prisma.survey.findUnique.mockResolvedValue(mockSurveyOutput);
-
-// await expect(getResponses(mockSurveyId)).rejects.toThrow(DatabaseError);
-// });
-
-// it("Throws a generic Error for unexpected problems", async () => {
-// const mockErrorMessage = "Mock error message";
-// prisma.response.findMany.mockRejectedValue(new Error(mockErrorMessage));
-// prisma.survey.findUnique.mockResolvedValue(mockSurveyOutput);
-
-// await expect(getResponses(mockSurveyId)).rejects.toThrow(Error);
-// });
-// });
-// });
-
describe("Tests for getSurveySummary service", () => {
describe("Happy Path", () => {
it("Returns a summary of the survey responses", async () => {
@@ -370,7 +179,7 @@ describe("Tests for getSurveySummary service", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
@@ -432,7 +241,7 @@ describe("Tests for getResponseDownloadUrl service", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError, when the getResponseCountBySurveyId fails", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
prisma.survey.findUnique.mockResolvedValue(mockSurveyOutput);
@@ -444,7 +253,7 @@ describe("Tests for getResponseDownloadUrl service", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError, when the getResponses fails", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
@@ -480,7 +289,7 @@ describe("Tests for getResponsesByEnvironmentId", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
@@ -531,7 +340,7 @@ describe("Tests for updateResponse Service", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
@@ -565,7 +374,7 @@ describe("Tests for deleteResponse service", () => {
it("Throws DatabaseError on PrismaClientKnownRequestError", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
diff --git a/packages/lib/survey/tests/__mock__/survey.mock.ts b/packages/lib/survey/tests/__mock__/survey.mock.ts
index cca45f9d37..7c8d4bdea1 100644
--- a/packages/lib/survey/tests/__mock__/survey.mock.ts
+++ b/packages/lib/survey/tests/__mock__/survey.mock.ts
@@ -1,6 +1,5 @@
import { Prisma } from "@prisma/client";
import { TActionClass } from "@formbricks/types/action-classes";
-import { TAttributeClass } from "@formbricks/types/attribute-classes";
import { TContactAttributeKey } from "@formbricks/types/contact-attribute-key";
import { TEnvironment } from "@formbricks/types/environment";
import { TOrganization } from "@formbricks/types/organizations";
@@ -14,9 +13,26 @@ import {
TSurveyWelcomeCard,
} from "@formbricks/types/surveys/types";
import { TUser } from "@formbricks/types/user";
-import { selectContact } from "../../../person/service";
import { selectSurvey } from "../../service";
+const selectContact = {
+ id: true,
+ createdAt: true,
+ updatedAt: true,
+ environmentId: true,
+ attributes: {
+ select: {
+ value: true,
+ attributeKey: {
+ select: {
+ key: true,
+ name: true,
+ },
+ },
+ },
+ },
+};
+
const currentDate = new Date();
const fourDaysAgo = new Date();
fourDaysAgo.setDate(currentDate.getDate() - 4);
@@ -42,6 +58,7 @@ export const mockSurveyLanguages: TSurveyLanguage[] = [
alias: null,
createdAt: new Date(),
updatedAt: new Date(),
+ projectId: mockId,
},
},
{
@@ -53,6 +70,7 @@ export const mockSurveyLanguages: TSurveyLanguage[] = [
alias: null,
createdAt: new Date(),
updatedAt: new Date(),
+ projectId: mockId,
},
},
];
@@ -63,10 +81,7 @@ export const mockProject: TProject = {
updatedAt: currentDate,
name: "mock Project",
organizationId: mockId,
- brandColor: "#000000",
- highlightBorderColor: "#000000",
recontactDays: 0,
- displayLimit: 0,
linkSurveyBranding: false,
inAppSurveyBranding: false,
placement: "bottomRight",
@@ -74,6 +89,10 @@ export const mockProject: TProject = {
darkOverlay: false,
environments: [],
languages: [],
+ config: {
+ channel: "link",
+ industry: "saas",
+ },
styling: {
allowStyleOverwrite: false,
},
@@ -115,9 +134,10 @@ export const mockUser: TUser = {
unsubscribedOrganizationIds: [],
},
role: "other",
+ locale: "en-US",
};
-export const mockPrismaPerson: Prisma.PersonGetPayload<{
+export const mockPrismaPerson: Prisma.ContactGetPayload<{
include: typeof selectContact;
}> = {
id: mockId,
@@ -125,8 +145,8 @@ export const mockPrismaPerson: Prisma.PersonGetPayload<{
attributes: [
{
value: "de",
- attributeClass: {
- id: mockId,
+ attributeKey: {
+ key: "language",
name: "language",
},
},
@@ -189,7 +209,7 @@ const baseSurveyProperties = {
endings: [
{
id: "umyknohldc7w26ocjdhaa62c",
- type: "endScreen",
+ type: "endScreen" as const,
headline: { default: "Thank You!", de: "Danke!" },
},
],
@@ -241,6 +261,11 @@ export const mockSyncSurveyOutput: SurveyMock = {
inlineTriggers: null,
languages: mockSurveyLanguages,
...baseSurveyProperties,
+ followUps: [],
+ variables: [],
+ showLanguageSwitch: null,
+ thankYouCard: null,
+ verifyEmail: null,
};
export const mockSurveyOutput: SurveyMock = {
@@ -260,6 +285,10 @@ export const mockSurveyOutput: SurveyMock = {
inlineTriggers: null,
languages: mockSurveyLanguages,
followUps: [],
+ variables: [],
+ showLanguageSwitch: null,
+ thankYouCard: null,
+ verifyEmail: null,
...baseSurveyProperties,
};
diff --git a/packages/lib/survey/tests/survey.test.ts b/packages/lib/survey/tests/survey.test.ts
index 95555ca362..277a7de4ce 100644
--- a/packages/lib/survey/tests/survey.test.ts
+++ b/packages/lib/survey/tests/survey.test.ts
@@ -3,6 +3,7 @@ import { Prisma } from "@prisma/client";
import { evaluateLogic } from "surveyLogic/utils";
import { beforeEach, describe, expect, it } from "vitest";
import { testInputValidation } from "vitestSetup";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { getSurvey, getSurveyCount, getSurveys, getSurveysByActionClassId, updateSurvey } from "../service";
import {
@@ -183,7 +184,7 @@ describe("Tests for getSurvey", () => {
it("should throw a DatabaseError error if there is a PrismaClientKnownRequestError", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
prisma.survey.findUnique.mockRejectedValue(errToThrow);
@@ -246,7 +247,7 @@ describe("Tests for getSurveys", () => {
it("should throw a DatabaseError error if there is a PrismaClientKnownRequestError", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
@@ -289,7 +290,7 @@ describe("Tests for updateSurvey", () => {
it("should throw a DatabaseError error if there is a PrismaClientKnownRequestError", async () => {
const mockErrorMessage = "Mock error message";
const errToThrow = new Prisma.PrismaClientKnownRequestError(mockErrorMessage, {
- code: "P2002",
+ code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
prisma.survey.findUnique.mockResolvedValueOnce(mockSurveyOutput);
diff --git a/packages/lib/user/service.ts b/packages/lib/user/service.ts
index 9e44ea31d7..a801a34f5c 100644
--- a/packages/lib/user/service.ts
+++ b/packages/lib/user/service.ts
@@ -3,6 +3,7 @@ import { Prisma } from "@prisma/client";
import { cache as reactCache } from "react";
import { z } from "zod";
import { prisma } from "@formbricks/database";
+import { PrismaErrorType } from "@formbricks/database/types/error";
import { ZId } from "@formbricks/types/common";
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
import { TUser, TUserLocale, TUserUpdateInput, ZUserUpdateInput } from "@formbricks/types/user";
@@ -111,7 +112,10 @@ export const updateUser = async (personId: string, data: TUserUpdateInput): Prom
return updatedUser;
} catch (error) {
- if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2016") {
+ if (
+ error instanceof Prisma.PrismaClientKnownRequestError &&
+ error.code === PrismaErrorType.RecordDoesNotExist
+ ) {
throw new ResourceNotFoundError("User", personId);
}
throw error; // Re-throw any other errors
diff --git a/packages/lib/utils/singleUseSurveys.ts b/packages/lib/utils/singleUseSurveys.ts
index 378f88db72..fe3c0cfe6c 100644
--- a/packages/lib/utils/singleUseSurveys.ts
+++ b/packages/lib/utils/singleUseSurveys.ts
@@ -9,6 +9,10 @@ export const generateSurveySingleUseId = (isEncrypted: boolean): string => {
return cuid;
}
+ if (!env.ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
+
const encryptedCuid = symmetricEncrypt(cuid, env.ENCRYPTION_KEY);
return encryptedCuid;
};
@@ -28,6 +32,10 @@ export const validateSurveySingleUseId = (surveySingleUseId: string): string | u
try {
let decryptedCuid: string | null = null;
+ if (!env.ENCRYPTION_KEY) {
+ throw new Error("ENCRYPTION_KEY is not set");
+ }
+
if (surveySingleUseId.length === 64) {
if (!env.FORMBRICKS_ENCRYPTION_KEY) {
throw new Error("FORMBRICKS_ENCRYPTION_KEY is not defined");
diff --git a/packages/logger/package.json b/packages/logger/package.json
index b11bf305fb..18ebaaa0b6 100644
--- a/packages/logger/package.json
+++ b/packages/logger/package.json
@@ -40,7 +40,7 @@
"pino-pretty": "^10.0.0"
},
"devDependencies": {
- "vite": "^6.2.0",
+ "vite": "^6.0.12",
"@formbricks/config-typescript": "workspace:*",
"vitest": "3.0.7",
"@formbricks/eslint-config": "workspace:*",
diff --git a/packages/react-native/package.json b/packages/react-native/package.json
index 95468ba426..9775359acf 100644
--- a/packages/react-native/package.json
+++ b/packages/react-native/package.json
@@ -1,6 +1,6 @@
{
"name": "@formbricks/react-native",
- "version": "2.1.0",
+ "version": "2.1.1",
"license": "MIT",
"description": "Formbricks React Native SDK allows you to connect your app to Formbricks, display surveys and trigger events.",
"homepage": "https://formbricks.com",
@@ -41,24 +41,25 @@
"coverage": "vitest run --coverage"
},
"dependencies": {
+ "@react-native-community/netinfo": "11.4.1",
"zod": "3.24.1"
},
"devDependencies": {
"@formbricks/api": "workspace:*",
"@formbricks/config-typescript": "workspace:*",
+ "@types/react": "18.3.1",
"@vitest/coverage-v8": "3.0.4",
+ "react": "18.3.1",
"react-native": "0.74.5",
"terser": "5.37.0",
- "vite": "6.0.9",
+ "vite": "6.0.12",
"vite-plugin-dts": "4.3.0",
- "vitest": "3.0.5",
- "react": "18.3.1",
- "@types/react": "18.3.1"
+ "vitest": "3.0.5"
},
"peerDependencies": {
+ "@react-native-async-storage/async-storage": ">=2.1.0",
"react": ">=16.8.0",
"react-native": ">=0.60.0",
- "react-native-webview": ">=13.0.0",
- "@react-native-async-storage/async-storage": ">=2.1.0"
+ "react-native-webview": ">=13.0.0"
}
}
diff --git a/packages/react-native/src/lib/survey/action.ts b/packages/react-native/src/lib/survey/action.ts
index e175e88e23..cb5a4bf325 100644
--- a/packages/react-native/src/lib/survey/action.ts
+++ b/packages/react-native/src/lib/survey/action.ts
@@ -1,3 +1,4 @@
+import { fetch } from "@react-native-community/netinfo";
import { RNConfig } from "@/lib/common/config";
import { Logger } from "@/lib/common/logger";
import { shouldDisplayBasedOnPercentage } from "@/lib/common/utils";
@@ -62,24 +63,52 @@ export const trackAction = (name: string, alias?: string): Result | Result => {
- const appConfig = RNConfig.getInstance();
+export const track = async (
+ code: string
+): Promise<
+ | Result
+ | Result
+ | Result
+> => {
+ try {
+ const appConfig = RNConfig.getInstance();
- const {
- environment: {
- data: { actionClasses = [] },
- },
- } = appConfig.get();
+ const netInfo = await fetch();
- const codeActionClasses = actionClasses.filter((action) => action.type === "code");
- const actionClass = codeActionClasses.find((action) => action.key === code);
+ if (!netInfo.isConnected) {
+ return err({
+ code: "network_error",
+ status: 500,
+ message: "No internet connection. Please check your connection and try again.",
+ responseMessage: "No internet connection. Please check your connection and try again.",
+ url: new URL(`${appConfig.get().appUrl}/js/surveys.umd.cjs`),
+ });
+ }
+
+ const {
+ environment: {
+ data: { actionClasses = [] },
+ },
+ } = appConfig.get();
+
+ const codeActionClasses = actionClasses.filter((action) => action.type === "code");
+ const actionClass = codeActionClasses.find((action) => action.key === code);
+
+ if (!actionClass) {
+ return err({
+ code: "invalid_code",
+ message: `${code} action unknown. Please add this action in Formbricks first in order to use it in your code.`,
+ });
+ }
+
+ return trackAction(actionClass.name, code);
+ } catch (error) {
+ const logger = Logger.getInstance();
+ logger.error(`Error tracking action ${error as string}`);
- if (!actionClass) {
return err({
- code: "invalid_code",
- message: `${code} action unknown. Please add this action in Formbricks first in order to use it in your code.`,
+ code: "error",
+ message: "Error tracking action",
});
}
-
- return trackAction(actionClass.name, code);
};
diff --git a/packages/react-native/src/lib/survey/tests/action.test.ts b/packages/react-native/src/lib/survey/tests/action.test.ts
index 76e93a36d8..9419fc6c37 100644
--- a/packages/react-native/src/lib/survey/tests/action.test.ts
+++ b/packages/react-native/src/lib/survey/tests/action.test.ts
@@ -36,6 +36,12 @@ vi.mock("@/lib/common/utils", () => ({
shouldDisplayBasedOnPercentage: vi.fn(),
}));
+vi.mock("@react-native-community/netinfo", () => ({
+ fetch: vi.fn(() => ({
+ isConnected: true,
+ })),
+}));
+
describe("survey/action.ts", () => {
const mockSurvey = {
id: "survey_001",
@@ -153,15 +159,14 @@ describe("survey/action.ts", () => {
});
});
- test("tracks a valid action by code", () => {
- const result = track("testCode");
+ test("tracks a valid action by code", async () => {
+ const result = await 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");
+ test("returns error for invalid action code", async () => {
+ const result = await track("invalidCode");
expect(result.ok).toBe(false);
diff --git a/packages/surveys/package.json b/packages/surveys/package.json
index 7e509aa4c7..53cbae38b2 100644
--- a/packages/surveys/package.json
+++ b/packages/surveys/package.json
@@ -52,7 +52,7 @@
"serve": "14.2.4",
"tailwindcss": "3.4.16",
"terser": "5.37.0",
- "vite": "6.0.9",
+ "vite": "6.0.12",
"vite-plugin-dts": "4.3.0",
"vite-tsconfig-paths": "5.1.4"
},
diff --git a/packages/vite-plugins/package.json b/packages/vite-plugins/package.json
index a92372ce22..78387bc4c7 100644
--- a/packages/vite-plugins/package.json
+++ b/packages/vite-plugins/package.json
@@ -17,6 +17,6 @@
"devDependencies": {
"@formbricks/config-typescript": "workspace:*",
"@formbricks/eslint-config": "workspace:*",
- "vite": "6.0.9"
+ "vite": "6.0.12"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 12f45034fc..b22e2cda35 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -146,7 +146,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.8.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.36.0)(storybook@8.4.7(prettier@3.4.2))(typescript@5.8.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.37.0)(storybook@8.4.7(prettier@3.4.2))(typescript@5.8.2)(vite@6.0.12(@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))
@@ -158,7 +158,7 @@ importers:
version: 8.18.0(eslint@8.57.0)(typescript@5.8.2)
'@vitejs/plugin-react':
specifier: 4.3.4
- version: 4.3.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))
+ version: 4.3.4(vite@6.0.12(@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))
esbuild:
specifier: 0.25.1
version: 0.25.1
@@ -173,10 +173,10 @@ importers:
version: 8.4.7(prettier@3.4.2)
tsup:
specifier: 8.3.5
- version: 8.3.5(@microsoft/api-extractor@7.52.1(@types/node@22.10.2))(jiti@2.4.1)(postcss@8.5.3)(tsx@4.19.2)(typescript@5.8.2)(yaml@2.7.0)
+ version: 8.3.5(@microsoft/api-extractor@7.52.2(@types/node@22.10.2))(jiti@2.4.1)(postcss@8.5.3)(tsx@4.19.2)(typescript@5.8.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)
+ specifier: 6.0.12
+ version: 6.0.12(@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)
apps/web:
dependencies:
@@ -590,11 +590,11 @@ importers:
specifier: 2.1.8
version: 2.1.8(vitest@3.0.7(@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))
vite:
- specifier: 6.2.0
- version: 6.2.0(@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)
+ specifier: 6.2.3
+ version: 6.2.3(@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.8.2)(vite@6.2.0(@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: 5.1.4(typescript@5.8.2)(vite@6.2.3(@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.7
version: 3.0.7(@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)
@@ -615,7 +615,7 @@ importers:
version: link:../types
'@rollup/plugin-inject':
specifier: 5.0.5
- version: 5.0.5(rollup@4.36.0)
+ version: 5.0.5(rollup@4.37.0)
buffer:
specifier: 6.0.3
version: 6.0.3
@@ -623,14 +623,14 @@ importers:
specifier: 5.37.0
version: 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)
+ specifier: 6.0.12
+ version: 6.0.12(@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.36.0)(typescript@5.8.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.37.0)(typescript@5.8.2)(vite@6.0.12(@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.36.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.37.0)(vite@6.0.12(@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:
@@ -756,11 +756,11 @@ importers:
specifier: 5.37.0
version: 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)
+ specifier: 6.0.12
+ version: 6.0.12(@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.36.0)(typescript@5.8.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.37.0)(typescript@5.8.2)(vite@6.0.12(@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:
@@ -780,11 +780,11 @@ importers:
specifier: 5.37.0
version: 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)
+ specifier: 6.0.12
+ version: 6.0.12(@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.36.0)(typescript@5.8.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.37.0)(typescript@5.8.2)(vite@6.0.12(@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.6
version: 3.0.6(@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)
@@ -908,11 +908,11 @@ importers:
specifier: workspace:*
version: link:../config-eslint
vite:
- specifier: ^6.2.0
- version: 6.2.0(@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)
+ specifier: ^6.0.12
+ version: 6.2.3(@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.36.0)(typescript@5.8.2)(vite@6.2.0(@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.37.0)(typescript@5.8.2)(vite@6.2.3(@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.7
version: 3.0.7(@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)
@@ -922,6 +922,9 @@ importers:
'@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.9(@babel/core@7.26.0))(@types/react@18.3.1)(encoding@0.1.13)(react@18.3.1))
+ '@react-native-community/netinfo':
+ specifier: 11.4.1
+ version: 11.4.1(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@babel/core@7.26.0))(@types/react@18.3.1)(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.9(@babel/core@7.26.0))(@types/react@18.3.1)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)
@@ -951,11 +954,11 @@ importers:
specifier: 5.37.0
version: 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)
+ specifier: 6.0.12
+ version: 6.0.12(@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.36.0)(typescript@5.8.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.37.0)(typescript@5.8.2)(vite@6.0.12(@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.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)
@@ -986,7 +989,7 @@ importers:
version: link:../types
'@preact/preset-vite':
specifier: 2.9.3
- version: 2.9.3(@babel/core@7.26.0)(preact@10.25.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: 2.9.3(@babel/core@7.26.0)(preact@10.25.2)(vite@6.0.12(@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))
'@types/react':
specifier: 19.0.1
version: 19.0.1
@@ -1018,14 +1021,14 @@ importers:
specifier: 5.37.0
version: 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)
+ specifier: 6.0.12
+ version: 6.0.12(@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.36.0)(typescript@5.8.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.37.0)(typescript@5.8.2)(vite@6.0.12(@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.8.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: 5.1.4(typescript@5.8.2)(vite@6.0.12(@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/types:
dependencies:
@@ -1052,8 +1055,8 @@ importers:
specifier: workspace:*
version: link:../config-eslint
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)
+ specifier: 6.0.12
+ version: 6.0.12(@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:
@@ -1387,20 +1390,20 @@ packages:
peerDependencies:
'@playwright/test': ^1.43.1
- '@azure/msal-browser@4.7.0':
- resolution: {integrity: sha512-H4AIPhIQVe1qW4+BJaitqod6UGQiXE3juj7q2ZBsOPjuZicQaqcbnBp2gCroF/icS0+TJ9rGuyCBJbjlAqVOGA==}
+ '@azure/msal-browser@4.9.0':
+ resolution: {integrity: sha512-7ozXQXts71Yjjy4AmCZK0/lkUhRt0pglcdXkl9T8FtMt7uLrzsaHhkbHTw3L5GfxsgkUgNh1hGIGchKrHBb9/Q==}
engines: {node: '>=0.8.0'}
- '@azure/msal-common@15.2.1':
- resolution: {integrity: sha512-eZHtYE5OHDN0o2NahCENkczQ6ffGc0MoUSAI3hpwGpZBHJXaEQMMZPWtIx86da2L9w7uT+Tr/xgJbGwIkvTZTQ==}
+ '@azure/msal-common@15.4.0':
+ resolution: {integrity: sha512-reeIUDXt6Xc+FpCBDEbUFQWvJ6SjE0JwsGYIfa3ZCR6Tpzjc9J1v+/InQgfCeJzfTRd7PDJVxI6TSzOmOd7+Ag==}
engines: {node: '>=0.8.0'}
- '@azure/msal-node@3.3.0':
- resolution: {integrity: sha512-ulsT3EHF1RQ29X55cxBLgKsIKWni9JdbUqG7sipGVP4uhWcBpmm/vhKOMH340+27Acm9+kHGnN/5XmQ5LrIDgA==}
+ '@azure/msal-node@3.4.1':
+ resolution: {integrity: sha512-VlW6ygnKBIqUKIHnA/ubQ+F3rZ8aW3K6VA1bpZ90Ln0vlE4XaA6yGB/FibPJxet7gWinAG1oSpQqPN/PL9AqIw==}
engines: {node: '>=16'}
- '@azure/storage-blob@12.26.0':
- resolution: {integrity: sha512-SriLPKezypIsiZ+TtlFfE46uuBIap2HeaQVS78e1P7rz5OSbq0rsd52WE1mC5f7vAeLiXqv7I7oRhL3WFZEw3Q==}
+ '@azure/storage-blob@12.27.0':
+ resolution: {integrity: sha512-IQjj9RIzAKatmNca3D6bT0qJ+Pkox1WZGOg2esJF2YLHb45pQKOwGPIAV+w3rfgkj7zV3RMxpn/c6iftzSOZJQ==}
engines: {node: '>=18.0.0'}
'@babel/code-frame@7.10.4':
@@ -1418,42 +1421,37 @@ packages:
resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==}
engines: {node: '>=6.9.0'}
- '@babel/eslint-parser@7.26.10':
- resolution: {integrity: sha512-QsfQZr4AiLpKqn7fz+j7SN+f43z2DZCgGyYbNJ2vJOqKfG4E6MZer1+jqGZqKJaxq/gdO2DC/nUu45+pOL5p2Q==}
+ '@babel/eslint-parser@7.27.0':
+ resolution: {integrity: sha512-dtnzmSjXfgL/HDgMcmsLSzyGbEosi4DrGWoCNfuI+W4IkVJw6izpTe7LtOdwAXnkDqw5yweboYCTkM2rQizCng==}
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.10':
- resolution: {integrity: sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==}
+ '@babel/generator@7.27.0':
+ resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==}
engines: {node: '>=6.9.0'}
'@babel/helper-annotate-as-pure@7.25.9':
resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==}
engines: {node: '>=6.9.0'}
- '@babel/helper-compilation-targets@7.26.5':
- resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==}
+ '@babel/helper-compilation-targets@7.27.0':
+ resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==}
engines: {node: '>=6.9.0'}
- '@babel/helper-create-class-features-plugin@7.26.9':
- resolution: {integrity: sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==}
+ '@babel/helper-create-class-features-plugin@7.27.0':
+ resolution: {integrity: sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/helper-create-regexp-features-plugin@7.26.3':
- resolution: {integrity: sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==}
+ '@babel/helper-create-regexp-features-plugin@7.27.0':
+ resolution: {integrity: sha512-fO8l08T76v48BhpNRW/nQ0MxfnSdoSKUJBMjubOAYffsVuGG5qOfMq7N6Es7UJvi7Y8goXXo07EfcHZXDPuELQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
- '@babel/helper-define-polyfill-provider@0.6.3':
- resolution: {integrity: sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==}
- peerDependencies:
- '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
'@babel/helper-define-polyfill-provider@0.6.4':
resolution: {integrity: sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==}
peerDependencies:
@@ -1517,16 +1515,16 @@ packages:
resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==}
engines: {node: '>=6.9.0'}
- '@babel/helpers@7.26.10':
- resolution: {integrity: sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==}
+ '@babel/helpers@7.27.0':
+ resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==}
engines: {node: '>=6.9.0'}
'@babel/highlight@7.25.9':
resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==}
engines: {node: '>=6.9.0'}
- '@babel/parser@7.26.10':
- resolution: {integrity: sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==}
+ '@babel/parser@7.27.0':
+ resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -1784,8 +1782,8 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-block-scoping@7.25.9':
- resolution: {integrity: sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==}
+ '@babel/plugin-transform-block-scoping@7.27.0':
+ resolution: {integrity: sha512-u1jGphZ8uDI2Pj/HJj6YQ6XQLZCNjOlprjxB5SVz6rq2T6SwAR+CdrWK0CP7F+9rDVMXdB0+r6Am5G5aobOjAQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
@@ -2030,8 +2028,8 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-regenerator@7.25.9':
- resolution: {integrity: sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==}
+ '@babel/plugin-transform-regenerator@7.27.0':
+ resolution: {integrity: sha512-LX/vCajUJQDqE7Aum/ELUMZAY19+cDpghxrnyt5I1tV6X5PyC86AOoWXWFYFeIvauyeSA6/ktn4tQVn/3ZifsA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
@@ -2078,14 +2076,14 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-typeof-symbol@7.26.7':
- resolution: {integrity: sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==}
+ '@babel/plugin-transform-typeof-symbol@7.27.0':
+ resolution: {integrity: sha512-+LLkxA9rKJpNoGsbLnAgOCdESl73vwYn+V6b+5wHbrE7OGKVDPHIQvbFSzqE6rwqaCw2RE+zdJrlLkcf8YOA0w==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/plugin-transform-typescript@7.26.8':
- resolution: {integrity: sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw==}
+ '@babel/plugin-transform-typescript@7.27.0':
+ resolution: {integrity: sha512-fRGGjO2UEGPjvEcyAZXRXAS8AfdaQoq7HnxAbJoAoW10B9xOKesmmndJv+Sym2a+9FHWZ9KbyyLCe9s0Sn5jtg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
@@ -2137,8 +2135,8 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/preset-typescript@7.26.0':
- resolution: {integrity: sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==}
+ '@babel/preset-typescript@7.27.0':
+ resolution: {integrity: sha512-vxaPFfJtHhgeOVXRKuHpHPAOgymmy8V8I65T1q53R7GCZlefKeCaTyDs3zOPHTTbmquvNlQYC5klEvWsBAtrBQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
@@ -2149,20 +2147,20 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
- '@babel/runtime@7.26.10':
- resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==}
+ '@babel/runtime@7.27.0':
+ resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
engines: {node: '>=6.9.0'}
- '@babel/template@7.26.9':
- resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==}
+ '@babel/template@7.27.0':
+ resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==}
engines: {node: '>=6.9.0'}
- '@babel/traverse@7.26.10':
- resolution: {integrity: sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==}
+ '@babel/traverse@7.27.0':
+ resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==}
engines: {node: '>=6.9.0'}
- '@babel/types@7.26.10':
- resolution: {integrity: sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==}
+ '@babel/types@7.27.0':
+ resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
engines: {node: '>=6.9.0'}
'@bcoe/v8-coverage@0.2.3':
@@ -3009,20 +3007,20 @@ packages:
'@floating-ui/utils@0.2.9':
resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
- '@formatjs/ecma402-abstract@2.3.3':
- resolution: {integrity: sha512-pJT1OkhplSmvvr6i3CWTPvC/FGC06MbN5TNBfRO6Ox62AEz90eMq+dVvtX9Bl3jxCEkS0tATzDarRZuOLw7oFg==}
+ '@formatjs/ecma402-abstract@2.3.4':
+ resolution: {integrity: sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==}
- '@formatjs/fast-memoize@2.2.6':
- resolution: {integrity: sha512-luIXeE2LJbQnnzotY1f2U2m7xuQNj2DA8Vq4ce1BY9ebRZaoPB1+8eZ6nXpLzsxuW5spQxr7LdCg+CApZwkqkw==}
+ '@formatjs/fast-memoize@2.2.7':
+ resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==}
- '@formatjs/icu-messageformat-parser@2.11.1':
- resolution: {integrity: sha512-o0AhSNaOfKoic0Sn1GkFCK4MxdRsw7mPJ5/rBpIqdvcC7MIuyUSW8WChUEvrK78HhNpYOgqCQbINxCTumJLzZA==}
+ '@formatjs/icu-messageformat-parser@2.11.2':
+ resolution: {integrity: sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==}
- '@formatjs/icu-skeleton-parser@1.8.13':
- resolution: {integrity: sha512-N/LIdTvVc1TpJmMt2jVg0Fr1F7Q1qJPdZSCs19unMskCmVQ/sa0H9L8PWt13vq+gLdLg1+pPsvBLydL1Apahjg==}
+ '@formatjs/icu-skeleton-parser@1.8.14':
+ resolution: {integrity: sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==}
- '@formatjs/intl-localematcher@0.6.0':
- resolution: {integrity: sha512-4rB4g+3hESy1bHSBG3tDFaMY2CH67iT7yne1e+0CLTsGLDcmoEWWpJjjpWVaYgYfYuohIRuo0E+N536gd2ZHZA==}
+ '@formatjs/intl-localematcher@0.6.1':
+ resolution: {integrity: sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==}
'@formkit/auto-animate@0.8.2':
resolution: {integrity: sha512-SwPWfeRa5veb1hOIBMdzI+73te5puUBHmqqaF1Bu7FjvxlYSz/kJcZKSa9Cg60zL0uRNeJL2SbRxV6Jp6Q1nFQ==}
@@ -3365,11 +3363,11 @@ packages:
'@types/react': '>=16'
react: '>=16'
- '@microsoft/api-extractor-model@7.30.4':
- resolution: {integrity: sha512-RobC0gyVYsd2Fao9MTKOfTdBm41P/bCMUmzS5mQ7/MoAKEqy0FOBph3JOYdq4X4BsEnMEiSHc+0NUNmdzxCpjA==}
+ '@microsoft/api-extractor-model@7.30.5':
+ resolution: {integrity: sha512-0ic4rcbcDZHz833RaTZWTGu+NpNgrxVNjVaor0ZDUymfDFzjA/Uuk8hYziIUIOEOSTfmIQqyzVwlzxZxPe7tOA==}
- '@microsoft/api-extractor@7.52.1':
- resolution: {integrity: sha512-m3I5uAwE05orsu3D1AGyisX5KxsgVXB+U4bWOOaX/Z7Ftp/2Cy41qsNhO6LPvSxHBaapyser5dVorF1t5M6tig==}
+ '@microsoft/api-extractor@7.52.2':
+ resolution: {integrity: sha512-RX37V5uhBBPUvrrcmIxuQ8TPsohvr6zxo7SsLPOzBYcH9nbjbvtdXrts4cxHCXGOin9JR5ar37qfxtCOuEBTHA==}
hasBin: true
'@microsoft/tsdoc-config@0.16.2':
@@ -3846,8 +3844,8 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
- '@pkgr/core@0.1.1':
- resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
+ '@pkgr/core@0.1.2':
+ resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
'@playwright/test@1.49.1':
@@ -4635,6 +4633,11 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ '@react-native-community/netinfo@11.4.1':
+ resolution: {integrity: sha512-B0BYAkghz3Q2V09BF88RA601XursIEA111tnc2JOaN7axJWmNefmfjZqw/KdSxKZp7CZUuPpjBmz/WCR9uaHYg==}
+ peerDependencies:
+ react-native: '>=0.59'
+
'@react-native/assets-registry@0.74.87':
resolution: {integrity: sha512-1XmRhqQchN+pXPKEKYdpJlwESxVomJOxtEnIkbo7GAlaN2sym84fHEGDXAjLilih5GVPpcpSmFzTy8jx3LtaFg==}
engines: {node: '>=18'}
@@ -4847,98 +4850,103 @@ packages:
rollup:
optional: true
- '@rollup/rollup-android-arm-eabi@4.36.0':
- resolution: {integrity: sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==}
+ '@rollup/rollup-android-arm-eabi@4.37.0':
+ resolution: {integrity: sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ==}
cpu: [arm]
os: [android]
- '@rollup/rollup-android-arm64@4.36.0':
- resolution: {integrity: sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==}
+ '@rollup/rollup-android-arm64@4.37.0':
+ resolution: {integrity: sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA==}
cpu: [arm64]
os: [android]
- '@rollup/rollup-darwin-arm64@4.36.0':
- resolution: {integrity: sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==}
+ '@rollup/rollup-darwin-arm64@4.37.0':
+ resolution: {integrity: sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA==}
cpu: [arm64]
os: [darwin]
- '@rollup/rollup-darwin-x64@4.36.0':
- resolution: {integrity: sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==}
+ '@rollup/rollup-darwin-x64@4.37.0':
+ resolution: {integrity: sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ==}
cpu: [x64]
os: [darwin]
- '@rollup/rollup-freebsd-arm64@4.36.0':
- resolution: {integrity: sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==}
+ '@rollup/rollup-freebsd-arm64@4.37.0':
+ resolution: {integrity: sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA==}
cpu: [arm64]
os: [freebsd]
- '@rollup/rollup-freebsd-x64@4.36.0':
- resolution: {integrity: sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==}
+ '@rollup/rollup-freebsd-x64@4.37.0':
+ resolution: {integrity: sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA==}
cpu: [x64]
os: [freebsd]
- '@rollup/rollup-linux-arm-gnueabihf@4.36.0':
- resolution: {integrity: sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==}
+ '@rollup/rollup-linux-arm-gnueabihf@4.37.0':
+ resolution: {integrity: sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w==}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm-musleabihf@4.36.0':
- resolution: {integrity: sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==}
+ '@rollup/rollup-linux-arm-musleabihf@4.37.0':
+ resolution: {integrity: sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag==}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm64-gnu@4.36.0':
- resolution: {integrity: sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==}
+ '@rollup/rollup-linux-arm64-gnu@4.37.0':
+ resolution: {integrity: sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-arm64-musl@4.36.0':
- resolution: {integrity: sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==}
+ '@rollup/rollup-linux-arm64-musl@4.37.0':
+ resolution: {integrity: sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ==}
cpu: [arm64]
os: [linux]
- '@rollup/rollup-linux-loongarch64-gnu@4.36.0':
- resolution: {integrity: sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==}
+ '@rollup/rollup-linux-loongarch64-gnu@4.37.0':
+ resolution: {integrity: sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA==}
cpu: [loong64]
os: [linux]
- '@rollup/rollup-linux-powerpc64le-gnu@4.36.0':
- resolution: {integrity: sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==}
+ '@rollup/rollup-linux-powerpc64le-gnu@4.37.0':
+ resolution: {integrity: sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ==}
cpu: [ppc64]
os: [linux]
- '@rollup/rollup-linux-riscv64-gnu@4.36.0':
- resolution: {integrity: sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==}
+ '@rollup/rollup-linux-riscv64-gnu@4.37.0':
+ resolution: {integrity: sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw==}
cpu: [riscv64]
os: [linux]
- '@rollup/rollup-linux-s390x-gnu@4.36.0':
- resolution: {integrity: sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==}
+ '@rollup/rollup-linux-riscv64-musl@4.37.0':
+ resolution: {integrity: sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.37.0':
+ resolution: {integrity: sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A==}
cpu: [s390x]
os: [linux]
- '@rollup/rollup-linux-x64-gnu@4.36.0':
- resolution: {integrity: sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==}
+ '@rollup/rollup-linux-x64-gnu@4.37.0':
+ resolution: {integrity: sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ==}
cpu: [x64]
os: [linux]
- '@rollup/rollup-linux-x64-musl@4.36.0':
- resolution: {integrity: sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==}
+ '@rollup/rollup-linux-x64-musl@4.37.0':
+ resolution: {integrity: sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w==}
cpu: [x64]
os: [linux]
- '@rollup/rollup-win32-arm64-msvc@4.36.0':
- resolution: {integrity: sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==}
+ '@rollup/rollup-win32-arm64-msvc@4.37.0':
+ resolution: {integrity: sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg==}
cpu: [arm64]
os: [win32]
- '@rollup/rollup-win32-ia32-msvc@4.36.0':
- resolution: {integrity: sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==}
+ '@rollup/rollup-win32-ia32-msvc@4.37.0':
+ resolution: {integrity: sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA==}
cpu: [ia32]
os: [win32]
- '@rollup/rollup-win32-x64-msvc@4.36.0':
- resolution: {integrity: sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==}
+ '@rollup/rollup-win32-x64-msvc@4.37.0':
+ resolution: {integrity: sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA==}
cpu: [x64]
os: [win32]
@@ -4948,8 +4956,8 @@ packages:
'@rushstack/eslint-patch@1.11.0':
resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==}
- '@rushstack/node-core-library@5.12.0':
- resolution: {integrity: sha512-QSwwzgzWoil1SCQse+yCHwlhRxNv2dX9siPnAb9zR/UmMhac4mjMrlMZpk64BlCeOFi1kJKgXRkihSwRMbboAQ==}
+ '@rushstack/node-core-library@5.13.0':
+ resolution: {integrity: sha512-IGVhy+JgUacAdCGXKUrRhwHMTzqhWwZUI+qEPcdzsb80heOw0QPbhhoVsoiMF7Klp8eYsp7hzpScMXmOa3Uhfg==}
peerDependencies:
'@types/node': '*'
peerDependenciesMeta:
@@ -4959,16 +4967,16 @@ packages:
'@rushstack/rig-package@0.5.3':
resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==}
- '@rushstack/terminal@0.15.1':
- resolution: {integrity: sha512-3vgJYwumcjoDOXU3IxZfd616lqOdmr8Ezj4OWgJZfhmiBK4Nh7eWcv8sU8N/HdzXcuHDXCRGn/6O2Q75QvaZMA==}
+ '@rushstack/terminal@0.15.2':
+ resolution: {integrity: sha512-7Hmc0ysK5077R/IkLS9hYu0QuNafm+TbZbtYVzCMbeOdMjaRboLKrhryjwZSRJGJzu+TV1ON7qZHeqf58XfLpA==}
peerDependencies:
'@types/node': '*'
peerDependenciesMeta:
'@types/node':
optional: true
- '@rushstack/ts-command-line@4.23.6':
- resolution: {integrity: sha512-7WepygaF3YPEoToh4MAL/mmHkiIImQq3/uAkQX46kVoKTNOOlCtFGyNnze6OYuWw2o9rxsyrHVfIBKxq/am2RA==}
+ '@rushstack/ts-command-line@4.23.7':
+ resolution: {integrity: sha512-Gr9cB7DGe6uz5vq2wdr89WbVDKz0UeuFEn5H2CfWDe7JvjFFaiV15gi6mqDBTbHhHCWS7w8mF1h3BnIfUndqdA==}
'@segment/loosely-validate-event@2.0.0':
resolution: {integrity: sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==}
@@ -5113,8 +5121,8 @@ packages:
'@sinonjs/fake-timers@10.3.0':
resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==}
- '@smithy/abort-controller@4.0.1':
- resolution: {integrity: sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==}
+ '@smithy/abort-controller@4.0.2':
+ resolution: {integrity: sha512-Sl/78VDtgqKxN2+1qduaVE140XF+Xg+TafkncspwM4jFP/LHr76ZHmIY/y3V1M0mMLNk+Je6IGbzxy23RSToMw==}
engines: {node: '>=18.0.0'}
'@smithy/chunked-blob-reader-native@4.0.0':
@@ -5125,56 +5133,56 @@ packages:
resolution: {integrity: sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==}
engines: {node: '>=18.0.0'}
- '@smithy/config-resolver@4.0.1':
- resolution: {integrity: sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==}
+ '@smithy/config-resolver@4.1.0':
+ resolution: {integrity: sha512-8smPlwhga22pwl23fM5ew4T9vfLUCeFXlcqNOCD5M5h8VmNPNUE9j6bQSuRXpDSV11L/E/SwEBQuW8hr6+nS1A==}
engines: {node: '>=18.0.0'}
- '@smithy/core@3.1.5':
- resolution: {integrity: sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA==}
+ '@smithy/core@3.2.0':
+ resolution: {integrity: sha512-k17bgQhVZ7YmUvA8at4af1TDpl0NDMBuBKJl8Yg0nrefwmValU+CnA5l/AriVdQNthU/33H3nK71HrLgqOPr1Q==}
engines: {node: '>=18.0.0'}
- '@smithy/credential-provider-imds@4.0.1':
- resolution: {integrity: sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==}
+ '@smithy/credential-provider-imds@4.0.2':
+ resolution: {integrity: sha512-32lVig6jCaWBHnY+OEQ6e6Vnt5vDHaLiydGrwYMW9tPqO688hPGTYRamYJ1EptxEC2rAwJrHWmPoKRBl4iTa8w==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-codec@4.0.1':
- resolution: {integrity: sha512-Q2bCAAR6zXNVtJgifsU16ZjKGqdw/DyecKNgIgi7dlqw04fqDu0mnq+JmGphqheypVc64CYq3azSuCpAdFk2+A==}
+ '@smithy/eventstream-codec@4.0.2':
+ resolution: {integrity: sha512-p+f2kLSK7ZrXVfskU/f5dzksKTewZk8pJLPvER3aFHPt76C2MxD9vNatSfLzzQSQB4FNO96RK4PSXfhD1TTeMQ==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-browser@4.0.1':
- resolution: {integrity: sha512-HbIybmz5rhNg+zxKiyVAnvdM3vkzjE6ccrJ620iPL8IXcJEntd3hnBl+ktMwIy12Te/kyrSbUb8UCdnUT4QEdA==}
+ '@smithy/eventstream-serde-browser@4.0.2':
+ resolution: {integrity: sha512-CepZCDs2xgVUtH7ZZ7oDdZFH8e6Y2zOv8iiX6RhndH69nlojCALSKK+OXwZUgOtUZEUaZ5e1hULVCHYbCn7pug==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-config-resolver@4.0.1':
- resolution: {integrity: sha512-lSipaiq3rmHguHa3QFF4YcCM3VJOrY9oq2sow3qlhFY+nBSTF/nrO82MUQRPrxHQXA58J5G1UnU2WuJfi465BA==}
+ '@smithy/eventstream-serde-config-resolver@4.1.0':
+ resolution: {integrity: sha512-1PI+WPZ5TWXrfj3CIoKyUycYynYJgZjuQo8U+sphneOtjsgrttYybdqESFReQrdWJ+LKt6NEdbYzmmfDBmjX2A==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-node@4.0.1':
- resolution: {integrity: sha512-o4CoOI6oYGYJ4zXo34U8X9szDe3oGjmHgsMGiZM0j4vtNoT+h80TLnkUcrLZR3+E6HIxqW+G+9WHAVfl0GXK0Q==}
+ '@smithy/eventstream-serde-node@4.0.2':
+ resolution: {integrity: sha512-C5bJ/C6x9ENPMx2cFOirspnF9ZsBVnBMtP6BdPl/qYSuUawdGQ34Lq0dMcf42QTjUZgWGbUIZnz6+zLxJlb9aw==}
engines: {node: '>=18.0.0'}
- '@smithy/eventstream-serde-universal@4.0.1':
- resolution: {integrity: sha512-Z94uZp0tGJuxds3iEAZBqGU2QiaBHP4YytLUjwZWx+oUeohCsLyUm33yp4MMBmhkuPqSbQCXq5hDet6JGUgHWA==}
+ '@smithy/eventstream-serde-universal@4.0.2':
+ resolution: {integrity: sha512-St8h9JqzvnbB52FtckiHPN4U/cnXcarMniXRXTKn0r4b4XesZOGiAyUdj1aXbqqn1icSqBlzzUsCl6nPB018ng==}
engines: {node: '>=18.0.0'}
- '@smithy/fetch-http-handler@5.0.1':
- resolution: {integrity: sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==}
+ '@smithy/fetch-http-handler@5.0.2':
+ resolution: {integrity: sha512-+9Dz8sakS9pe7f2cBocpJXdeVjMopUDLgZs1yWeu7h++WqSbjUYv/JAJwKwXw1HV6gq1jyWjxuyn24E2GhoEcQ==}
engines: {node: '>=18.0.0'}
- '@smithy/hash-blob-browser@4.0.1':
- resolution: {integrity: sha512-rkFIrQOKZGS6i1D3gKJ8skJ0RlXqDvb1IyAphksaFOMzkn3v3I1eJ8m7OkLj0jf1McP63rcCEoLlkAn/HjcTRw==}
+ '@smithy/hash-blob-browser@4.0.2':
+ resolution: {integrity: sha512-3g188Z3DyhtzfBRxpZjU8R9PpOQuYsbNnyStc/ZVS+9nVX1f6XeNOa9IrAh35HwwIZg+XWk8bFVtNINVscBP+g==}
engines: {node: '>=18.0.0'}
- '@smithy/hash-node@4.0.1':
- resolution: {integrity: sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==}
+ '@smithy/hash-node@4.0.2':
+ resolution: {integrity: sha512-VnTpYPnRUE7yVhWozFdlxcYknv9UN7CeOqSrMH+V877v4oqtVYuoqhIhtSjmGPvYrYnAkaM61sLMKHvxL138yg==}
engines: {node: '>=18.0.0'}
- '@smithy/hash-stream-node@4.0.1':
- resolution: {integrity: sha512-U1rAE1fxmReCIr6D2o/4ROqAQX+GffZpyMt3d7njtGDr2pUNmAKRWa49gsNVhCh2vVAuf3wXzWwNr2YN8PAXIw==}
+ '@smithy/hash-stream-node@4.0.2':
+ resolution: {integrity: sha512-POWDuTznzbIwlEXEvvXoPMS10y0WKXK790soe57tFRfvf4zBHyzE529HpZMqmDdwG9MfFflnyzndUQ8j78ZdSg==}
engines: {node: '>=18.0.0'}
- '@smithy/invalid-dependency@4.0.1':
- resolution: {integrity: sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==}
+ '@smithy/invalid-dependency@4.0.2':
+ resolution: {integrity: sha512-GatB4+2DTpgWPday+mnUkoumP54u/MDM/5u44KF9hIu8jF0uafZtQLcdfIKkIcUNuF/fBojpLEHZS/56JqPeXQ==}
engines: {node: '>=18.0.0'}
'@smithy/is-array-buffer@2.2.0':
@@ -5185,76 +5193,76 @@ packages:
resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==}
engines: {node: '>=18.0.0'}
- '@smithy/md5-js@4.0.1':
- resolution: {integrity: sha512-HLZ647L27APi6zXkZlzSFZIjpo8po45YiyjMGJZM3gyDY8n7dPGdmxIIljLm4gPt/7rRvutLTTkYJpZVfG5r+A==}
+ '@smithy/md5-js@4.0.2':
+ resolution: {integrity: sha512-Hc0R8EiuVunUewCse2syVgA2AfSRco3LyAv07B/zCOMa+jpXI9ll+Q21Nc6FAlYPcpNcAXqBzMhNs1CD/pP2bA==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-content-length@4.0.1':
- resolution: {integrity: sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==}
+ '@smithy/middleware-content-length@4.0.2':
+ resolution: {integrity: sha512-hAfEXm1zU+ELvucxqQ7I8SszwQ4znWMbNv6PLMndN83JJN41EPuS93AIyh2N+gJ6x8QFhzSO6b7q2e6oClDI8A==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-endpoint@4.0.6':
- resolution: {integrity: sha512-ftpmkTHIFqgaFugcjzLZv3kzPEFsBFSnq1JsIkr2mwFzCraZVhQk2gqN51OOeRxqhbPTkRFj39Qd2V91E/mQxg==}
+ '@smithy/middleware-endpoint@4.1.0':
+ resolution: {integrity: sha512-xhLimgNCbCzsUppRTGXWkZywksuTThxaIB0HwbpsVLY5sceac4e1TZ/WKYqufQLaUy+gUSJGNdwD2jo3cXL0iA==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-retry@4.0.7':
- resolution: {integrity: sha512-58j9XbUPLkqAcV1kHzVX/kAR16GT+j7DUZJqwzsxh1jtz7G82caZiGyyFgUvogVfNTg3TeAOIJepGc8TXF4AVQ==}
+ '@smithy/middleware-retry@4.1.0':
+ resolution: {integrity: sha512-2zAagd1s6hAaI/ap6SXi5T3dDwBOczOMCSkkYzktqN1+tzbk1GAsHNAdo/1uzxz3Ky02jvZQwbi/vmDA6z4Oyg==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-serde@4.0.2':
- resolution: {integrity: sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==}
+ '@smithy/middleware-serde@4.0.3':
+ resolution: {integrity: sha512-rfgDVrgLEVMmMn0BI8O+8OVr6vXzjV7HZj57l0QxslhzbvVfikZbVfBVthjLHqib4BW44QhcIgJpvebHlRaC9A==}
engines: {node: '>=18.0.0'}
- '@smithy/middleware-stack@4.0.1':
- resolution: {integrity: sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==}
+ '@smithy/middleware-stack@4.0.2':
+ resolution: {integrity: sha512-eSPVcuJJGVYrFYu2hEq8g8WWdJav3sdrI4o2c6z/rjnYDd3xH9j9E7deZQCzFn4QvGPouLngH3dQ+QVTxv5bOQ==}
engines: {node: '>=18.0.0'}
- '@smithy/node-config-provider@4.0.1':
- resolution: {integrity: sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==}
+ '@smithy/node-config-provider@4.0.2':
+ resolution: {integrity: sha512-WgCkILRZfJwJ4Da92a6t3ozN/zcvYyJGUTmfGbgS/FkCcoCjl7G4FJaCDN1ySdvLvemnQeo25FdkyMSTSwulsw==}
engines: {node: '>=18.0.0'}
- '@smithy/node-http-handler@4.0.3':
- resolution: {integrity: sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA==}
+ '@smithy/node-http-handler@4.0.4':
+ resolution: {integrity: sha512-/mdqabuAT3o/ihBGjL94PUbTSPSRJ0eeVTdgADzow0wRJ0rN4A27EOrtlK56MYiO1fDvlO3jVTCxQtQmK9dZ1g==}
engines: {node: '>=18.0.0'}
- '@smithy/property-provider@4.0.1':
- resolution: {integrity: sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==}
+ '@smithy/property-provider@4.0.2':
+ resolution: {integrity: sha512-wNRoQC1uISOuNc2s4hkOYwYllmiyrvVXWMtq+TysNRVQaHm4yoafYQyjN/goYZS+QbYlPIbb/QRjaUZMuzwQ7A==}
engines: {node: '>=18.0.0'}
- '@smithy/protocol-http@5.0.1':
- resolution: {integrity: sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==}
+ '@smithy/protocol-http@5.1.0':
+ resolution: {integrity: sha512-KxAOL1nUNw2JTYrtviRRjEnykIDhxc84qMBzxvu1MUfQfHTuBlCG7PA6EdVwqpJjH7glw7FqQoFxUJSyBQgu7g==}
engines: {node: '>=18.0.0'}
- '@smithy/querystring-builder@4.0.1':
- resolution: {integrity: sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==}
+ '@smithy/querystring-builder@4.0.2':
+ resolution: {integrity: sha512-NTOs0FwHw1vimmQM4ebh+wFQvOwkEf/kQL6bSM1Lock+Bv4I89B3hGYoUEPkmvYPkDKyp5UdXJYu+PoTQ3T31Q==}
engines: {node: '>=18.0.0'}
- '@smithy/querystring-parser@4.0.1':
- resolution: {integrity: sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==}
+ '@smithy/querystring-parser@4.0.2':
+ resolution: {integrity: sha512-v6w8wnmZcVXjfVLjxw8qF7OwESD9wnpjp0Dqry/Pod0/5vcEA3qxCr+BhbOHlxS8O+29eLpT3aagxXGwIoEk7Q==}
engines: {node: '>=18.0.0'}
- '@smithy/service-error-classification@4.0.1':
- resolution: {integrity: sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==}
+ '@smithy/service-error-classification@4.0.2':
+ resolution: {integrity: sha512-LA86xeFpTKn270Hbkixqs5n73S+LVM0/VZco8dqd+JT75Dyx3Lcw/MraL7ybjmz786+160K8rPOmhsq0SocoJQ==}
engines: {node: '>=18.0.0'}
- '@smithy/shared-ini-file-loader@4.0.1':
- resolution: {integrity: sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==}
+ '@smithy/shared-ini-file-loader@4.0.2':
+ resolution: {integrity: sha512-J9/gTWBGVuFZ01oVA6vdb4DAjf1XbDhK6sLsu3OS9qmLrS6KB5ygpeHiM3miIbj1qgSJ96GYszXFWv6ErJ8QEw==}
engines: {node: '>=18.0.0'}
- '@smithy/signature-v4@5.0.1':
- resolution: {integrity: sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==}
+ '@smithy/signature-v4@5.0.2':
+ resolution: {integrity: sha512-Mz+mc7okA73Lyz8zQKJNyr7lIcHLiPYp0+oiqiMNc/t7/Kf2BENs5d63pEj7oPqdjaum6g0Fc8wC78dY1TgtXw==}
engines: {node: '>=18.0.0'}
- '@smithy/smithy-client@4.1.6':
- resolution: {integrity: sha512-UYDolNg6h2O0L+cJjtgSyKKvEKCOa/8FHYJnBobyeoeWDmNpXjwOAtw16ezyeu1ETuuLEOZbrynK0ZY1Lx9Jbw==}
+ '@smithy/smithy-client@4.2.0':
+ resolution: {integrity: sha512-Qs65/w30pWV7LSFAez9DKy0Koaoh3iHhpcpCCJ4waj/iqwsuSzJna2+vYwq46yBaqO5ZbP9TjUsATUNxrKeBdw==}
engines: {node: '>=18.0.0'}
- '@smithy/types@4.1.0':
- resolution: {integrity: sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==}
+ '@smithy/types@4.2.0':
+ resolution: {integrity: sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==}
engines: {node: '>=18.0.0'}
- '@smithy/url-parser@4.0.1':
- resolution: {integrity: sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==}
+ '@smithy/url-parser@4.0.2':
+ resolution: {integrity: sha512-Bm8n3j2ScqnT+kJaClSVCMeiSenK6jVAzZCNewsYWuZtnBehEz4r2qP0riZySZVfzB+03XZHJeqfmJDkeeSLiQ==}
engines: {node: '>=18.0.0'}
'@smithy/util-base64@4.0.0':
@@ -5281,32 +5289,32 @@ packages:
resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==}
engines: {node: '>=18.0.0'}
- '@smithy/util-defaults-mode-browser@4.0.7':
- resolution: {integrity: sha512-CZgDDrYHLv0RUElOsmZtAnp1pIjwDVCSuZWOPhIOBvG36RDfX1Q9+6lS61xBf+qqvHoqRjHxgINeQz47cYFC2Q==}
+ '@smithy/util-defaults-mode-browser@4.0.8':
+ resolution: {integrity: sha512-ZTypzBra+lI/LfTYZeop9UjoJhhGRTg3pxrNpfSTQLd3AJ37r2z4AXTKpq1rFXiiUIJsYyFgNJdjWRGP/cbBaQ==}
engines: {node: '>=18.0.0'}
- '@smithy/util-defaults-mode-node@4.0.7':
- resolution: {integrity: sha512-79fQW3hnfCdrfIi1soPbK3zmooRFnLpSx3Vxi6nUlqaaQeC5dm8plt4OTNDNqEEEDkvKghZSaoti684dQFVrGQ==}
+ '@smithy/util-defaults-mode-node@4.0.8':
+ resolution: {integrity: sha512-Rgk0Jc/UDfRTzVthye/k2dDsz5Xxs9LZaKCNPgJTRyoyBoeiNCnHsYGOyu1PKN+sDyPnJzMOz22JbwxzBp9NNA==}
engines: {node: '>=18.0.0'}
- '@smithy/util-endpoints@3.0.1':
- resolution: {integrity: sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==}
+ '@smithy/util-endpoints@3.0.2':
+ resolution: {integrity: sha512-6QSutU5ZyrpNbnd51zRTL7goojlcnuOB55+F9VBD+j8JpRY50IGamsjlycrmpn8PQkmJucFW8A0LSfXj7jjtLQ==}
engines: {node: '>=18.0.0'}
'@smithy/util-hex-encoding@4.0.0':
resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==}
engines: {node: '>=18.0.0'}
- '@smithy/util-middleware@4.0.1':
- resolution: {integrity: sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==}
+ '@smithy/util-middleware@4.0.2':
+ resolution: {integrity: sha512-6GDamTGLuBQVAEuQ4yDQ+ti/YINf/MEmIegrEeg7DdB/sld8BX1lqt9RRuIcABOhAGTA50bRbPzErez7SlDtDQ==}
engines: {node: '>=18.0.0'}
- '@smithy/util-retry@4.0.1':
- resolution: {integrity: sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==}
+ '@smithy/util-retry@4.0.2':
+ resolution: {integrity: sha512-Qryc+QG+7BCpvjloFLQrmlSd0RsVRHejRXd78jNO3+oREueCjwG1CCEH1vduw/ZkM1U9TztwIKVIi3+8MJScGg==}
engines: {node: '>=18.0.0'}
- '@smithy/util-stream@4.1.2':
- resolution: {integrity: sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw==}
+ '@smithy/util-stream@4.2.0':
+ resolution: {integrity: sha512-Vj1TtwWnuWqdgQI6YTUF5hQ/0jmFiOYsc51CSMgj7QfyO+RF4EnT2HNjoviNlOOmgzgvf3f5yno+EiC4vrnaWQ==}
engines: {node: '>=18.0.0'}
'@smithy/util-uri-escape@4.0.0':
@@ -5321,8 +5329,8 @@ packages:
resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==}
engines: {node: '>=18.0.0'}
- '@smithy/util-waiter@4.0.2':
- resolution: {integrity: sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==}
+ '@smithy/util-waiter@4.0.3':
+ resolution: {integrity: sha512-JtaY3FxmD+te+KSI2FJuEcfNC9T/DGGVf551babM7fAaXhjJUt7oSYurH1Devxd2+BOSUACCgt3buinx4UnmEA==}
engines: {node: '>=18.0.0'}
'@sqltools/formatter@1.2.5':
@@ -5711,6 +5719,9 @@ packages:
'@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+ '@types/estree@1.0.7':
+ resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+
'@types/graceful-fs@4.1.9':
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
@@ -5777,8 +5788,8 @@ packages:
'@types/node@12.20.55':
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
- '@types/node@18.19.80':
- resolution: {integrity: sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==}
+ '@types/node@18.19.83':
+ resolution: {integrity: sha512-D69JeR5SfFS5H6FLbUaS0vE4r1dGhmMBbG4Ed6BNS4wkDK8GZjsdCShT5LCN59vOHEUHnFCY9J4aclXlIphMkA==}
'@types/node@22.10.2':
resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==}
@@ -5924,8 +5935,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.26.1':
- resolution: {integrity: sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==}
+ '@typescript-eslint/scope-manager@8.28.0':
+ resolution: {integrity: sha512-u2oITX3BJwzWCapoZ/pXw6BCOl8rJP4Ij/3wPoGvY8XwvXflOzd1kLrDUUUAIEdJSFh+ASwdTHqtan9xSg8buw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/type-utils@7.18.0':
@@ -5957,8 +5968,8 @@ packages:
resolution: {integrity: sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/types@8.26.1':
- resolution: {integrity: sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==}
+ '@typescript-eslint/types@8.28.0':
+ resolution: {integrity: sha512-bn4WS1bkKEjx7HqiwG2JNB3YJdC1q6Ue7GyGlwPHyt0TnVq6TtD/hiOdTZt71sq0s7UzqBFXD8t8o2e63tXgwA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@5.62.0':
@@ -5985,8 +5996,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <5.8.0'
- '@typescript-eslint/typescript-estree@8.26.1':
- resolution: {integrity: sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==}
+ '@typescript-eslint/typescript-estree@8.28.0':
+ resolution: {integrity: sha512-H74nHEeBGeklctAVUvmDkxB1mk+PAZ9FiOMPFncdqeRBXxk1lWSYraHw8V12b7aa6Sg9HOBNbGdSHobBPuQSuA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
@@ -6010,8 +6021,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.8.0'
- '@typescript-eslint/utils@8.26.1':
- resolution: {integrity: sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==}
+ '@typescript-eslint/utils@8.28.0':
+ resolution: {integrity: sha512-OELa9hbTYciYITqgurT1u/SzpQVtDLmQMFzy/N8pQE+tefOyCWT79jHsav294aTqV1q1u+VzqDGbuujvRYaeSQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -6029,8 +6040,8 @@ packages:
resolution: {integrity: sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/visitor-keys@8.26.1':
- resolution: {integrity: sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==}
+ '@typescript-eslint/visitor-keys@8.28.0':
+ resolution: {integrity: sha512-hbn8SZ8w4u2pRwgQ1GlUrPKE+t2XvcCW5tTRF7j6SMYIuYG37XuzIW44JCZPa36evi0Oy2SnM664BlIaAuQcvg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.3.0':
@@ -6048,58 +6059,78 @@ packages:
'@unkey/rbac@0.3.1':
resolution: {integrity: sha512-Hj+52XRIlBBl3/qOUq9K71Fwy3PWExBQOpOClVYHdrcmbgqNL6L4EdW/BzliLhqPCdwZTPVSJTnZ3Hw4ZYixsQ==}
- '@unrs/rspack-resolver-binding-darwin-arm64@1.2.1':
- resolution: {integrity: sha512-xgSjy64typsn/lhQk/uKaS363H7ZeIBlWSh25FJFWXSCeLMHpEZ0umDo5Vzqi5iS26OZ5R1SpQkwiS78GhQRjw==}
+ '@unrs/rspack-resolver-binding-darwin-arm64@1.3.0':
+ resolution: {integrity: sha512-EcjI0Hh2HiNOM0B9UuYH1PfLWgE6/SBQ4dKoHXWNloERfveha/n6aUZSBThtPGnJenmdfaJYXXZtqyNbWtJAFw==}
cpu: [arm64]
os: [darwin]
- '@unrs/rspack-resolver-binding-darwin-x64@1.2.1':
- resolution: {integrity: sha512-3maDtW0vehzciEbuLxc2g+0FmDw5LGfCt+yMN1ZDn0lW0ikEBEFp6ul3h2fRphtfuCc7IvBJE9WWTt1UHkS7Nw==}
+ '@unrs/rspack-resolver-binding-darwin-x64@1.3.0':
+ resolution: {integrity: sha512-3CgG+mhfudDfnaDqwEl0W1mcGTto5f5mqPyJSXcWDxrnNc7pr/p01khIgWOoOD1eCwVejmgpYvRKGBwJPwgHOQ==}
cpu: [x64]
os: [darwin]
- '@unrs/rspack-resolver-binding-freebsd-x64@1.2.1':
- resolution: {integrity: sha512-aN6ifws9rNLjK2+6sIU9wvHyjXEf3S5+EZTHRarzd4jfa8i5pA7Mwt28un2DZVrBtIxhWDQvUPVKGI7zSBfVCA==}
+ '@unrs/rspack-resolver-binding-freebsd-x64@1.3.0':
+ resolution: {integrity: sha512-ww8BwryDrpXlSajwSIEUXEv8oKDkw04L2ke3hxjaxWohuBV8pAQie9XBS4yQTyREuL2ypcqbARfoCXJJzVp7ig==}
cpu: [x64]
os: [freebsd]
- '@unrs/rspack-resolver-binding-linux-arm-gnueabihf@1.2.1':
- resolution: {integrity: sha512-tKqu9VQyCO1yEUX6n6jgOHi7SJA9e6lvHczK60gur4VBITxnPmVYiCj2aekrOOIavvvjjuWAL2rqPQuc4g7RHQ==}
+ '@unrs/rspack-resolver-binding-linux-arm-gnueabihf@1.3.0':
+ resolution: {integrity: sha512-WyhonI1mkuAlnG2iaMjk7uy4aWX+FWi2Au8qCCwj57wVHbAEfrN6xN2YhzbrsCC+ciumKhj5c01MqwsnYDNzWQ==}
cpu: [arm]
os: [linux]
- '@unrs/rspack-resolver-binding-linux-arm64-gnu@1.2.1':
- resolution: {integrity: sha512-+xDI0kvwPiCR7334O83TPfaUXSe0UMVi5srQpQxP4+SDVYuONWsbwAC1IXe+yfOwRVGZsUdW9wE0ZiWs4Z+egw==}
+ '@unrs/rspack-resolver-binding-linux-arm-musleabihf@1.3.0':
+ resolution: {integrity: sha512-+uCP6hIAMVWHKQnLZHESJ1U1TFVGLR3FTeaS2A4zB0k8w+IbZlWwl9FiBUOwOiqhcCCyKiUEifgnYFNGpxi3pw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/rspack-resolver-binding-linux-arm64-gnu@1.3.0':
+ resolution: {integrity: sha512-p+s/Wp8rf75Qqs2EPw4HC0xVLLW+/60MlVAsB7TYLoeg1e1CU/QCis36FxpziLS0ZY2+wXdTnPUxr+5kkThzwQ==}
cpu: [arm64]
os: [linux]
- '@unrs/rspack-resolver-binding-linux-arm64-musl@1.2.1':
- resolution: {integrity: sha512-fcrVHlw+6UgQliMbI0znFD4ASWKuyY17FdH67ZmyNH62b0hRhhxQuJE0D6N3410m8lKVu4QW4EzFiHxYFUC0cg==}
+ '@unrs/rspack-resolver-binding-linux-arm64-musl@1.3.0':
+ resolution: {integrity: sha512-cZEL9jmZ2kAN53MEk+fFCRJM8pRwOEboDn7sTLjZW+hL6a0/8JNfHP20n8+MBDrhyD34BSF4A6wPCj/LNhtOIQ==}
cpu: [arm64]
os: [linux]
- '@unrs/rspack-resolver-binding-linux-x64-gnu@1.2.1':
- resolution: {integrity: sha512-xISTyUJ2PiAT4x9nlh8FdciDcdKbsatgK9qO7EEsILt9VB7Y1mHYGaszj3ouxfZnaKQ13WwW+dFLGxkZLP/WVg==}
+ '@unrs/rspack-resolver-binding-linux-ppc64-gnu@1.3.0':
+ resolution: {integrity: sha512-IOeRhcMXTNlk2oApsOozYVcOHu4t1EKYKnTz4huzdPyKNPX0Y9C7X8/6rk4aR3Inb5s4oVMT9IVKdgNXLcpGAQ==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@unrs/rspack-resolver-binding-linux-s390x-gnu@1.3.0':
+ resolution: {integrity: sha512-op54XrlEbhgVRCxzF1pHFcLamdOmHDapwrqJ9xYRB7ZjwP/zQCKzz/uAsSaAlyQmbSi/PXV7lwfca4xkv860/Q==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@unrs/rspack-resolver-binding-linux-x64-gnu@1.3.0':
+ resolution: {integrity: sha512-orbQF7sN02N/b9QF8Xp1RBO5YkfI+AYo9VZw0H2Gh4JYWSuiDHjOPEeFPDIRyWmXbQJuiVNSB+e1pZOjPPKIyg==}
cpu: [x64]
os: [linux]
- '@unrs/rspack-resolver-binding-linux-x64-musl@1.2.1':
- resolution: {integrity: sha512-LE8EjE/iPlvSsFbZ6P9c0Jh5/pifAi03UYeXYwOnQqt1molKAPMB0R4kGWOM7dnDYaNgkk1MN9MOTCLsqe97Fw==}
+ '@unrs/rspack-resolver-binding-linux-x64-musl@1.3.0':
+ resolution: {integrity: sha512-kpjqjIAC9MfsjmlgmgeC8U9gZi6g/HTuCqpI7SBMjsa7/9MvBaQ6TJ7dtnsV/+DXvfJ2+L5teBBXG+XxfpvIFA==}
cpu: [x64]
os: [linux]
- '@unrs/rspack-resolver-binding-wasm32-wasi@1.2.1':
- resolution: {integrity: sha512-XERT3B88+G55RgG96May8QvAdgGzHr8qtQ70cIdbuWTpIcA0I76cnxSZ8Qwx33y73jE5N/myX2YKDlFksn4z6w==}
+ '@unrs/rspack-resolver-binding-wasm32-wasi@1.3.0':
+ resolution: {integrity: sha512-JAg0hY3kGsCPk7Jgh16yMTBZ6VEnoNR1DFZxiozjKwH+zSCfuDuM5S15gr50ofbwVw9drobIP2TTHdKZ15MJZQ==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
- '@unrs/rspack-resolver-binding-win32-arm64-msvc@1.2.1':
- resolution: {integrity: sha512-I8OLI6JbmNx2E/SG8MOEuo/d6rNx8dwgL09rcItSMcP82v1oZ8AY8HNA+axxuxEH95nkb6MPJU09p63isDvzrA==}
+ '@unrs/rspack-resolver-binding-win32-arm64-msvc@1.3.0':
+ resolution: {integrity: sha512-h5N83i407ntS3ndDkhT/3vC3Dj8oP0BIwMtekETNJcxk7IuWccSXifzCEhdxxu/FOX4OICGIHdHrxf5fJuAjfw==}
cpu: [arm64]
os: [win32]
- '@unrs/rspack-resolver-binding-win32-x64-msvc@1.2.1':
- resolution: {integrity: sha512-s5WvCljhFqiE3McvaD3lDIsQpmk7gEJRUHy1PRwLPzEB7snq9P2xQeqgzdjGhJQq62jBFz7NDy7NbMkocWr2pw==}
+ '@unrs/rspack-resolver-binding-win32-ia32-msvc@1.3.0':
+ resolution: {integrity: sha512-9QH7Gq3dRL8Q/D6PGS3Dwtjx9yw6kbCEu6iBkAUhFTDAuVUk2L0H/5NekRVA13AQaSc3OsEUKt60EOn/kq5Dug==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@unrs/rspack-resolver-binding-win32-x64-msvc@1.3.0':
+ resolution: {integrity: sha512-IYuXJCuwBOVV0H73l6auaZwtAPHjCPBJkxd4Co0yO6dSjDM5Na5OceaxhUmJLZ3z8kuEGhTYWIHH7PchGztnlg==}
cpu: [x64]
os: [win32]
@@ -6785,8 +6816,8 @@ packages:
axios@1.7.9:
resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==}
- axios@1.8.3:
- resolution: {integrity: sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==}
+ axios@1.8.4:
+ resolution: {integrity: sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==}
axobject-query@4.1.0:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
@@ -6811,11 +6842,6 @@ packages:
resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
- babel-plugin-polyfill-corejs2@0.4.12:
- resolution: {integrity: sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==}
- peerDependencies:
- '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
babel-plugin-polyfill-corejs2@0.4.13:
resolution: {integrity: sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==}
peerDependencies:
@@ -6826,11 +6852,6 @@ packages:
peerDependencies:
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
- babel-plugin-polyfill-regenerator@0.6.3:
- resolution: {integrity: sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==}
- peerDependencies:
- '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
babel-plugin-polyfill-regenerator@0.6.4:
resolution: {integrity: sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==}
peerDependencies:
@@ -7124,8 +7145,8 @@ packages:
camelize@1.0.1:
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
- caniuse-lite@1.0.30001706:
- resolution: {integrity: sha512-3ZczoTApMAZwPKYWmwVbQMFpXBDds3/0VciVoUwPUbldlYyVLmRVuRs/PcUZtHpbLRpzzDvrvnFuREsGt6lUug==}
+ caniuse-lite@1.0.30001707:
+ resolution: {integrity: sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
@@ -7899,8 +7920,8 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
- electron-to-chromium@1.5.120:
- resolution: {integrity: sha512-oTUp3gfX1gZI+xfD2djr2rzQdHCwHzPQrrK0CD7WpTdF0nPdQ/INcRVjWgLdCT4a9W3jFObR9DAfsuyFQnI8CQ==}
+ electron-to-chromium@1.5.123:
+ resolution: {integrity: sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==}
elliptic@6.6.1:
resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==}
@@ -8341,8 +8362,8 @@ packages:
resolution: {integrity: sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==}
engines: {node: '>=12.0.0'}
- expo-asset@11.0.4:
- resolution: {integrity: sha512-CdIywU0HrR3wsW5c3n0cT3jW9hccZdnqGsRqY+EY/RWzJbDXtDfAQVEiFHO3mDK7oveUwrP2jK/6ZRNek41/sg==}
+ expo-asset@11.0.5:
+ resolution: {integrity: sha512-TL60LmMBGVzs3NQcO8ylWqBumMh4sx0lmeJsn7+9C88fylGDhyyVnKZ1PyTXo9CVDBkndutZx2JUEQWM9BaiXw==}
peerDependencies:
expo: '*'
react: '*'
@@ -8354,8 +8375,8 @@ packages:
expo: '*'
react-native: '*'
- expo-file-system@18.0.11:
- resolution: {integrity: sha512-yDwYfEzWgPXsBZHJW2RJ8Q66ceiFN9Wa5D20pp3fjXVkzPBDwxnYwiPWk4pVmCa5g4X5KYMoMne1pUrsL4OEpg==}
+ expo-file-system@18.0.12:
+ resolution: {integrity: sha512-HAkrd/mb8r+G3lJ9MzmGeuW2B+BxQR1joKfeCyY4deLl1zoZ48FrAWjgZjHK9aHUVhJ0ehzInu/NQtikKytaeg==}
peerDependencies:
expo: '*'
react-native: '*'
@@ -9753,8 +9774,8 @@ packages:
lexical@0.21.0:
resolution: {integrity: sha512-Dxc5SCG4kB+wF+Rh55ism3SuecOKeOtCtGHFGKd6pj2QKVojtjkxGTQPMt7//2z5rMSue4R+hmRM0pCEZflupA==}
- lib0@0.2.99:
- resolution: {integrity: sha512-vwztYuUf1uf/1zQxfzRfO5yzfNKhTtgOByCruuiQQxWQXnPb8Itaube5ylofcV0oM0aKal9Mv+S1s1Ky0UYP1w==}
+ lib0@0.2.101:
+ resolution: {integrity: sha512-LljA6+Ehf0Z7YnxhgSAvspzWALjW4wlWdN/W4iGiqYc1KvXQgOVXWI0xwlwqozIL5WRdKeUW2gq0DLhFsY+Xlw==}
engines: {node: '>=16'}
hasBin: true
@@ -10147,80 +10168,80 @@ packages:
resolution: {integrity: sha512-YZziRs0MgA3pzCkkvOoQRXjIoVjvrpi/yRlJnObyIvMP6lFdtyG4nUGIwGY9VXnBvxmXD6mPY2e+NSw6JAyiRg==}
engines: {node: '>=18'}
- metro-babel-transformer@0.81.3:
- resolution: {integrity: sha512-ENqtnPy2mQZFOuKrbqHRcAwZuaYe43X+30xIF0xlkLuMyCvc0CsFzrrSK9EqrQwexhVlqaRALb0GQbBMcE/y8g==}
+ metro-babel-transformer@0.81.4:
+ resolution: {integrity: sha512-WW0yswWrW+eTVK9sYD+b1HwWOiUlZlUoomiw9TIOk0C+dh2V90Wttn/8g62kYi0Y4i+cJfISerB2LbV4nuRGTA==}
engines: {node: '>=18.18'}
metro-cache-key@0.80.12:
resolution: {integrity: sha512-o4BspKnugg/pE45ei0LGHVuBJXwRgruW7oSFAeSZvBKA/sGr0UhOGY3uycOgWInnS3v5yTTfiBA9lHlNRhsvGA==}
engines: {node: '>=18'}
- metro-cache-key@0.81.3:
- resolution: {integrity: sha512-KPsPSRUd6uRva7k7k/DqiiD8td7URQWx0RkX/Cj5+bed5zSXEg/XoQA+b+DmMxS5C7TqP61Fh3XvHx6TQRW82A==}
+ metro-cache-key@0.81.4:
+ resolution: {integrity: sha512-3SaWQybvf1ivasjBegIxzVKLJzOpcz+KsnGwXFOYADQq0VN4cnM7tT+u2jkOhk6yJiiO1WIjl68hqyMOQJRRLg==}
engines: {node: '>=18.18'}
metro-cache@0.80.12:
resolution: {integrity: sha512-p5kNHh2KJ0pbQI/H7ZBPCEwkyNcSz7OUkslzsiIWBMPQGFJ/xArMwkV7I+GJcWh+b4m6zbLxE5fk6fqbVK1xGA==}
engines: {node: '>=18'}
- metro-cache@0.81.3:
- resolution: {integrity: sha512-6UelMQYjlto/79tTXu0vsTxAX4e+Bkf0tgtDL1BNx3wd68pBg8qKIYpJPaUlOIaNUzFXTBDjYwUverkEW0KAtA==}
+ metro-cache@0.81.4:
+ resolution: {integrity: sha512-sxCPH3gowDxazSaZZrwdNPEpnxR8UeXDnvPjBF9+5btDBNN2DpWvDAXPvrohkYkFImhc0LajS2V7eOXvu9PnvQ==}
engines: {node: '>=18.18'}
metro-config@0.80.12:
resolution: {integrity: sha512-4rwOWwrhm62LjB12ytiuR5NgK1ZBNr24/He8mqCsC+HXZ+ATbrewLNztzbAZHtFsrxP4D4GLTGgh96pCpYLSAQ==}
engines: {node: '>=18'}
- metro-config@0.81.3:
- resolution: {integrity: sha512-WpTaT0iQr5juVY50Y/cyacG2ggZqF38VshEQepT+ovPK8E/xUVxlbO5yxLSXUxxUXX3Hka9r6g64+y2WC6c/xQ==}
+ metro-config@0.81.4:
+ resolution: {integrity: sha512-QnhMy3bRiuimCTy7oi5Ug60javrSa3lPh0gpMAspQZHY9h6y86jwHtZPLtlj8hdWQESIlrbeL8inMSF6qI/i9Q==}
engines: {node: '>=18.18'}
metro-core@0.80.12:
resolution: {integrity: sha512-QqdJ/yAK+IpPs2HU/h5v2pKEdANBagSsc6DRSjnwSyJsCoHlmyJKCaCJ7KhWGx+N4OHxh37hoA8fc2CuZbx0Fw==}
engines: {node: '>=18'}
- metro-core@0.81.3:
- resolution: {integrity: sha512-WZ+qohnpvvSWdPj1VJPUrZz+2ik29M+UUpMU6YrmzQUfDyZ6JYHhzlw5WVBtwpt/+2xTsIyrZ2C1fByT/DsLQA==}
+ metro-core@0.81.4:
+ resolution: {integrity: sha512-GdL4IgmgJhrMA/rTy2lRqXKeXfC77Rg+uvhUEkbhyfj/oz7PrdSgvIFzziapjdHwk1XYq0KyFh/CcVm8ZawG6A==}
engines: {node: '>=18.18'}
metro-file-map@0.80.12:
resolution: {integrity: sha512-sYdemWSlk66bWzW2wp79kcPMzwuG32x1ZF3otI0QZTmrnTaaTiGyhE66P1z6KR4n2Eu5QXiABa6EWbAQv0r8bw==}
engines: {node: '>=18'}
- metro-file-map@0.81.3:
- resolution: {integrity: sha512-F+t4lnVRoauJxtr9xmI4pWIOE77/vl0IrHDGeJSI9cW6LmuqxkpOlZHTKpbs/hMAo6+KhG2JMJACQDvXDLd/GA==}
+ metro-file-map@0.81.4:
+ resolution: {integrity: sha512-qUIBzkiqOi3qEuscu4cJ83OYQ4hVzjON19FAySWqYys9GKCmxlKa7LkmwqdpBso6lQl+JXZ7nCacX90w5wQvPA==}
engines: {node: '>=18.18'}
metro-minify-terser@0.80.12:
resolution: {integrity: sha512-muWzUw3y5k+9083ZoX9VaJLWEV2Jcgi+Oan0Mmb/fBNMPqP9xVDuy4pOMn/HOiGndgfh/MK7s4bsjkyLJKMnXQ==}
engines: {node: '>=18'}
- metro-minify-terser@0.81.3:
- resolution: {integrity: sha512-912AYv3OmwcbUwzCdWbdQRk+RV6kXXluHKlhBdYFD3kr4Ece691rzlofU/Mlt9qZrhHtctD5Q8cFqOEf9Z69bQ==}
+ metro-minify-terser@0.81.4:
+ resolution: {integrity: sha512-oVvq/AGvqmbhuijJDZZ9npeWzaVyeBwQKtdlnjcQ9fH7nR15RiBr5y2zTdgTEdynqOIb1Kc16l8CQIUSzOWVFA==}
engines: {node: '>=18.18'}
metro-resolver@0.80.12:
resolution: {integrity: sha512-PR24gYRZnYHM3xT9pg6BdbrGbM/Cu1TcyIFBVlAk7qDAuHkUNQ1nMzWumWs+kwSvtd9eZGzHoucGJpTUEeLZAw==}
engines: {node: '>=18'}
- metro-resolver@0.81.3:
- resolution: {integrity: sha512-XnjENY1c6jcsEfFVIjN/8McUIInCVgGxv5eva+9ZWeCTyiAE/L5HPj2ai/Myb349+6QuSMR0dscTkKCnOwWXdw==}
+ metro-resolver@0.81.4:
+ resolution: {integrity: sha512-Ng7G2mXjSExMeRzj6GC19G6IJ0mfIbOLgjArsMWJgtt9ViZiluCwgWsMW9juBC5NSwjJxUMK2x6pC5NIMFLiHA==}
engines: {node: '>=18.18'}
metro-runtime@0.80.12:
resolution: {integrity: sha512-LIx7+92p5rpI0i6iB4S4GBvvLxStNt6fF0oPMaUd1Weku7jZdfkCZzmrtDD9CSQ6EPb0T9NUZoyXIxlBa3wOCw==}
engines: {node: '>=18'}
- metro-runtime@0.81.3:
- resolution: {integrity: sha512-neuGRMC2pgGKIFPbmbrxW41/SmvL7OX4i1LN+saUY2t1cZfxf9haQHUMCGhO3498uEL2N+ulKRSlQrHt6XwGaw==}
+ metro-runtime@0.81.4:
+ resolution: {integrity: sha512-fBoRgqkF69CwyPtBNxlDi5ha26Zc8f85n2THXYoh13Jn/Bkg8KIDCdKPp/A1BbSeNnkH/++H2EIIfnmaff4uRg==}
engines: {node: '>=18.18'}
metro-source-map@0.80.12:
resolution: {integrity: sha512-o+AXmE7hpvM8r8MKsx7TI21/eerYYy2DCDkWfoBkv+jNkl61khvDHlQn0cXZa6lrcNZiZkl9oHSMcwLLIrFmpw==}
engines: {node: '>=18'}
- metro-source-map@0.81.3:
- resolution: {integrity: sha512-BHJJurmDQRn3hCbBawh/UHzPz3duMpwpE3ofImO2DoWHYzn6nSg/D4wfCN4y14d9fFLE4e0I+BAOX1HWNP4jsw==}
+ metro-source-map@0.81.4:
+ resolution: {integrity: sha512-IOwVQ7mLqoqvsL70RZtl1EyE3f9jp43kVsAsb/B/zoWmu0/k4mwEhGLTxmjdXRkLJqPqPrh7WmFChAEf9trW4Q==}
engines: {node: '>=18.18'}
metro-symbolicate@0.80.12:
@@ -10228,8 +10249,8 @@ packages:
engines: {node: '>=18'}
hasBin: true
- metro-symbolicate@0.81.3:
- resolution: {integrity: sha512-LQLT6WopQmIz2SDSVh3Lw7nLzF58HpsrPYqRB7RpRXBYhYmPFIjiGaP8qqtKHXczM/5YAOJzpgt8t/OGZgh6Eg==}
+ metro-symbolicate@0.81.4:
+ resolution: {integrity: sha512-rWxTmYVN6/BOSaMDUHT8HgCuRf6acd0AjHkenYlHpmgxg7dqdnAG1hLq999q2XpW5rX+cMamZD5W5Ez2LqGaag==}
engines: {node: '>=18.18'}
hasBin: true
@@ -10237,16 +10258,16 @@ packages:
resolution: {integrity: sha512-WQWp00AcZvXuQdbjQbx1LzFR31IInlkCDYJNRs6gtEtAyhwpMMlL2KcHmdY+wjDO9RPcliZ+Xl1riOuBecVlPA==}
engines: {node: '>=18'}
- metro-transform-plugins@0.81.3:
- resolution: {integrity: sha512-4JMUXhBB5y4h3dyA272k7T7+U3+J4fSBcct0Y8Yur9ziZB/dK8fieEQg5ZPfEGsgOGI+54zTzOUqga6AgmZSNg==}
+ metro-transform-plugins@0.81.4:
+ resolution: {integrity: sha512-nlP069nDXm4v28vbll4QLApAlvVtlB66rP6h+ml8Q/CCQCPBXu2JLaoxUmkIOJQjLhMRUcgTyQHq+TXWJhydOQ==}
engines: {node: '>=18.18'}
metro-transform-worker@0.80.12:
resolution: {integrity: sha512-KAPFN1y3eVqEbKLx1I8WOarHPqDMUa8WelWxaJCNKO/yHCP26zELeqTJvhsQup+8uwB6EYi/sp0b6TGoh6lOEA==}
engines: {node: '>=18'}
- metro-transform-worker@0.81.3:
- resolution: {integrity: sha512-KZqm9sVyBKRygUxRm+yP4DguE9R1EEv28KJhIxghNp5dcdVXBYUPe1xHoc3QVdzD9c3tf8JFzA2FBlKTlwMwNg==}
+ metro-transform-worker@0.81.4:
+ resolution: {integrity: sha512-lKAeRZ8EUMtx2cA/Y4KvICr9bIr5SE03iK3lm+l9wyn2lkjLUuPjYVep159inLeDqC6AtSubsA8MZLziP7c03g==}
engines: {node: '>=18.18'}
metro@0.80.12:
@@ -10254,8 +10275,8 @@ packages:
engines: {node: '>=18'}
hasBin: true
- metro@0.81.3:
- resolution: {integrity: sha512-upilFs7z1uLKvdzFYHiVKrGT/uC7h7d53R0g/FaJoQvLfA8jQG2V69jeOcGi4wCsFYvl1zBSZvKxpQb0nA3giQ==}
+ metro@0.81.4:
+ resolution: {integrity: sha512-78f0aBNPuwXW7GFnSc+Y0vZhbuQorXxdgqQfvSRqcSizqwg9cwF27I05h47tL8AzQcizS1JZncvq4xf5u/Qykw==}
engines: {node: '>=18.18'}
hasBin: true
@@ -10588,11 +10609,6 @@ packages:
react: '*'
react-dom: '*'
- nanoid@3.3.10:
- resolution: {integrity: sha512-vSJJTG+t/dIKAUhUDw/dLdZ9s//5OxcHqLaDWWrW4Cdq7o6tdLIczUkMXt2MBNmk6sJRZBZRXVixs7URY1CmIg==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -10844,8 +10860,8 @@ packages:
resolution: {integrity: sha512-VMArClVT6LkhUGpnuEoBuyjG9rzUyEzg4PDkav6wK1cLhOK02gPCYFxoiB4mqVnrMhDpIzJcrGNAMVi9P+hXrw==}
engines: {node: '>=18'}
- ob1@0.81.3:
- resolution: {integrity: sha512-wd8zdH0DWsn2iDVn2zT/QURihcqoc73K8FhNCmQ16qkJaoYJLNb/N+huOwdCgsbNP8Lk/s1+dPnDETx+RzsrWA==}
+ ob1@0.81.4:
+ resolution: {integrity: sha512-EZLYM8hfPraC2SYOR5EWLFAPV5e6g+p83m2Jth9bzCpFxP1NDQJYXdmXRB2bfbaWQSmm6NkIQlbzk7uU5lLfgg==}
engines: {node: '>=18.18'}
object-assign@4.1.1:
@@ -11645,8 +11661,8 @@ packages:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
- quansync@0.2.8:
- resolution: {integrity: sha512-4+saucphJMazjt7iOM27mbFCk+D9dd/zmgMDCzRZ8MEoBfYp7lAvoN38et/phRQF6wOPMy/OROBGgoWeSKyluA==}
+ quansync@0.2.10:
+ resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
querystring-es3@0.2.1:
resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==}
@@ -12143,8 +12159,8 @@ packages:
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
- rollup@4.36.0:
- resolution: {integrity: sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==}
+ rollup@4.37.0:
+ resolution: {integrity: sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@@ -12154,8 +12170,8 @@ packages:
rrweb-cssom@0.8.0:
resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
- rspack-resolver@1.2.1:
- resolution: {integrity: sha512-yTaWGUvHOjcoyFMdVTdYt2nq2Hu8sw6ia3X9szloXFJlWLQZnQ9g/4TPhL3Bb3qN58Mkye8mFG7MCaKhya7fOw==}
+ rspack-resolver@1.3.0:
+ resolution: {integrity: sha512-az/PLDwa1xijNv4bAFBS8mtqqJC1Y3lVyFag4cuyIUOHq/ft5kSZlHbqYaLZLpsQtPWv4ZGDo5ycySKJzUvU/A==}
rtl-css-js@1.16.1:
resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==}
@@ -12946,11 +12962,11 @@ packages:
resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
engines: {node: '>=14.0.0'}
- tldts-core@6.1.84:
- resolution: {integrity: sha512-NaQa1W76W2aCGjXybvnMYzGSM4x8fvG2AN/pla7qxcg0ZHbooOPhA8kctmOZUDfZyhDL27OGNbwAeig8P4p1vg==}
+ tldts-core@6.1.85:
+ resolution: {integrity: sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==}
- tldts@6.1.84:
- resolution: {integrity: sha512-aRGIbCIF3teodtUFAYSdQONVmDRy21REM3o6JnqWn5ZkQBJJ4gHxhw6OfwQ+WkSAi3ASamrS4N4nyazWx6uTYg==}
+ tldts@6.1.85:
+ resolution: {integrity: sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==}
hasBin: true
tmp@0.0.33:
@@ -13005,8 +13021,8 @@ packages:
peerDependencies:
typescript: '>=4.2.0'
- ts-api-utils@2.0.1:
- resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==}
+ ts-api-utils@2.1.0:
+ resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
engines: {node: '>=18.12'}
peerDependencies:
typescript: '>=4.8.4'
@@ -13522,8 +13538,8 @@ packages:
vite:
optional: true
- vite@5.4.14:
- resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==}
+ vite@5.4.15:
+ resolution: {integrity: sha512-6ANcZRivqL/4WtwPGTKNaosuNJr5tWiftOC7liM7G9+rMb8+oeJeyzymDu4rTN93seySBmbjSfsS3Vzr19KNtA==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -13553,8 +13569,8 @@ packages:
terser:
optional: true
- vite@6.0.9:
- resolution: {integrity: sha512-MSgUxHcaXLtnBPktkbUSoQUANApKYuxZ6DrbVENlIorbhL2dZydTLaZ01tjUoE3szeFzlFk9ANOKk0xurh4MKA==}
+ vite@6.0.12:
+ resolution: {integrity: sha512-gzLogvGSgX2xyAt0J5qhJ7SmdO5aLdShABkU8Ev7dIl8AcrlFSLcj9GHReSq9pGJF/q5C4CZKdtDlkC6DyvQ3w==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
@@ -13593,8 +13609,8 @@ packages:
yaml:
optional: true
- vite@6.2.0:
- resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==}
+ vite@6.2.3:
+ resolution: {integrity: sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
@@ -14093,8 +14109,8 @@ packages:
peerDependencies:
zod: ^3.21.4
- zod-to-json-schema@3.24.4:
- resolution: {integrity: sha512-0uNlcvgabyrni9Ag8Vghj21drk7+7tp7VTwwR7KxxXXc/3pbXz2PHlDgj3cICahgF1kHm4dExBFj7BXrZJXzig==}
+ zod-to-json-schema@3.24.5:
+ resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==}
peerDependencies:
zod: ^3.24.1
@@ -14127,7 +14143,7 @@ snapshots:
dependencies:
'@ai-sdk/provider': 1.0.7
eventsource-parser: 3.0.0
- nanoid: 3.3.10
+ nanoid: 3.3.11
secure-json-parse: 2.7.0
optionalDependencies:
zod: 3.24.1
@@ -14150,7 +14166,7 @@ snapshots:
dependencies:
'@ai-sdk/provider': 1.0.7
'@ai-sdk/provider-utils': 2.1.6(zod@3.24.1)
- zod-to-json-schema: 3.24.4(zod@3.24.1)
+ zod-to-json-schema: 3.24.5(zod@3.24.1)
optionalDependencies:
zod: 3.24.1
@@ -14233,30 +14249,30 @@ snapshots:
'@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.5
- '@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.6
- '@smithy/middleware-retry': 4.0.7
- '@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.3
- '@smithy/protocol-http': 5.0.1
- '@smithy/smithy-client': 4.1.6
- '@smithy/types': 4.1.0
- '@smithy/url-parser': 4.0.1
+ '@smithy/config-resolver': 4.1.0
+ '@smithy/core': 3.2.0
+ '@smithy/fetch-http-handler': 5.0.2
+ '@smithy/hash-node': 4.0.2
+ '@smithy/invalid-dependency': 4.0.2
+ '@smithy/middleware-content-length': 4.0.2
+ '@smithy/middleware-endpoint': 4.1.0
+ '@smithy/middleware-retry': 4.1.0
+ '@smithy/middleware-serde': 4.0.3
+ '@smithy/middleware-stack': 4.0.2
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/node-http-handler': 4.0.4
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
+ '@smithy/url-parser': 4.0.2
'@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.7
- '@smithy/util-defaults-mode-node': 4.0.7
- '@smithy/util-endpoints': 3.0.1
- '@smithy/util-middleware': 4.0.1
- '@smithy/util-retry': 4.0.1
+ '@smithy/util-defaults-mode-browser': 4.0.8
+ '@smithy/util-defaults-mode-node': 4.0.8
+ '@smithy/util-endpoints': 3.0.2
+ '@smithy/util-middleware': 4.0.2
+ '@smithy/util-retry': 4.0.2
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
transitivePeerDependencies:
@@ -14278,32 +14294,32 @@ snapshots:
'@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.5
- '@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.6
- '@smithy/middleware-retry': 4.0.7
- '@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.3
- '@smithy/protocol-http': 5.0.1
- '@smithy/smithy-client': 4.1.6
- '@smithy/types': 4.1.0
- '@smithy/url-parser': 4.0.1
+ '@smithy/config-resolver': 4.1.0
+ '@smithy/core': 3.2.0
+ '@smithy/fetch-http-handler': 5.0.2
+ '@smithy/hash-node': 4.0.2
+ '@smithy/invalid-dependency': 4.0.2
+ '@smithy/middleware-content-length': 4.0.2
+ '@smithy/middleware-endpoint': 4.1.0
+ '@smithy/middleware-retry': 4.1.0
+ '@smithy/middleware-serde': 4.0.3
+ '@smithy/middleware-stack': 4.0.2
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/node-http-handler': 4.0.4
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
+ '@smithy/url-parser': 4.0.2
'@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.7
- '@smithy/util-defaults-mode-node': 4.0.7
- '@smithy/util-endpoints': 3.0.1
- '@smithy/util-middleware': 4.0.1
- '@smithy/util-retry': 4.0.1
+ '@smithy/util-defaults-mode-browser': 4.0.8
+ '@smithy/util-defaults-mode-node': 4.0.8
+ '@smithy/util-endpoints': 3.0.2
+ '@smithy/util-middleware': 4.0.2
+ '@smithy/util-retry': 4.0.2
'@smithy/util-utf8': 4.0.0
- '@smithy/util-waiter': 4.0.2
+ '@smithy/util-waiter': 4.0.3
'@types/uuid': 9.0.8
tslib: 2.8.1
uuid: 9.0.1
@@ -14334,39 +14350,39 @@ snapshots:
'@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.5
- '@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.6
- '@smithy/middleware-retry': 4.0.7
- '@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.3
- '@smithy/protocol-http': 5.0.1
- '@smithy/smithy-client': 4.1.6
- '@smithy/types': 4.1.0
- '@smithy/url-parser': 4.0.1
+ '@smithy/config-resolver': 4.1.0
+ '@smithy/core': 3.2.0
+ '@smithy/eventstream-serde-browser': 4.0.2
+ '@smithy/eventstream-serde-config-resolver': 4.1.0
+ '@smithy/eventstream-serde-node': 4.0.2
+ '@smithy/fetch-http-handler': 5.0.2
+ '@smithy/hash-blob-browser': 4.0.2
+ '@smithy/hash-node': 4.0.2
+ '@smithy/hash-stream-node': 4.0.2
+ '@smithy/invalid-dependency': 4.0.2
+ '@smithy/md5-js': 4.0.2
+ '@smithy/middleware-content-length': 4.0.2
+ '@smithy/middleware-endpoint': 4.1.0
+ '@smithy/middleware-retry': 4.1.0
+ '@smithy/middleware-serde': 4.0.3
+ '@smithy/middleware-stack': 4.0.2
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/node-http-handler': 4.0.4
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
+ '@smithy/url-parser': 4.0.2
'@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.7
- '@smithy/util-defaults-mode-node': 4.0.7
- '@smithy/util-endpoints': 3.0.1
- '@smithy/util-middleware': 4.0.1
- '@smithy/util-retry': 4.0.1
- '@smithy/util-stream': 4.1.2
+ '@smithy/util-defaults-mode-browser': 4.0.8
+ '@smithy/util-defaults-mode-node': 4.0.8
+ '@smithy/util-endpoints': 3.0.2
+ '@smithy/util-middleware': 4.0.2
+ '@smithy/util-retry': 4.0.2
+ '@smithy/util-stream': 4.2.0
'@smithy/util-utf8': 4.0.0
- '@smithy/util-waiter': 4.0.2
+ '@smithy/util-waiter': 4.0.3
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
@@ -14385,30 +14401,30 @@ snapshots:
'@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.5
- '@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.6
- '@smithy/middleware-retry': 4.0.7
- '@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.3
- '@smithy/protocol-http': 5.0.1
- '@smithy/smithy-client': 4.1.6
- '@smithy/types': 4.1.0
- '@smithy/url-parser': 4.0.1
+ '@smithy/config-resolver': 4.1.0
+ '@smithy/core': 3.2.0
+ '@smithy/fetch-http-handler': 5.0.2
+ '@smithy/hash-node': 4.0.2
+ '@smithy/invalid-dependency': 4.0.2
+ '@smithy/middleware-content-length': 4.0.2
+ '@smithy/middleware-endpoint': 4.1.0
+ '@smithy/middleware-retry': 4.1.0
+ '@smithy/middleware-serde': 4.0.3
+ '@smithy/middleware-stack': 4.0.2
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/node-http-handler': 4.0.4
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
+ '@smithy/url-parser': 4.0.2
'@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.7
- '@smithy/util-defaults-mode-node': 4.0.7
- '@smithy/util-endpoints': 3.0.1
- '@smithy/util-middleware': 4.0.1
- '@smithy/util-retry': 4.0.1
+ '@smithy/util-defaults-mode-browser': 4.0.8
+ '@smithy/util-defaults-mode-node': 4.0.8
+ '@smithy/util-endpoints': 3.0.2
+ '@smithy/util-middleware': 4.0.2
+ '@smithy/util-retry': 4.0.2
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
transitivePeerDependencies:
@@ -14417,14 +14433,14 @@ snapshots:
'@aws-sdk/core@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/core': 3.1.5
- '@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.6
- '@smithy/types': 4.1.0
- '@smithy/util-middleware': 4.0.1
+ '@smithy/core': 3.2.0
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/property-provider': 4.0.2
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/signature-v4': 5.0.2
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
+ '@smithy/util-middleware': 4.0.2
fast-xml-parser: 4.4.1
tslib: 2.8.1
@@ -14432,8 +14448,8 @@ snapshots:
dependencies:
'@aws-sdk/client-cognito-identity': 3.741.0(aws-crt@1.25.3)
'@aws-sdk/types': 3.734.0
- '@smithy/property-provider': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/property-provider': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
@@ -14442,21 +14458,21 @@ snapshots:
dependencies:
'@aws-sdk/core': 3.734.0
'@aws-sdk/types': 3.734.0
- '@smithy/property-provider': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/property-provider': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/credential-provider-http@3.734.0':
dependencies:
'@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.3
- '@smithy/property-provider': 4.0.1
- '@smithy/protocol-http': 5.0.1
- '@smithy/smithy-client': 4.1.6
- '@smithy/types': 4.1.0
- '@smithy/util-stream': 4.1.2
+ '@smithy/fetch-http-handler': 5.0.2
+ '@smithy/node-http-handler': 4.0.4
+ '@smithy/property-provider': 4.0.2
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
+ '@smithy/util-stream': 4.2.0
tslib: 2.8.1
'@aws-sdk/credential-provider-ini@3.741.0(aws-crt@1.25.3)':
@@ -14469,10 +14485,10 @@ snapshots:
'@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
+ '@smithy/credential-provider-imds': 4.0.2
+ '@smithy/property-provider': 4.0.2
+ '@smithy/shared-ini-file-loader': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
@@ -14486,10 +14502,10 @@ snapshots:
'@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
+ '@smithy/credential-provider-imds': 4.0.2
+ '@smithy/property-provider': 4.0.2
+ '@smithy/shared-ini-file-loader': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
@@ -14498,9 +14514,9 @@ snapshots:
dependencies:
'@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
+ '@smithy/property-provider': 4.0.2
+ '@smithy/shared-ini-file-loader': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/credential-provider-sso@3.734.0(aws-crt@1.25.3)':
@@ -14509,9 +14525,9 @@ snapshots:
'@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
+ '@smithy/property-provider': 4.0.2
+ '@smithy/shared-ini-file-loader': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
@@ -14521,8 +14537,8 @@ snapshots:
'@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
+ '@smithy/property-provider': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
@@ -14541,10 +14557,10 @@ snapshots:
'@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/core': 3.1.5
- '@smithy/credential-provider-imds': 4.0.1
- '@smithy/property-provider': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/core': 3.2.0
+ '@smithy/credential-provider-imds': 4.0.2
+ '@smithy/property-provider': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
@@ -14558,9 +14574,9 @@ snapshots:
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/node-config-provider': 4.0.2
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
'@smithy/util-config-provider': 4.0.0
tslib: 2.8.1
@@ -14568,16 +14584,16 @@ snapshots:
dependencies:
'@aws-sdk/endpoint-cache': 3.723.0
'@aws-sdk/types': 3.734.0
- '@smithy/node-config-provider': 4.0.1
- '@smithy/protocol-http': 5.0.1
- '@smithy/types': 4.1.0
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/middleware-expect-continue@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/protocol-http': 5.0.1
- '@smithy/types': 4.1.0
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/middleware-flexible-checksums@3.735.0':
@@ -14588,38 +14604,38 @@ snapshots:
'@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.1.2
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
+ '@smithy/util-middleware': 4.0.2
+ '@smithy/util-stream': 4.2.0
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
'@aws-sdk/middleware-host-header@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/protocol-http': 5.0.1
- '@smithy/types': 4.1.0
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/middleware-location-constraint@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/middleware-logger@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/middleware-recursion-detection@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/protocol-http': 5.0.1
- '@smithy/types': 4.1.0
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/middleware-sdk-s3@3.740.0':
@@ -14627,22 +14643,22 @@ snapshots:
'@aws-sdk/core': 3.734.0
'@aws-sdk/types': 3.734.0
'@aws-sdk/util-arn-parser': 3.723.0
- '@smithy/core': 3.1.5
- '@smithy/node-config-provider': 4.0.1
- '@smithy/protocol-http': 5.0.1
- '@smithy/signature-v4': 5.0.1
- '@smithy/smithy-client': 4.1.6
- '@smithy/types': 4.1.0
+ '@smithy/core': 3.2.0
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/signature-v4': 5.0.2
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
'@smithy/util-config-provider': 4.0.0
- '@smithy/util-middleware': 4.0.1
- '@smithy/util-stream': 4.1.2
+ '@smithy/util-middleware': 4.0.2
+ '@smithy/util-stream': 4.2.0
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
'@aws-sdk/middleware-ssec@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/middleware-user-agent@3.734.0':
@@ -14650,9 +14666,9 @@ snapshots:
'@aws-sdk/core': 3.734.0
'@aws-sdk/types': 3.734.0
'@aws-sdk/util-endpoints': 3.734.0
- '@smithy/core': 3.1.5
- '@smithy/protocol-http': 5.0.1
- '@smithy/types': 4.1.0
+ '@smithy/core': 3.2.0
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/nested-clients@3.734.0(aws-crt@1.25.3)':
@@ -14669,30 +14685,30 @@ snapshots:
'@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.5
- '@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.6
- '@smithy/middleware-retry': 4.0.7
- '@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.3
- '@smithy/protocol-http': 5.0.1
- '@smithy/smithy-client': 4.1.6
- '@smithy/types': 4.1.0
- '@smithy/url-parser': 4.0.1
+ '@smithy/config-resolver': 4.1.0
+ '@smithy/core': 3.2.0
+ '@smithy/fetch-http-handler': 5.0.2
+ '@smithy/hash-node': 4.0.2
+ '@smithy/invalid-dependency': 4.0.2
+ '@smithy/middleware-content-length': 4.0.2
+ '@smithy/middleware-endpoint': 4.1.0
+ '@smithy/middleware-retry': 4.1.0
+ '@smithy/middleware-serde': 4.0.3
+ '@smithy/middleware-stack': 4.0.2
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/node-http-handler': 4.0.4
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
+ '@smithy/url-parser': 4.0.2
'@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.7
- '@smithy/util-defaults-mode-node': 4.0.7
- '@smithy/util-endpoints': 3.0.1
- '@smithy/util-middleware': 4.0.1
- '@smithy/util-retry': 4.0.1
+ '@smithy/util-defaults-mode-browser': 4.0.8
+ '@smithy/util-defaults-mode-node': 4.0.8
+ '@smithy/util-endpoints': 3.0.2
+ '@smithy/util-middleware': 4.0.2
+ '@smithy/util-retry': 4.0.2
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
transitivePeerDependencies:
@@ -14701,10 +14717,10 @@ snapshots:
'@aws-sdk/region-config-resolver@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/node-config-provider': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/types': 4.2.0
'@smithy/util-config-provider': 4.0.0
- '@smithy/util-middleware': 4.0.1
+ '@smithy/util-middleware': 4.0.2
tslib: 2.8.1
'@aws-sdk/s3-presigned-post@3.741.0(aws-crt@1.25.3)':
@@ -14712,9 +14728,9 @@ snapshots:
'@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.6
- '@smithy/signature-v4': 5.0.1
- '@smithy/types': 4.1.0
+ '@smithy/middleware-endpoint': 4.1.0
+ '@smithy/signature-v4': 5.0.2
+ '@smithy/types': 4.2.0
'@smithy/util-hex-encoding': 4.0.0
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
@@ -14726,35 +14742,35 @@ snapshots:
'@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.6
- '@smithy/protocol-http': 5.0.1
- '@smithy/smithy-client': 4.1.6
- '@smithy/types': 4.1.0
+ '@smithy/middleware-endpoint': 4.1.0
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/signature-v4-multi-region@3.740.0':
dependencies:
'@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
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/signature-v4': 5.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/token-providers@3.734.0(aws-crt@1.25.3)':
dependencies:
'@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
+ '@smithy/property-provider': 4.0.2
+ '@smithy/shared-ini-file-loader': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
transitivePeerDependencies:
- aws-crt
'@aws-sdk/types@3.734.0':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/util-arn-parser@3.723.0':
@@ -14769,15 +14785,15 @@ snapshots:
'@aws-sdk/util-endpoints@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/types': 4.1.0
- '@smithy/util-endpoints': 3.0.1
+ '@smithy/types': 4.2.0
+ '@smithy/util-endpoints': 3.0.2
tslib: 2.8.1
'@aws-sdk/util-format-url@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/querystring-builder': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/querystring-builder': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@aws-sdk/util-locate-window@3.723.0':
@@ -14787,7 +14803,7 @@ snapshots:
'@aws-sdk/util-user-agent-browser@3.734.0':
dependencies:
'@aws-sdk/types': 3.734.0
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
bowser: 2.11.0
tslib: 2.8.1
@@ -14795,8 +14811,8 @@ snapshots:
dependencies:
'@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
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
optionalDependencies:
aws-crt: 1.25.3
@@ -14807,7 +14823,7 @@ snapshots:
'@aws-sdk/xml-builder@3.734.0':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@azure/abort-controller@2.1.2':
@@ -14887,8 +14903,8 @@ snapshots:
'@azure/core-tracing': 1.2.0
'@azure/core-util': 1.11.0
'@azure/logger': 1.1.4
- '@azure/msal-browser': 4.7.0
- '@azure/msal-node': 3.3.0
+ '@azure/msal-browser': 4.9.0
+ '@azure/msal-node': 3.4.1
events: 3.3.0
jws: 4.0.0
open: 10.1.0
@@ -14936,25 +14952,25 @@ snapshots:
'@azure/core-rest-pipeline': 1.19.1
'@azure/identity': 4.8.0
'@azure/logger': 1.1.4
- '@azure/storage-blob': 12.26.0
+ '@azure/storage-blob': 12.27.0
'@playwright/test': 1.49.1
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
- '@azure/msal-browser@4.7.0':
+ '@azure/msal-browser@4.9.0':
dependencies:
- '@azure/msal-common': 15.2.1
+ '@azure/msal-common': 15.4.0
- '@azure/msal-common@15.2.1': {}
+ '@azure/msal-common@15.4.0': {}
- '@azure/msal-node@3.3.0':
+ '@azure/msal-node@3.4.1':
dependencies:
- '@azure/msal-common': 15.2.1
+ '@azure/msal-common': 15.4.0
jsonwebtoken: 9.0.2
uuid: 8.3.2
- '@azure/storage-blob@12.26.0':
+ '@azure/storage-blob@12.27.0':
dependencies:
'@azure/abort-controller': 2.1.2
'@azure/core-auth': 1.9.0
@@ -14988,14 +15004,14 @@ snapshots:
dependencies:
'@ampproject/remapping': 2.3.0
'@babel/code-frame': 7.26.2
- '@babel/generator': 7.26.10
- '@babel/helper-compilation-targets': 7.26.5
+ '@babel/generator': 7.27.0
+ '@babel/helper-compilation-targets': 7.27.0
'@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
- '@babel/helpers': 7.26.10
- '@babel/parser': 7.26.10
- '@babel/template': 7.26.9
- '@babel/traverse': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/helpers': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/template': 7.27.0
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
convert-source-map: 2.0.0
debug: 4.4.0
gensync: 1.0.0-beta.2
@@ -15004,7 +15020,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/eslint-parser@7.26.10(@babel/core@7.26.0)(eslint@8.57.0)':
+ '@babel/eslint-parser@7.27.0(@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
@@ -15012,19 +15028,19 @@ snapshots:
eslint-visitor-keys: 2.1.0
semver: 6.3.1
- '@babel/generator@7.26.10':
+ '@babel/generator@7.27.0':
dependencies:
- '@babel/parser': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
'@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.10
+ '@babel/types': 7.27.0
- '@babel/helper-compilation-targets@7.26.5':
+ '@babel/helper-compilation-targets@7.27.0':
dependencies:
'@babel/compat-data': 7.26.8
'@babel/helper-validator-option': 7.25.9
@@ -15032,7 +15048,7 @@ snapshots:
lru-cache: 5.1.1
semver: 6.3.1
- '@babel/helper-create-class-features-plugin@7.26.9(@babel/core@7.26.0)':
+ '@babel/helper-create-class-features-plugin@7.27.0(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
'@babel/helper-annotate-as-pure': 7.25.9
@@ -15040,33 +15056,22 @@ snapshots:
'@babel/helper-optimise-call-expression': 7.25.9
'@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.10
+ '@babel/traverse': 7.27.0
semver: 6.3.1
transitivePeerDependencies:
- supports-color
- '@babel/helper-create-regexp-features-plugin@7.26.3(@babel/core@7.26.0)':
+ '@babel/helper-create-regexp-features-plugin@7.27.0(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
'@babel/helper-annotate-as-pure': 7.25.9
regexpu-core: 6.2.0
semver: 6.3.1
- '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.0)':
- dependencies:
- '@babel/core': 7.26.0
- '@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
- transitivePeerDependencies:
- - supports-color
-
'@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-compilation-targets': 7.26.5
+ '@babel/helper-compilation-targets': 7.27.0
'@babel/helper-plugin-utils': 7.26.5
debug: 4.4.0
lodash.debounce: 4.0.8
@@ -15076,19 +15081,19 @@ snapshots:
'@babel/helper-environment-visitor@7.24.7':
dependencies:
- '@babel/types': 7.26.10
+ '@babel/types': 7.27.0
'@babel/helper-member-expression-to-functions@7.25.9':
dependencies:
- '@babel/traverse': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
transitivePeerDependencies:
- supports-color
'@babel/helper-module-imports@7.25.9':
dependencies:
- '@babel/traverse': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
transitivePeerDependencies:
- supports-color
@@ -15097,13 +15102,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.10
+ '@babel/traverse': 7.27.0
transitivePeerDependencies:
- supports-color
'@babel/helper-optimise-call-expression@7.25.9':
dependencies:
- '@babel/types': 7.26.10
+ '@babel/types': 7.27.0
'@babel/helper-plugin-utils@7.26.5': {}
@@ -15112,7 +15117,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.10
+ '@babel/traverse': 7.27.0
transitivePeerDependencies:
- supports-color
@@ -15121,14 +15126,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.10
+ '@babel/traverse': 7.27.0
transitivePeerDependencies:
- supports-color
'@babel/helper-skip-transparent-expression-wrappers@7.25.9':
dependencies:
- '@babel/traverse': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
transitivePeerDependencies:
- supports-color
@@ -15140,16 +15145,16 @@ snapshots:
'@babel/helper-wrap-function@7.25.9':
dependencies:
- '@babel/template': 7.26.9
- '@babel/traverse': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/template': 7.27.0
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
transitivePeerDependencies:
- supports-color
- '@babel/helpers@7.26.10':
+ '@babel/helpers@7.27.0':
dependencies:
- '@babel/template': 7.26.9
- '@babel/types': 7.26.10
+ '@babel/template': 7.27.0
+ '@babel/types': 7.27.0
'@babel/highlight@7.25.9':
dependencies:
@@ -15158,15 +15163,15 @@ snapshots:
js-tokens: 4.0.0
picocolors: 1.1.1
- '@babel/parser@7.26.10':
+ '@babel/parser@7.27.0':
dependencies:
- '@babel/types': 7.26.10
+ '@babel/types': 7.27.0
'@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.10
+ '@babel/traverse': 7.27.0
transitivePeerDependencies:
- supports-color
@@ -15193,7 +15198,7 @@ snapshots:
dependencies:
'@babel/core': 7.26.0
'@babel/helper-plugin-utils': 7.26.5
- '@babel/traverse': 7.26.10
+ '@babel/traverse': 7.27.0
transitivePeerDependencies:
- supports-color
@@ -15210,7 +15215,7 @@ snapshots:
'@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-class-features-plugin': 7.26.9(@babel/core@7.26.0)
+ '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
transitivePeerDependencies:
- supports-color
@@ -15218,7 +15223,7 @@ snapshots:
'@babel/plugin-proposal-decorators@7.25.9(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-class-features-plugin': 7.26.9(@babel/core@7.26.0)
+ '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-syntax-decorators': 7.25.9(@babel/core@7.26.0)
transitivePeerDependencies:
@@ -15251,7 +15256,7 @@ snapshots:
dependencies:
'@babel/compat-data': 7.26.8
'@babel/core': 7.26.0
- '@babel/helper-compilation-targets': 7.26.5
+ '@babel/helper-compilation-targets': 7.27.0
'@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)
@@ -15388,7 +15393,7 @@ snapshots:
'@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0)
+ '@babel/helper-create-regexp-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)':
@@ -15401,7 +15406,7 @@ snapshots:
'@babel/core': 7.26.0
'@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.10
+ '@babel/traverse': 7.27.0
transitivePeerDependencies:
- supports-color
@@ -15419,7 +15424,7 @@ snapshots:
'@babel/core': 7.26.0
'@babel/helper-plugin-utils': 7.26.5
- '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)':
+ '@babel/plugin-transform-block-scoping@7.27.0(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
'@babel/helper-plugin-utils': 7.26.5
@@ -15427,7 +15432,7 @@ snapshots:
'@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.26.9(@babel/core@7.26.0)
+ '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
transitivePeerDependencies:
- supports-color
@@ -15435,7 +15440,7 @@ snapshots:
'@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-class-features-plugin': 7.26.9(@babel/core@7.26.0)
+ '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
transitivePeerDependencies:
- supports-color
@@ -15444,10 +15449,10 @@ snapshots:
dependencies:
'@babel/core': 7.26.0
'@babel/helper-annotate-as-pure': 7.25.9
- '@babel/helper-compilation-targets': 7.26.5
+ '@babel/helper-compilation-targets': 7.27.0
'@babel/helper-plugin-utils': 7.26.5
'@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0)
- '@babel/traverse': 7.26.10
+ '@babel/traverse': 7.27.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -15456,7 +15461,7 @@ snapshots:
dependencies:
'@babel/core': 7.26.0
'@babel/helper-plugin-utils': 7.26.5
- '@babel/template': 7.26.9
+ '@babel/template': 7.27.0
'@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)':
dependencies:
@@ -15466,7 +15471,7 @@ snapshots:
'@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0)
+ '@babel/helper-create-regexp-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.0)':
@@ -15477,7 +15482,7 @@ snapshots:
'@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0)
+ '@babel/helper-create-regexp-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.0)':
@@ -15512,9 +15517,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.26.5
+ '@babel/helper-compilation-targets': 7.27.0
'@babel/helper-plugin-utils': 7.26.5
- '@babel/traverse': 7.26.10
+ '@babel/traverse': 7.27.0
transitivePeerDependencies:
- supports-color
@@ -15560,7 +15565,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.10
+ '@babel/traverse': 7.27.0
transitivePeerDependencies:
- supports-color
@@ -15575,7 +15580,7 @@ snapshots:
'@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0)
+ '@babel/helper-create-regexp-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0)':
@@ -15596,7 +15601,7 @@ snapshots:
'@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.26.5
+ '@babel/helper-compilation-targets': 7.27.0
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0)
@@ -15629,7 +15634,7 @@ snapshots:
'@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.26.9(@babel/core@7.26.0)
+ '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
transitivePeerDependencies:
- supports-color
@@ -15638,7 +15643,7 @@ snapshots:
dependencies:
'@babel/core': 7.26.0
'@babel/helper-annotate-as-pure': 7.25.9
- '@babel/helper-create-class-features-plugin': 7.26.9(@babel/core@7.26.0)
+ '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
transitivePeerDependencies:
- supports-color
@@ -15677,7 +15682,7 @@ snapshots:
'@babel/helper-module-imports': 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.10
+ '@babel/types': 7.27.0
transitivePeerDependencies:
- supports-color
@@ -15687,7 +15692,7 @@ snapshots:
'@babel/helper-annotate-as-pure': 7.25.9
'@babel/helper-plugin-utils': 7.26.5
- '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0)':
+ '@babel/plugin-transform-regenerator@7.27.0(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
'@babel/helper-plugin-utils': 7.26.5
@@ -15696,7 +15701,7 @@ snapshots:
'@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0)
+ '@babel/helper-create-regexp-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.0)':
@@ -15709,9 +15714,9 @@ snapshots:
'@babel/core': 7.26.0
'@babel/helper-module-imports': 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-corejs2: 0.4.13(@babel/core@7.26.0)
babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.26.0)
- babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.0)
+ babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.26.0)
semver: 6.3.1
transitivePeerDependencies:
- supports-color
@@ -15739,16 +15744,16 @@ snapshots:
'@babel/core': 7.26.0
'@babel/helper-plugin-utils': 7.26.5
- '@babel/plugin-transform-typeof-symbol@7.26.7(@babel/core@7.26.0)':
+ '@babel/plugin-transform-typeof-symbol@7.27.0(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
'@babel/helper-plugin-utils': 7.26.5
- '@babel/plugin-transform-typescript@7.26.8(@babel/core@7.26.0)':
+ '@babel/plugin-transform-typescript@7.27.0(@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.26.9(@babel/core@7.26.0)
+ '@babel/helper-create-class-features-plugin': 7.27.0(@babel/core@7.26.0)
'@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)
@@ -15763,26 +15768,26 @@ snapshots:
'@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0)
+ '@babel/helper-create-regexp-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0)
+ '@babel/helper-create-regexp-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0)
+ '@babel/helper-create-regexp-features-plugin': 7.27.0(@babel/core@7.26.0)
'@babel/helper-plugin-utils': 7.26.5
'@babel/preset-env@7.26.9(@babel/core@7.26.0)':
dependencies:
'@babel/compat-data': 7.26.8
'@babel/core': 7.26.0
- '@babel/helper-compilation-targets': 7.26.5
+ '@babel/helper-compilation-targets': 7.27.0
'@babel/helper-plugin-utils': 7.26.5
'@babel/helper-validator-option': 7.25.9
'@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.0)
@@ -15798,7 +15803,7 @@ snapshots:
'@babel/plugin-transform-async-generator-functions': 7.26.8(@babel/core@7.26.0)
'@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-block-scoped-functions': 7.26.5(@babel/core@7.26.0)
- '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-block-scoping': 7.27.0(@babel/core@7.26.0)
'@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.0)
'@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0)
@@ -15832,14 +15837,14 @@ snapshots:
'@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.0)
- '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-regenerator': 7.27.0(@babel/core@7.26.0)
'@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.26.0)
'@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.26.0)
'@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-template-literals': 7.26.8(@babel/core@7.26.0)
- '@babel/plugin-transform-typeof-symbol': 7.26.7(@babel/core@7.26.0)
+ '@babel/plugin-transform-typeof-symbol': 7.27.0(@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)
@@ -15864,7 +15869,7 @@ snapshots:
dependencies:
'@babel/core': 7.26.0
'@babel/helper-plugin-utils': 7.26.5
- '@babel/types': 7.26.10
+ '@babel/types': 7.27.0
esutils: 2.0.3
'@babel/preset-react@7.26.3(@babel/core@7.26.0)':
@@ -15879,14 +15884,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@babel/preset-typescript@7.26.0(@babel/core@7.26.0)':
+ '@babel/preset-typescript@7.27.0(@babel/core@7.26.0)':
dependencies:
'@babel/core': 7.26.0
'@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.8(@babel/core@7.26.0)
+ '@babel/plugin-transform-typescript': 7.27.0(@babel/core@7.26.0)
transitivePeerDependencies:
- supports-color
@@ -15899,29 +15904,29 @@ snapshots:
pirates: 4.0.6
source-map-support: 0.5.21
- '@babel/runtime@7.26.10':
+ '@babel/runtime@7.27.0':
dependencies:
regenerator-runtime: 0.14.1
- '@babel/template@7.26.9':
+ '@babel/template@7.27.0':
dependencies:
'@babel/code-frame': 7.26.2
- '@babel/parser': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
- '@babel/traverse@7.26.10':
+ '@babel/traverse@7.27.0':
dependencies:
'@babel/code-frame': 7.26.2
- '@babel/generator': 7.26.10
- '@babel/parser': 7.26.10
- '@babel/template': 7.26.9
- '@babel/types': 7.26.10
+ '@babel/generator': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/template': 7.27.0
+ '@babel/types': 7.27.0
debug: 4.4.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
- '@babel/types@7.26.10':
+ '@babel/types@7.27.0':
dependencies:
'@babel/helper-string-parser': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
@@ -16557,7 +16562,7 @@ snapshots:
'@expo/cli@0.22.11(encoding@0.1.13)':
dependencies:
'@0no-co/graphql.web': 1.1.2
- '@babel/runtime': 7.26.10
+ '@babel/runtime': 7.27.0
'@expo/code-signing-certificates': 0.0.5
'@expo/config': 10.0.11
'@expo/config-plugins': 9.0.17
@@ -16742,9 +16747,9 @@ snapshots:
'@expo/metro-config@0.19.9':
dependencies:
'@babel/core': 7.26.0
- '@babel/generator': 7.26.10
- '@babel/parser': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/generator': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
'@expo/config': 10.0.11
'@expo/env': 0.4.2
'@expo/json-file': 9.0.2
@@ -16850,29 +16855,29 @@ snapshots:
'@floating-ui/utils@0.2.9': {}
- '@formatjs/ecma402-abstract@2.3.3':
+ '@formatjs/ecma402-abstract@2.3.4':
dependencies:
- '@formatjs/fast-memoize': 2.2.6
- '@formatjs/intl-localematcher': 0.6.0
+ '@formatjs/fast-memoize': 2.2.7
+ '@formatjs/intl-localematcher': 0.6.1
decimal.js: 10.5.0
tslib: 2.8.1
- '@formatjs/fast-memoize@2.2.6':
+ '@formatjs/fast-memoize@2.2.7':
dependencies:
tslib: 2.8.1
- '@formatjs/icu-messageformat-parser@2.11.1':
+ '@formatjs/icu-messageformat-parser@2.11.2':
dependencies:
- '@formatjs/ecma402-abstract': 2.3.3
- '@formatjs/icu-skeleton-parser': 1.8.13
+ '@formatjs/ecma402-abstract': 2.3.4
+ '@formatjs/icu-skeleton-parser': 1.8.14
tslib: 2.8.1
- '@formatjs/icu-skeleton-parser@1.8.13':
+ '@formatjs/icu-skeleton-parser@1.8.14':
dependencies:
- '@formatjs/ecma402-abstract': 2.3.3
+ '@formatjs/ecma402-abstract': 2.3.4
tslib: 2.8.1
- '@formatjs/intl-localematcher@0.6.0':
+ '@formatjs/intl-localematcher@0.6.1':
dependencies:
tslib: 2.8.1
@@ -17095,11 +17100,11 @@ snapshots:
'@types/yargs': 17.0.33
chalk: 4.1.2
- '@joshwooding/vite-plugin-react-docgen-typescript@0.4.2(typescript@5.8.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))':
+ '@joshwooding/vite-plugin-react-docgen-typescript@0.4.2(typescript@5.8.2)(vite@6.0.12(@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:
magic-string: 0.27.0
react-docgen-typescript: 2.2.2(typescript@5.8.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: 6.0.12(@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)
optionalDependencies:
typescript: 5.8.2
@@ -17327,14 +17332,14 @@ snapshots:
'@manypkg/find-root@1.1.0':
dependencies:
- '@babel/runtime': 7.26.10
+ '@babel/runtime': 7.27.0
'@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.10
+ '@babel/runtime': 7.27.0
'@changesets/types': 4.1.0
'@manypkg/find-root': 1.1.0
fs-extra: 8.1.0
@@ -17347,23 +17352,23 @@ snapshots:
'@types/react': 19.0.1
react: 18.3.1
- '@microsoft/api-extractor-model@7.30.4(@types/node@22.10.2)':
+ '@microsoft/api-extractor-model@7.30.5(@types/node@22.10.2)':
dependencies:
'@microsoft/tsdoc': 0.15.1
'@microsoft/tsdoc-config': 0.17.1
- '@rushstack/node-core-library': 5.12.0(@types/node@22.10.2)
+ '@rushstack/node-core-library': 5.13.0(@types/node@22.10.2)
transitivePeerDependencies:
- '@types/node'
- '@microsoft/api-extractor@7.52.1(@types/node@22.10.2)':
+ '@microsoft/api-extractor@7.52.2(@types/node@22.10.2)':
dependencies:
- '@microsoft/api-extractor-model': 7.30.4(@types/node@22.10.2)
+ '@microsoft/api-extractor-model': 7.30.5(@types/node@22.10.2)
'@microsoft/tsdoc': 0.15.1
'@microsoft/tsdoc-config': 0.17.1
- '@rushstack/node-core-library': 5.12.0(@types/node@22.10.2)
+ '@rushstack/node-core-library': 5.13.0(@types/node@22.10.2)
'@rushstack/rig-package': 0.5.3
- '@rushstack/terminal': 0.15.1(@types/node@22.10.2)
- '@rushstack/ts-command-line': 4.23.6(@types/node@22.10.2)
+ '@rushstack/terminal': 0.15.2(@types/node@22.10.2)
+ '@rushstack/ts-command-line': 4.23.7(@types/node@22.10.2)
lodash: 4.17.21
minimatch: 3.0.8
resolve: 1.22.10
@@ -17933,19 +17938,19 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
- '@pkgr/core@0.1.1': {}
+ '@pkgr/core@0.1.2': {}
'@playwright/test@1.49.1':
dependencies:
playwright: 1.49.1
- '@preact/preset-vite@2.9.3(@babel/core@7.26.0)(preact@10.25.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))':
+ '@preact/preset-vite@2.9.3(@babel/core@7.26.0)(preact@10.25.2)(vite@6.0.12(@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:
'@babel/code-frame': 7.26.2
'@babel/core': 7.26.0
'@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-react-jsx-development': 7.25.9(@babel/core@7.26.0)
- '@prefresh/vite': 2.4.7(preact@10.25.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))
+ '@prefresh/vite': 2.4.7(preact@10.25.2)(vite@6.0.12(@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': 4.2.1
babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.26.0)
debug: 4.4.0
@@ -17954,7 +17959,7 @@ snapshots:
node-html-parser: 6.1.13
source-map: 0.7.4
stack-trace: 1.0.0-pre2
- 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: 6.0.12(@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:
- preact
- supports-color
@@ -17967,7 +17972,7 @@ snapshots:
'@prefresh/utils@1.2.0': {}
- '@prefresh/vite@2.4.7(preact@10.25.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))':
+ '@prefresh/vite@2.4.7(preact@10.25.2)(vite@6.0.12(@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:
'@babel/core': 7.26.0
'@prefresh/babel-plugin': 0.5.1
@@ -17975,7 +17980,7 @@ snapshots:
'@prefresh/utils': 1.2.0
'@rollup/pluginutils': 4.2.1
preact: 10.25.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: 6.0.12(@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:
- supports-color
@@ -18942,6 +18947,10 @@ snapshots:
- supports-color
- utf-8-validate
+ '@react-native-community/netinfo@11.4.1(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@babel/core@7.26.0))(@types/react@18.3.1)(encoding@0.1.13)(react@18.3.1))':
+ dependencies:
+ react-native: 0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@babel/core@7.26.0))(@types/react@18.3.1)(encoding@0.1.13)(react@18.3.1)
+
'@react-native/assets-registry@0.74.87': {}
'@react-native/assets-registry@0.76.6': {}
@@ -18986,7 +18995,7 @@ snapshots:
'@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0)
'@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0)
- '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-block-scoping': 7.27.0(@babel/core@7.26.0)
'@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)
@@ -19006,9 +19015,9 @@ 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.8(@babel/core@7.26.0)
+ '@babel/plugin-transform-typescript': 7.27.0(@babel/core@7.26.0)
'@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0)
- '@babel/template': 7.26.9
+ '@babel/template': 7.27.0
'@react-native/babel-plugin-codegen': 0.74.87(@babel/preset-env@7.26.9(@babel/core@7.26.0))
babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.26.0)
react-refresh: 0.14.2
@@ -19027,7 +19036,7 @@ snapshots:
'@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-async-generator-functions': 7.26.8(@babel/core@7.26.0)
'@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0)
- '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-block-scoping': 7.27.0(@babel/core@7.26.0)
'@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0)
'@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)
@@ -19051,14 +19060,14 @@ snapshots:
'@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
- '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-regenerator': 7.27.0(@babel/core@7.26.0)
'@babel/plugin-transform-runtime': 7.26.10(@babel/core@7.26.0)
'@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.8(@babel/core@7.26.0)
+ '@babel/plugin-transform-typescript': 7.27.0(@babel/core@7.26.0)
'@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0)
- '@babel/template': 7.26.9
+ '@babel/template': 7.27.0
'@react-native/babel-plugin-codegen': 0.76.6(@babel/preset-env@7.26.9(@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)
@@ -19078,7 +19087,7 @@ snapshots:
'@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-async-generator-functions': 7.26.8(@babel/core@7.26.0)
'@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0)
- '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-block-scoping': 7.27.0(@babel/core@7.26.0)
'@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0)
'@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)
@@ -19102,14 +19111,14 @@ snapshots:
'@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
- '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-regenerator': 7.27.0(@babel/core@7.26.0)
'@babel/plugin-transform-runtime': 7.26.10(@babel/core@7.26.0)
'@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.8(@babel/core@7.26.0)
+ '@babel/plugin-transform-typescript': 7.27.0(@babel/core@7.26.0)
'@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0)
- '@babel/template': 7.26.9
+ '@babel/template': 7.27.0
'@react-native/babel-plugin-codegen': 0.76.7(@babel/preset-env@7.26.9(@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)
@@ -19120,7 +19129,7 @@ snapshots:
'@react-native/codegen@0.74.87(@babel/preset-env@7.26.9(@babel/core@7.26.0))':
dependencies:
- '@babel/parser': 7.26.10
+ '@babel/parser': 7.27.0
'@babel/preset-env': 7.26.9(@babel/core@7.26.0)
glob: 7.2.3
hermes-parser: 0.19.1
@@ -19133,7 +19142,7 @@ snapshots:
'@react-native/codegen@0.76.6(@babel/preset-env@7.26.9(@babel/core@7.26.0))':
dependencies:
- '@babel/parser': 7.26.10
+ '@babel/parser': 7.27.0
'@babel/preset-env': 7.26.9(@babel/core@7.26.0)
glob: 7.2.3
hermes-parser: 0.23.1
@@ -19147,7 +19156,7 @@ snapshots:
'@react-native/codegen@0.76.7(@babel/preset-env@7.26.9(@babel/core@7.26.0))':
dependencies:
- '@babel/parser': 7.26.10
+ '@babel/parser': 7.27.0
'@babel/preset-env': 7.26.9(@babel/core@7.26.0)
glob: 7.2.3
hermes-parser: 0.23.1
@@ -19188,9 +19197,9 @@ snapshots:
chalk: 4.1.2
execa: 5.1.1
invariant: 2.2.4
- metro: 0.81.3
- metro-config: 0.81.3
- metro-core: 0.81.3
+ metro: 0.81.4
+ metro-config: 0.81.4
+ metro-core: 0.81.4
node-fetch: 2.7.0(encoding@0.1.13)
readline: 1.3.0
semver: 7.7.1
@@ -19329,7 +19338,7 @@ snapshots:
'@rnx-kit/chromium-edge-launcher@1.0.0':
dependencies:
- '@types/node': 18.19.80
+ '@types/node': 18.19.83
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@@ -19350,13 +19359,13 @@ snapshots:
optionalDependencies:
rollup: 3.29.5
- '@rollup/plugin-inject@5.0.5(rollup@4.36.0)':
+ '@rollup/plugin-inject@5.0.5(rollup@4.37.0)':
dependencies:
- '@rollup/pluginutils': 5.1.4(rollup@4.36.0)
+ '@rollup/pluginutils': 5.1.4(rollup@4.37.0)
estree-walker: 2.0.2
magic-string: 0.30.17
optionalDependencies:
- rollup: 4.36.0
+ rollup: 4.37.0
'@rollup/pluginutils@4.2.1':
dependencies:
@@ -19365,82 +19374,85 @@ snapshots:
'@rollup/pluginutils@5.1.4(rollup@3.29.5)':
dependencies:
- '@types/estree': 1.0.6
+ '@types/estree': 1.0.7
estree-walker: 2.0.2
picomatch: 4.0.2
optionalDependencies:
rollup: 3.29.5
- '@rollup/pluginutils@5.1.4(rollup@4.36.0)':
+ '@rollup/pluginutils@5.1.4(rollup@4.37.0)':
dependencies:
- '@types/estree': 1.0.6
+ '@types/estree': 1.0.7
estree-walker: 2.0.2
picomatch: 4.0.2
optionalDependencies:
- rollup: 4.36.0
+ rollup: 4.37.0
- '@rollup/rollup-android-arm-eabi@4.36.0':
+ '@rollup/rollup-android-arm-eabi@4.37.0':
optional: true
- '@rollup/rollup-android-arm64@4.36.0':
+ '@rollup/rollup-android-arm64@4.37.0':
optional: true
- '@rollup/rollup-darwin-arm64@4.36.0':
+ '@rollup/rollup-darwin-arm64@4.37.0':
optional: true
- '@rollup/rollup-darwin-x64@4.36.0':
+ '@rollup/rollup-darwin-x64@4.37.0':
optional: true
- '@rollup/rollup-freebsd-arm64@4.36.0':
+ '@rollup/rollup-freebsd-arm64@4.37.0':
optional: true
- '@rollup/rollup-freebsd-x64@4.36.0':
+ '@rollup/rollup-freebsd-x64@4.37.0':
optional: true
- '@rollup/rollup-linux-arm-gnueabihf@4.36.0':
+ '@rollup/rollup-linux-arm-gnueabihf@4.37.0':
optional: true
- '@rollup/rollup-linux-arm-musleabihf@4.36.0':
+ '@rollup/rollup-linux-arm-musleabihf@4.37.0':
optional: true
- '@rollup/rollup-linux-arm64-gnu@4.36.0':
+ '@rollup/rollup-linux-arm64-gnu@4.37.0':
optional: true
- '@rollup/rollup-linux-arm64-musl@4.36.0':
+ '@rollup/rollup-linux-arm64-musl@4.37.0':
optional: true
- '@rollup/rollup-linux-loongarch64-gnu@4.36.0':
+ '@rollup/rollup-linux-loongarch64-gnu@4.37.0':
optional: true
- '@rollup/rollup-linux-powerpc64le-gnu@4.36.0':
+ '@rollup/rollup-linux-powerpc64le-gnu@4.37.0':
optional: true
- '@rollup/rollup-linux-riscv64-gnu@4.36.0':
+ '@rollup/rollup-linux-riscv64-gnu@4.37.0':
optional: true
- '@rollup/rollup-linux-s390x-gnu@4.36.0':
+ '@rollup/rollup-linux-riscv64-musl@4.37.0':
optional: true
- '@rollup/rollup-linux-x64-gnu@4.36.0':
+ '@rollup/rollup-linux-s390x-gnu@4.37.0':
optional: true
- '@rollup/rollup-linux-x64-musl@4.36.0':
+ '@rollup/rollup-linux-x64-gnu@4.37.0':
optional: true
- '@rollup/rollup-win32-arm64-msvc@4.36.0':
+ '@rollup/rollup-linux-x64-musl@4.37.0':
optional: true
- '@rollup/rollup-win32-ia32-msvc@4.36.0':
+ '@rollup/rollup-win32-arm64-msvc@4.37.0':
optional: true
- '@rollup/rollup-win32-x64-msvc@4.36.0':
+ '@rollup/rollup-win32-ia32-msvc@4.37.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.37.0':
optional: true
'@rtsao/scc@1.1.0': {}
'@rushstack/eslint-patch@1.11.0': {}
- '@rushstack/node-core-library@5.12.0(@types/node@22.10.2)':
+ '@rushstack/node-core-library@5.13.0(@types/node@22.10.2)':
dependencies:
ajv: 8.13.0
ajv-draft-04: 1.0.0(ajv@8.13.0)
@@ -19458,16 +19470,16 @@ snapshots:
resolve: 1.22.10
strip-json-comments: 3.1.1
- '@rushstack/terminal@0.15.1(@types/node@22.10.2)':
+ '@rushstack/terminal@0.15.2(@types/node@22.10.2)':
dependencies:
- '@rushstack/node-core-library': 5.12.0(@types/node@22.10.2)
+ '@rushstack/node-core-library': 5.13.0(@types/node@22.10.2)
supports-color: 8.1.1
optionalDependencies:
'@types/node': 22.10.2
- '@rushstack/ts-command-line@4.23.6(@types/node@22.10.2)':
+ '@rushstack/ts-command-line@4.23.7(@types/node@22.10.2)':
dependencies:
- '@rushstack/terminal': 0.15.1(@types/node@22.10.2)
+ '@rushstack/terminal': 0.15.2(@types/node@22.10.2)
'@types/argparse': 1.0.38
argparse: 1.0.10
string-argv: 0.3.2
@@ -19688,9 +19700,9 @@ snapshots:
dependencies:
'@sinonjs/commons': 3.0.1
- '@smithy/abort-controller@4.0.1':
+ '@smithy/abort-controller@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@smithy/chunked-blob-reader-native@4.0.0':
@@ -19702,94 +19714,94 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@smithy/config-resolver@4.0.1':
+ '@smithy/config-resolver@4.1.0':
dependencies:
- '@smithy/node-config-provider': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/types': 4.2.0
'@smithy/util-config-provider': 4.0.0
- '@smithy/util-middleware': 4.0.1
+ '@smithy/util-middleware': 4.0.2
tslib: 2.8.1
- '@smithy/core@3.1.5':
+ '@smithy/core@3.2.0':
dependencies:
- '@smithy/middleware-serde': 4.0.2
- '@smithy/protocol-http': 5.0.1
- '@smithy/types': 4.1.0
+ '@smithy/middleware-serde': 4.0.3
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
'@smithy/util-body-length-browser': 4.0.0
- '@smithy/util-middleware': 4.0.1
- '@smithy/util-stream': 4.1.2
+ '@smithy/util-middleware': 4.0.2
+ '@smithy/util-stream': 4.2.0
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@smithy/credential-provider-imds@4.0.1':
+ '@smithy/credential-provider-imds@4.0.2':
dependencies:
- '@smithy/node-config-provider': 4.0.1
- '@smithy/property-provider': 4.0.1
- '@smithy/types': 4.1.0
- '@smithy/url-parser': 4.0.1
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/property-provider': 4.0.2
+ '@smithy/types': 4.2.0
+ '@smithy/url-parser': 4.0.2
tslib: 2.8.1
- '@smithy/eventstream-codec@4.0.1':
+ '@smithy/eventstream-codec@4.0.2':
dependencies:
'@aws-crypto/crc32': 5.2.0
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
'@smithy/util-hex-encoding': 4.0.0
tslib: 2.8.1
- '@smithy/eventstream-serde-browser@4.0.1':
+ '@smithy/eventstream-serde-browser@4.0.2':
dependencies:
- '@smithy/eventstream-serde-universal': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/eventstream-serde-universal': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/eventstream-serde-config-resolver@4.0.1':
+ '@smithy/eventstream-serde-config-resolver@4.1.0':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/eventstream-serde-node@4.0.1':
+ '@smithy/eventstream-serde-node@4.0.2':
dependencies:
- '@smithy/eventstream-serde-universal': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/eventstream-serde-universal': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/eventstream-serde-universal@4.0.1':
+ '@smithy/eventstream-serde-universal@4.0.2':
dependencies:
- '@smithy/eventstream-codec': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/eventstream-codec': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/fetch-http-handler@5.0.1':
+ '@smithy/fetch-http-handler@5.0.2':
dependencies:
- '@smithy/protocol-http': 5.0.1
- '@smithy/querystring-builder': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/querystring-builder': 4.0.2
+ '@smithy/types': 4.2.0
'@smithy/util-base64': 4.0.0
tslib: 2.8.1
- '@smithy/hash-blob-browser@4.0.1':
+ '@smithy/hash-blob-browser@4.0.2':
dependencies:
'@smithy/chunked-blob-reader': 5.0.0
'@smithy/chunked-blob-reader-native': 4.0.0
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/hash-node@4.0.1':
+ '@smithy/hash-node@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
'@smithy/util-buffer-from': 4.0.0
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@smithy/hash-stream-node@4.0.1':
+ '@smithy/hash-stream-node@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@smithy/invalid-dependency@4.0.1':
+ '@smithy/invalid-dependency@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@smithy/is-array-buffer@2.2.0':
@@ -19800,125 +19812,125 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@smithy/md5-js@4.0.1':
+ '@smithy/md5-js@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@smithy/middleware-content-length@4.0.1':
+ '@smithy/middleware-content-length@4.0.2':
dependencies:
- '@smithy/protocol-http': 5.0.1
- '@smithy/types': 4.1.0
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/middleware-endpoint@4.0.6':
+ '@smithy/middleware-endpoint@4.1.0':
dependencies:
- '@smithy/core': 3.1.5
- '@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
+ '@smithy/core': 3.2.0
+ '@smithy/middleware-serde': 4.0.3
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/shared-ini-file-loader': 4.0.2
+ '@smithy/types': 4.2.0
+ '@smithy/url-parser': 4.0.2
+ '@smithy/util-middleware': 4.0.2
tslib: 2.8.1
- '@smithy/middleware-retry@4.0.7':
+ '@smithy/middleware-retry@4.1.0':
dependencies:
- '@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.6
- '@smithy/types': 4.1.0
- '@smithy/util-middleware': 4.0.1
- '@smithy/util-retry': 4.0.1
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/service-error-classification': 4.0.2
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
+ '@smithy/util-middleware': 4.0.2
+ '@smithy/util-retry': 4.0.2
tslib: 2.8.1
uuid: 9.0.1
- '@smithy/middleware-serde@4.0.2':
+ '@smithy/middleware-serde@4.0.3':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/middleware-stack@4.0.1':
+ '@smithy/middleware-stack@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/node-config-provider@4.0.1':
+ '@smithy/node-config-provider@4.0.2':
dependencies:
- '@smithy/property-provider': 4.0.1
- '@smithy/shared-ini-file-loader': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/property-provider': 4.0.2
+ '@smithy/shared-ini-file-loader': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/node-http-handler@4.0.3':
+ '@smithy/node-http-handler@4.0.4':
dependencies:
- '@smithy/abort-controller': 4.0.1
- '@smithy/protocol-http': 5.0.1
- '@smithy/querystring-builder': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/abort-controller': 4.0.2
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/querystring-builder': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/property-provider@4.0.1':
+ '@smithy/property-provider@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/protocol-http@5.0.1':
+ '@smithy/protocol-http@5.1.0':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/querystring-builder@4.0.1':
+ '@smithy/querystring-builder@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
'@smithy/util-uri-escape': 4.0.0
tslib: 2.8.1
- '@smithy/querystring-parser@4.0.1':
+ '@smithy/querystring-parser@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/service-error-classification@4.0.1':
+ '@smithy/service-error-classification@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
- '@smithy/shared-ini-file-loader@4.0.1':
+ '@smithy/shared-ini-file-loader@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/signature-v4@5.0.1':
+ '@smithy/signature-v4@5.0.2':
dependencies:
'@smithy/is-array-buffer': 4.0.0
- '@smithy/protocol-http': 5.0.1
- '@smithy/types': 4.1.0
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
'@smithy/util-hex-encoding': 4.0.0
- '@smithy/util-middleware': 4.0.1
+ '@smithy/util-middleware': 4.0.2
'@smithy/util-uri-escape': 4.0.0
'@smithy/util-utf8': 4.0.0
tslib: 2.8.1
- '@smithy/smithy-client@4.1.6':
+ '@smithy/smithy-client@4.2.0':
dependencies:
- '@smithy/core': 3.1.5
- '@smithy/middleware-endpoint': 4.0.6
- '@smithy/middleware-stack': 4.0.1
- '@smithy/protocol-http': 5.0.1
- '@smithy/types': 4.1.0
- '@smithy/util-stream': 4.1.2
+ '@smithy/core': 3.2.0
+ '@smithy/middleware-endpoint': 4.1.0
+ '@smithy/middleware-stack': 4.0.2
+ '@smithy/protocol-http': 5.1.0
+ '@smithy/types': 4.2.0
+ '@smithy/util-stream': 4.2.0
tslib: 2.8.1
- '@smithy/types@4.1.0':
+ '@smithy/types@4.2.0':
dependencies:
tslib: 2.8.1
- '@smithy/url-parser@4.0.1':
+ '@smithy/url-parser@4.0.2':
dependencies:
- '@smithy/querystring-parser': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/querystring-parser': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@smithy/util-base64@4.0.0':
@@ -19949,50 +19961,50 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@smithy/util-defaults-mode-browser@4.0.7':
+ '@smithy/util-defaults-mode-browser@4.0.8':
dependencies:
- '@smithy/property-provider': 4.0.1
- '@smithy/smithy-client': 4.1.6
- '@smithy/types': 4.1.0
+ '@smithy/property-provider': 4.0.2
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
bowser: 2.11.0
tslib: 2.8.1
- '@smithy/util-defaults-mode-node@4.0.7':
+ '@smithy/util-defaults-mode-node@4.0.8':
dependencies:
- '@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.6
- '@smithy/types': 4.1.0
+ '@smithy/config-resolver': 4.1.0
+ '@smithy/credential-provider-imds': 4.0.2
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/property-provider': 4.0.2
+ '@smithy/smithy-client': 4.2.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/util-endpoints@3.0.1':
+ '@smithy/util-endpoints@3.0.2':
dependencies:
- '@smithy/node-config-provider': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/node-config-provider': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@smithy/util-hex-encoding@4.0.0':
dependencies:
tslib: 2.8.1
- '@smithy/util-middleware@4.0.1':
+ '@smithy/util-middleware@4.0.2':
dependencies:
- '@smithy/types': 4.1.0
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/util-retry@4.0.1':
+ '@smithy/util-retry@4.0.2':
dependencies:
- '@smithy/service-error-classification': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/service-error-classification': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
- '@smithy/util-stream@4.1.2':
+ '@smithy/util-stream@4.2.0':
dependencies:
- '@smithy/fetch-http-handler': 5.0.1
- '@smithy/node-http-handler': 4.0.3
- '@smithy/types': 4.1.0
+ '@smithy/fetch-http-handler': 5.0.2
+ '@smithy/node-http-handler': 4.0.4
+ '@smithy/types': 4.2.0
'@smithy/util-base64': 4.0.0
'@smithy/util-buffer-from': 4.0.0
'@smithy/util-hex-encoding': 4.0.0
@@ -20013,10 +20025,10 @@ snapshots:
'@smithy/util-buffer-from': 4.0.0
tslib: 2.8.1
- '@smithy/util-waiter@4.0.2':
+ '@smithy/util-waiter@4.0.3':
dependencies:
- '@smithy/abort-controller': 4.0.1
- '@smithy/types': 4.1.0
+ '@smithy/abort-controller': 4.0.2
+ '@smithy/types': 4.2.0
tslib: 2.8.1
'@sqltools/formatter@1.2.5': {}
@@ -20150,13 +20162,13 @@ snapshots:
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
- '@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/builder-vite@8.4.7(storybook@8.4.7(prettier@3.4.2))(vite@6.0.12(@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:
'@storybook/csf-plugin': 8.4.7(storybook@8.4.7(prettier@3.4.2))
browser-assert: 1.2.1
storybook: 8.4.7(prettier@3.4.2)
ts-dedent: 2.2.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: 6.0.12(@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/components@8.4.7(storybook@8.4.7(prettier@3.4.2))':
dependencies:
@@ -20229,11 +20241,11 @@ 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.36.0)(storybook@8.4.7(prettier@3.4.2))(typescript@5.8.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.37.0)(storybook@8.4.7(prettier@3.4.2))(typescript@5.8.2)(vite@6.0.12(@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.8.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.36.0)
- '@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))
+ '@joshwooding/vite-plugin-react-docgen-typescript': 0.4.2(typescript@5.8.2)(vite@6.0.12(@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.37.0)
+ '@storybook/builder-vite': 8.4.7(storybook@8.4.7(prettier@3.4.2))(vite@6.0.12(@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.8.2)
find-up: 5.0.0
magic-string: 0.30.17
@@ -20243,7 +20255,7 @@ snapshots:
resolve: 1.22.10
storybook: 8.4.7(prettier@3.4.2)
tsconfig-paths: 4.2.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: 6.0.12(@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:
- '@storybook/test'
- rollup
@@ -20327,7 +20339,7 @@ snapshots:
'@testing-library/dom@10.4.0':
dependencies:
'@babel/code-frame': 7.26.2
- '@babel/runtime': 7.26.10
+ '@babel/runtime': 7.27.0
'@types/aria-query': 5.0.4
aria-query: 5.3.0
chalk: 4.1.2
@@ -20357,7 +20369,7 @@ snapshots:
'@testing-library/react@16.2.0(@testing-library/dom@10.4.0)(@types/react-dom@19.0.2(@types/react@19.0.1))(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
- '@babel/runtime': 7.26.10
+ '@babel/runtime': 7.27.0
'@testing-library/dom': 10.4.0
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -20406,10 +20418,10 @@ snapshots:
'@trivago/prettier-plugin-sort-imports@5.2.0(prettier@3.4.2)':
dependencies:
- '@babel/generator': 7.26.10
- '@babel/parser': 7.26.10
- '@babel/traverse': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/generator': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
javascript-natural-sort: 0.7.1
lodash: 4.17.21
prettier: 3.4.2
@@ -20435,24 +20447,24 @@ snapshots:
'@types/babel__core@7.20.5':
dependencies:
- '@babel/parser': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
'@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.10
+ '@babel/types': 7.27.0
'@types/babel__template@7.4.4':
dependencies:
- '@babel/parser': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
'@types/babel__traverse@7.20.6':
dependencies:
- '@babel/types': 7.26.10
+ '@babel/types': 7.27.0
'@types/bcryptjs@2.4.6': {}
@@ -20479,19 +20491,21 @@ snapshots:
'@types/eslint-scope@3.7.7':
dependencies:
'@types/eslint': 9.6.1
- '@types/estree': 1.0.6
+ '@types/estree': 1.0.7
'@types/eslint@9.6.1':
dependencies:
- '@types/estree': 1.0.6
+ '@types/estree': 1.0.7
'@types/json-schema': 7.0.15
'@types/estree-jsx@1.0.5':
dependencies:
- '@types/estree': 1.0.6
+ '@types/estree': 1.0.7
'@types/estree@1.0.6': {}
+ '@types/estree@1.0.7': {}
+
'@types/graceful-fs@4.1.9':
dependencies:
'@types/node': 22.10.2
@@ -20559,7 +20573,7 @@ snapshots:
'@types/node@12.20.55': {}
- '@types/node@18.19.80':
+ '@types/node@18.19.83':
dependencies:
undici-types: 5.26.5
@@ -20746,10 +20760,10 @@ snapshots:
'@typescript-eslint/types': 8.18.0
'@typescript-eslint/visitor-keys': 8.18.0
- '@typescript-eslint/scope-manager@8.26.1':
+ '@typescript-eslint/scope-manager@8.28.0':
dependencies:
- '@typescript-eslint/types': 8.26.1
- '@typescript-eslint/visitor-keys': 8.26.1
+ '@typescript-eslint/types': 8.28.0
+ '@typescript-eslint/visitor-keys': 8.28.0
'@typescript-eslint/type-utils@7.18.0(eslint@8.57.0)(typescript@5.8.2)':
dependencies:
@@ -20780,7 +20794,7 @@ snapshots:
'@typescript-eslint/types@8.18.0': {}
- '@typescript-eslint/types@8.26.1': {}
+ '@typescript-eslint/types@8.28.0': {}
'@typescript-eslint/typescript-estree@5.62.0(typescript@5.8.2)':
dependencies:
@@ -20825,16 +20839,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/typescript-estree@8.26.1(typescript@5.8.2)':
+ '@typescript-eslint/typescript-estree@8.28.0(typescript@5.8.2)':
dependencies:
- '@typescript-eslint/types': 8.26.1
- '@typescript-eslint/visitor-keys': 8.26.1
+ '@typescript-eslint/types': 8.28.0
+ '@typescript-eslint/visitor-keys': 8.28.0
debug: 4.4.0
fast-glob: 3.3.3
is-glob: 4.0.3
minimatch: 9.0.5
semver: 7.7.1
- ts-api-utils: 2.0.1(typescript@5.8.2)
+ ts-api-utils: 2.1.0(typescript@5.8.2)
typescript: 5.8.2
transitivePeerDependencies:
- supports-color
@@ -20876,12 +20890,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.26.1(eslint@8.57.0)(typescript@5.8.2)':
+ '@typescript-eslint/utils@8.28.0(eslint@8.57.0)(typescript@5.8.2)':
dependencies:
'@eslint-community/eslint-utils': 4.5.1(eslint@8.57.0)
- '@typescript-eslint/scope-manager': 8.26.1
- '@typescript-eslint/types': 8.26.1
- '@typescript-eslint/typescript-estree': 8.26.1(typescript@5.8.2)
+ '@typescript-eslint/scope-manager': 8.28.0
+ '@typescript-eslint/types': 8.28.0
+ '@typescript-eslint/typescript-estree': 8.28.0(typescript@5.8.2)
eslint: 8.57.0
typescript: 5.8.2
transitivePeerDependencies:
@@ -20902,9 +20916,9 @@ snapshots:
'@typescript-eslint/types': 8.18.0
eslint-visitor-keys: 4.2.0
- '@typescript-eslint/visitor-keys@8.26.1':
+ '@typescript-eslint/visitor-keys@8.28.0':
dependencies:
- '@typescript-eslint/types': 8.26.1
+ '@typescript-eslint/types': 8.28.0
eslint-visitor-keys: 4.2.0
'@ungap/structured-clone@1.3.0': {}
@@ -20926,39 +20940,51 @@ snapshots:
'@unkey/error': 0.2.0
zod: 3.24.1
- '@unrs/rspack-resolver-binding-darwin-arm64@1.2.1':
+ '@unrs/rspack-resolver-binding-darwin-arm64@1.3.0':
optional: true
- '@unrs/rspack-resolver-binding-darwin-x64@1.2.1':
+ '@unrs/rspack-resolver-binding-darwin-x64@1.3.0':
optional: true
- '@unrs/rspack-resolver-binding-freebsd-x64@1.2.1':
+ '@unrs/rspack-resolver-binding-freebsd-x64@1.3.0':
optional: true
- '@unrs/rspack-resolver-binding-linux-arm-gnueabihf@1.2.1':
+ '@unrs/rspack-resolver-binding-linux-arm-gnueabihf@1.3.0':
optional: true
- '@unrs/rspack-resolver-binding-linux-arm64-gnu@1.2.1':
+ '@unrs/rspack-resolver-binding-linux-arm-musleabihf@1.3.0':
optional: true
- '@unrs/rspack-resolver-binding-linux-arm64-musl@1.2.1':
+ '@unrs/rspack-resolver-binding-linux-arm64-gnu@1.3.0':
optional: true
- '@unrs/rspack-resolver-binding-linux-x64-gnu@1.2.1':
+ '@unrs/rspack-resolver-binding-linux-arm64-musl@1.3.0':
optional: true
- '@unrs/rspack-resolver-binding-linux-x64-musl@1.2.1':
+ '@unrs/rspack-resolver-binding-linux-ppc64-gnu@1.3.0':
optional: true
- '@unrs/rspack-resolver-binding-wasm32-wasi@1.2.1':
+ '@unrs/rspack-resolver-binding-linux-s390x-gnu@1.3.0':
+ optional: true
+
+ '@unrs/rspack-resolver-binding-linux-x64-gnu@1.3.0':
+ optional: true
+
+ '@unrs/rspack-resolver-binding-linux-x64-musl@1.3.0':
+ optional: true
+
+ '@unrs/rspack-resolver-binding-wasm32-wasi@1.3.0':
dependencies:
'@napi-rs/wasm-runtime': 0.2.7
optional: true
- '@unrs/rspack-resolver-binding-win32-arm64-msvc@1.2.1':
+ '@unrs/rspack-resolver-binding-win32-arm64-msvc@1.3.0':
optional: true
- '@unrs/rspack-resolver-binding-win32-x64-msvc@1.2.1':
+ '@unrs/rspack-resolver-binding-win32-ia32-msvc@1.3.0':
+ optional: true
+
+ '@unrs/rspack-resolver-binding-win32-x64-msvc@1.3.0':
optional: true
'@urql/core@5.1.1':
@@ -21001,7 +21027,7 @@ snapshots:
'@vercel/style-guide@6.0.0(@next/eslint-plugin-next@15.1.0)(eslint@8.57.0)(prettier@3.4.2)(typescript@5.8.2)(vitest@3.0.7(@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.26.10(@babel/core@7.26.0)(eslint@8.57.0)
+ '@babel/eslint-parser': 7.27.0(@babel/core@7.26.0)(eslint@8.57.0)
'@rushstack/eslint-patch': 1.11.0
'@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.8.2))(eslint@8.57.0)(typescript@5.8.2)
'@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.8.2)
@@ -21032,14 +21058,14 @@ snapshots:
- supports-color
- vitest
- '@vitejs/plugin-react@4.3.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))':
+ '@vitejs/plugin-react@4.3.4(vite@6.0.12(@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:
'@babel/core': 7.26.0
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
'@types/babel__core': 7.20.5
react-refresh: 0.14.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: 6.0.12(@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:
- supports-color
@@ -21132,37 +21158,37 @@ snapshots:
chai: 5.2.0
tinyrainbow: 2.0.0
- '@vitest/mocker@2.1.9(vite@5.4.14(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0))':
+ '@vitest/mocker@2.1.9(vite@5.4.15(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0))':
dependencies:
'@vitest/spy': 2.1.9
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
- vite: 5.4.14(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0)
+ vite: 5.4.15(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0)
- '@vitest/mocker@3.0.5(vite@6.2.0(@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.2.3(@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.2.0(@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: 6.2.3(@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.6(vite@6.2.0(@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.6(vite@6.2.3(@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.6
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
- vite: 6.2.0(@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: 6.2.3(@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.7(vite@6.2.0(@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.7(vite@6.2.3(@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.7
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
- vite: 6.2.0(@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: 6.2.3(@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:
@@ -21297,7 +21323,7 @@ snapshots:
'@vue/compiler-core@3.5.13':
dependencies:
- '@babel/parser': 7.26.10
+ '@babel/parser': 7.27.0
'@vue/shared': 3.5.13
entities: 4.5.0
estree-walker: 2.0.2
@@ -21765,7 +21791,7 @@ snapshots:
autoprefixer@10.4.20(postcss@8.4.49):
dependencies:
browserslist: 4.24.4
- caniuse-lite: 1.0.30001706
+ caniuse-lite: 1.0.30001707
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.1.1
@@ -21780,7 +21806,7 @@ snapshots:
dependencies:
'@aws-sdk/util-utf8-browser': 3.259.0
'@httptoolkit/websocket-stream': 6.0.1
- axios: 1.8.3
+ axios: 1.8.4
buffer: 6.0.3
crypto-js: 4.2.0
mqtt: 4.3.8
@@ -21803,7 +21829,7 @@ snapshots:
transitivePeerDependencies:
- debug
- axios@1.8.3:
+ axios@1.8.4:
dependencies:
follow-redirects: 1.15.9
form-data: 4.0.2
@@ -21842,20 +21868,11 @@ snapshots:
babel-plugin-jest-hoist@29.6.3:
dependencies:
- '@babel/template': 7.26.9
- '@babel/types': 7.26.10
+ '@babel/template': 7.27.0
+ '@babel/types': 7.27.0
'@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.8
- '@babel/core': 7.26.0
- '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0)
- semver: 6.3.1
- transitivePeerDependencies:
- - supports-color
-
babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.26.0):
dependencies:
'@babel/compat-data': 7.26.8
@@ -21868,18 +21885,11 @@ snapshots:
babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.26.0):
dependencies:
'@babel/core': 7.26.0
- '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0)
+ '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.26.0)
core-js-compat: 3.41.0
transitivePeerDependencies:
- supports-color
- babel-plugin-polyfill-regenerator@0.6.3(@babel/core@7.26.0):
- dependencies:
- '@babel/core': 7.26.0
- '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0)
- transitivePeerDependencies:
- - supports-color
-
babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.26.0):
dependencies:
'@babel/core': 7.26.0
@@ -21933,7 +21943,7 @@ snapshots:
'@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.0)
'@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)
+ '@babel/preset-typescript': 7.27.0(@babel/core@7.26.0)
'@react-native/babel-preset': 0.76.7(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@babel/core@7.26.0))
babel-plugin-react-native-web: 0.19.13
react-refresh: 0.14.2
@@ -22098,8 +22108,8 @@ snapshots:
browserslist@4.24.4:
dependencies:
- caniuse-lite: 1.0.30001706
- electron-to-chromium: 1.5.120
+ caniuse-lite: 1.0.30001707
+ electron-to-chromium: 1.5.123
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.24.4)
@@ -22237,7 +22247,7 @@ snapshots:
camelize@1.0.1: {}
- caniuse-lite@1.0.30001706: {}
+ caniuse-lite@1.0.30001707: {}
ccount@2.0.1: {}
@@ -23017,7 +23027,7 @@ snapshots:
ee-first@1.1.1: {}
- electron-to-chromium@1.5.120: {}
+ electron-to-chromium@1.5.123: {}
elliptic@6.6.1:
dependencies:
@@ -23361,7 +23371,7 @@ snapshots:
eslint: 8.57.0
get-tsconfig: 4.10.0
is-bun-module: 1.3.0
- rspack-resolver: 1.2.1
+ rspack-resolver: 1.3.0
stable-hash: 0.0.5
tinyglobby: 0.2.12
optionalDependencies:
@@ -23399,7 +23409,7 @@ snapshots:
eslint-plugin-i18n-json@4.0.0(eslint@8.57.0):
dependencies:
- '@formatjs/icu-messageformat-parser': 2.11.1
+ '@formatjs/icu-messageformat-parser': 2.11.2
chalk: 2.4.2
eslint: 8.57.0
indent-string: 3.2.0
@@ -23544,7 +23554,7 @@ snapshots:
eslint-plugin-storybook@0.11.1(eslint@8.57.0)(typescript@5.8.2):
dependencies:
'@storybook/csf': 0.1.13
- '@typescript-eslint/utils': 8.26.1(eslint@8.57.0)(typescript@5.8.2)
+ '@typescript-eslint/utils': 8.28.0(eslint@8.57.0)(typescript@5.8.2)
eslint: 8.57.0
ts-dedent: 2.2.0
transitivePeerDependencies:
@@ -23687,7 +23697,7 @@ snapshots:
estree-walker@3.0.3:
dependencies:
- '@types/estree': 1.0.6
+ '@types/estree': 1.0.7
esutils@2.0.3: {}
@@ -23746,7 +23756,7 @@ snapshots:
expect-type@1.2.0: {}
- expo-asset@11.0.4(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.9(@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-asset@11.0.5(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.9(@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.5
expo: 52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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)
@@ -23767,7 +23777,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- expo-file-system@18.0.11(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.9(@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.12(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.9(@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.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.9(@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)
@@ -23806,7 +23816,7 @@ snapshots:
expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.10
+ '@babel/runtime': 7.27.0
'@expo/cli': 0.22.11(encoding@0.1.13)
'@expo/config': 10.0.11
'@expo/config-plugins': 9.0.17
@@ -23814,9 +23824,9 @@ snapshots:
'@expo/metro-config': 0.19.9
'@expo/vector-icons': 14.0.4
babel-preset-expo: 12.0.9(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@babel/core@7.26.0))
- expo-asset: 11.0.4(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.9(@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-asset: 11.0.5(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.9(@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.8(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.9(@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.11(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.9(@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.12(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.9(@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.4(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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.3(expo@52.0.28(@babel/core@7.26.0)(@babel/preset-env@7.26.9(@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.9(@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.9(@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
@@ -24415,7 +24425,7 @@ snapshots:
hast-util-to-jsx-runtime@2.3.6:
dependencies:
- '@types/estree': 1.0.6
+ '@types/estree': 1.0.7
'@types/hast': 3.0.4
'@types/unist': 3.0.3
comma-separated-tokens: 2.0.3
@@ -24842,7 +24852,7 @@ snapshots:
is-reference@1.2.1:
dependencies:
- '@types/estree': 1.0.6
+ '@types/estree': 1.0.7
is-regex@1.2.1:
dependencies:
@@ -24942,7 +24952,7 @@ snapshots:
istanbul-lib-instrument@5.2.1:
dependencies:
'@babel/core': 7.26.0
- '@babel/parser': 7.26.10
+ '@babel/parser': 7.27.0
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
semver: 6.3.1
@@ -25131,14 +25141,14 @@ snapshots:
jscodeshift@0.14.0(@babel/preset-env@7.26.9(@babel/core@7.26.0)):
dependencies:
'@babel/core': 7.26.0
- '@babel/parser': 7.26.10
+ '@babel/parser': 7.27.0
'@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.9(@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/preset-typescript': 7.27.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
@@ -25322,7 +25332,7 @@ snapshots:
lexical@0.21.0: {}
- lib0@0.2.99:
+ lib0@0.2.101:
dependencies:
isomorphic.js: 0.2.5
@@ -25562,8 +25572,8 @@ snapshots:
magicast@0.3.5:
dependencies:
- '@babel/parser': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
source-map-js: 1.2.1
make-dir@2.1.0:
@@ -25775,7 +25785,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- metro-babel-transformer@0.81.3:
+ metro-babel-transformer@0.81.4:
dependencies:
'@babel/core': 7.26.0
flow-enums-runtime: 0.0.6
@@ -25788,7 +25798,7 @@ snapshots:
dependencies:
flow-enums-runtime: 0.0.6
- metro-cache-key@0.81.3:
+ metro-cache-key@0.81.4:
dependencies:
flow-enums-runtime: 0.0.6
@@ -25798,11 +25808,11 @@ snapshots:
flow-enums-runtime: 0.0.6
metro-core: 0.80.12
- metro-cache@0.81.3:
+ metro-cache@0.81.4:
dependencies:
exponential-backoff: 3.1.2
flow-enums-runtime: 0.0.6
- metro-core: 0.81.3
+ metro-core: 0.81.4
metro-config@0.80.12:
dependencies:
@@ -25819,16 +25829,16 @@ snapshots:
- supports-color
- utf-8-validate
- metro-config@0.81.3:
+ metro-config@0.81.4:
dependencies:
connect: 3.7.0
cosmiconfig: 5.2.1
flow-enums-runtime: 0.0.6
jest-validate: 29.7.0
- metro: 0.81.3
- metro-cache: 0.81.3
- metro-core: 0.81.3
- metro-runtime: 0.81.3
+ metro: 0.81.4
+ metro-cache: 0.81.4
+ metro-core: 0.81.4
+ metro-runtime: 0.81.4
transitivePeerDependencies:
- bufferutil
- supports-color
@@ -25840,11 +25850,11 @@ snapshots:
lodash.throttle: 4.1.1
metro-resolver: 0.80.12
- metro-core@0.81.3:
+ metro-core@0.81.4:
dependencies:
flow-enums-runtime: 0.0.6
lodash.throttle: 4.1.1
- metro-resolver: 0.81.3
+ metro-resolver: 0.81.4
metro-file-map@0.80.12:
dependencies:
@@ -25864,7 +25874,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- metro-file-map@0.81.3:
+ metro-file-map@0.81.4:
dependencies:
debug: 2.6.9
fb-watchman: 2.0.2
@@ -25883,7 +25893,7 @@ snapshots:
flow-enums-runtime: 0.0.6
terser: 5.37.0
- metro-minify-terser@0.81.3:
+ metro-minify-terser@0.81.4:
dependencies:
flow-enums-runtime: 0.0.6
terser: 5.37.0
@@ -25892,24 +25902,24 @@ snapshots:
dependencies:
flow-enums-runtime: 0.0.6
- metro-resolver@0.81.3:
+ metro-resolver@0.81.4:
dependencies:
flow-enums-runtime: 0.0.6
metro-runtime@0.80.12:
dependencies:
- '@babel/runtime': 7.26.10
+ '@babel/runtime': 7.27.0
flow-enums-runtime: 0.0.6
- metro-runtime@0.81.3:
+ metro-runtime@0.81.4:
dependencies:
- '@babel/runtime': 7.26.10
+ '@babel/runtime': 7.27.0
flow-enums-runtime: 0.0.6
metro-source-map@0.80.12:
dependencies:
- '@babel/traverse': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
flow-enums-runtime: 0.0.6
invariant: 2.2.4
metro-symbolicate: 0.80.12
@@ -25920,16 +25930,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
- metro-source-map@0.81.3:
+ metro-source-map@0.81.4:
dependencies:
- '@babel/traverse': 7.26.10
- '@babel/traverse--for-generate-function-map': '@babel/traverse@7.26.10'
- '@babel/types': 7.26.10
+ '@babel/traverse': 7.27.0
+ '@babel/traverse--for-generate-function-map': '@babel/traverse@7.27.0'
+ '@babel/types': 7.27.0
flow-enums-runtime: 0.0.6
invariant: 2.2.4
- metro-symbolicate: 0.81.3
+ metro-symbolicate: 0.81.4
nullthrows: 1.1.1
- ob1: 0.81.3
+ ob1: 0.81.4
source-map: 0.5.7
vlq: 1.0.1
transitivePeerDependencies:
@@ -25947,11 +25957,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- metro-symbolicate@0.81.3:
+ metro-symbolicate@0.81.4:
dependencies:
flow-enums-runtime: 0.0.6
invariant: 2.2.4
- metro-source-map: 0.81.3
+ metro-source-map: 0.81.4
nullthrows: 1.1.1
source-map: 0.5.7
vlq: 1.0.1
@@ -25961,20 +25971,20 @@ snapshots:
metro-transform-plugins@0.80.12:
dependencies:
'@babel/core': 7.26.0
- '@babel/generator': 7.26.10
- '@babel/template': 7.26.9
- '@babel/traverse': 7.26.10
+ '@babel/generator': 7.27.0
+ '@babel/template': 7.27.0
+ '@babel/traverse': 7.27.0
flow-enums-runtime: 0.0.6
nullthrows: 1.1.1
transitivePeerDependencies:
- supports-color
- metro-transform-plugins@0.81.3:
+ metro-transform-plugins@0.81.4:
dependencies:
'@babel/core': 7.26.0
- '@babel/generator': 7.26.10
- '@babel/template': 7.26.9
- '@babel/traverse': 7.26.10
+ '@babel/generator': 7.27.0
+ '@babel/template': 7.27.0
+ '@babel/traverse': 7.27.0
flow-enums-runtime: 0.0.6
nullthrows: 1.1.1
transitivePeerDependencies:
@@ -25983,9 +25993,9 @@ snapshots:
metro-transform-worker@0.80.12:
dependencies:
'@babel/core': 7.26.0
- '@babel/generator': 7.26.10
- '@babel/parser': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/generator': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
flow-enums-runtime: 0.0.6
metro: 0.80.12
metro-babel-transformer: 0.80.12
@@ -26000,20 +26010,20 @@ snapshots:
- supports-color
- utf-8-validate
- metro-transform-worker@0.81.3:
+ metro-transform-worker@0.81.4:
dependencies:
'@babel/core': 7.26.0
- '@babel/generator': 7.26.10
- '@babel/parser': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/generator': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/types': 7.27.0
flow-enums-runtime: 0.0.6
- metro: 0.81.3
- metro-babel-transformer: 0.81.3
- metro-cache: 0.81.3
- metro-cache-key: 0.81.3
- metro-minify-terser: 0.81.3
- metro-source-map: 0.81.3
- metro-transform-plugins: 0.81.3
+ metro: 0.81.4
+ metro-babel-transformer: 0.81.4
+ metro-cache: 0.81.4
+ metro-cache-key: 0.81.4
+ metro-minify-terser: 0.81.4
+ metro-source-map: 0.81.4
+ metro-transform-plugins: 0.81.4
nullthrows: 1.1.1
transitivePeerDependencies:
- bufferutil
@@ -26024,11 +26034,11 @@ snapshots:
dependencies:
'@babel/code-frame': 7.26.2
'@babel/core': 7.26.0
- '@babel/generator': 7.26.10
- '@babel/parser': 7.26.10
- '@babel/template': 7.26.9
- '@babel/traverse': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/generator': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/template': 7.27.0
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
accepts: 1.3.8
chalk: 4.1.2
ci-info: 2.0.0
@@ -26069,15 +26079,15 @@ snapshots:
- supports-color
- utf-8-validate
- metro@0.81.3:
+ metro@0.81.4:
dependencies:
'@babel/code-frame': 7.26.2
'@babel/core': 7.26.0
- '@babel/generator': 7.26.10
- '@babel/parser': 7.26.10
- '@babel/template': 7.26.9
- '@babel/traverse': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/generator': 7.27.0
+ '@babel/parser': 7.27.0
+ '@babel/template': 7.27.0
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
accepts: 1.3.8
chalk: 4.1.2
ci-info: 2.0.0
@@ -26092,18 +26102,18 @@ snapshots:
jest-worker: 29.7.0
jsc-safe-url: 0.2.4
lodash.throttle: 4.1.1
- metro-babel-transformer: 0.81.3
- metro-cache: 0.81.3
- metro-cache-key: 0.81.3
- metro-config: 0.81.3
- metro-core: 0.81.3
- metro-file-map: 0.81.3
- metro-resolver: 0.81.3
- metro-runtime: 0.81.3
- metro-source-map: 0.81.3
- metro-symbolicate: 0.81.3
- metro-transform-plugins: 0.81.3
- metro-transform-worker: 0.81.3
+ metro-babel-transformer: 0.81.4
+ metro-cache: 0.81.4
+ metro-cache-key: 0.81.4
+ metro-config: 0.81.4
+ metro-core: 0.81.4
+ metro-file-map: 0.81.4
+ metro-resolver: 0.81.4
+ metro-runtime: 0.81.4
+ metro-source-map: 0.81.4
+ metro-symbolicate: 0.81.4
+ metro-transform-plugins: 0.81.4
+ metro-transform-worker: 0.81.4
mime-types: 2.1.35
nullthrows: 1.1.1
serialize-error: 2.1.0
@@ -26510,8 +26520,6 @@ snapshots:
stacktrace-js: 2.0.2
stylis: 4.3.6
- nanoid@3.3.10: {}
-
nanoid@3.3.11: {}
nanoid@5.0.9: {}
@@ -26534,7 +26542,7 @@ snapshots:
next-auth@4.24.11(next@15.2.3(@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.10
+ '@babel/runtime': 7.27.0
'@panva/hkdf': 1.2.1
cookie: 0.7.2
jose: 4.15.9
@@ -26563,7 +26571,7 @@ snapshots:
'@swc/counter': 0.1.3
'@swc/helpers': 0.5.15
busboy: 1.6.0
- caniuse-lite: 1.0.30001706
+ caniuse-lite: 1.0.30001707
postcss: 8.4.31
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -26767,7 +26775,7 @@ snapshots:
dependencies:
flow-enums-runtime: 0.0.6
- ob1@0.81.3:
+ ob1@0.81.4:
dependencies:
flow-enums-runtime: 0.0.6
@@ -26995,7 +27003,7 @@ snapshots:
package-manager-detector@0.2.11:
dependencies:
- quansync: 0.2.8
+ quansync: 0.2.10
pako@0.2.9: {}
@@ -27256,7 +27264,7 @@ snapshots:
polished@4.3.1:
dependencies:
- '@babel/runtime': 7.26.10
+ '@babel/runtime': 7.27.0
possible-typed-array-names@1.1.0: {}
@@ -27314,13 +27322,13 @@ snapshots:
postcss@8.4.49:
dependencies:
- nanoid: 3.3.10
+ nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
postcss@8.5.3:
dependencies:
- nanoid: 3.3.10
+ nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
@@ -27343,7 +27351,7 @@ snapshots:
posthog-node@4.4.1:
dependencies:
- axios: 1.8.3
+ axios: 1.8.4
transitivePeerDependencies:
- debug
@@ -27542,7 +27550,7 @@ snapshots:
dependencies:
side-channel: 1.1.0
- quansync@0.2.8: {}
+ quansync@0.2.10: {}
querystring-es3@0.2.1: {}
@@ -27634,8 +27642,8 @@ snapshots:
react-docgen@7.1.1:
dependencies:
'@babel/core': 7.26.0
- '@babel/traverse': 7.26.10
- '@babel/types': 7.26.10
+ '@babel/traverse': 7.27.0
+ '@babel/types': 7.27.0
'@types/babel__core': 7.20.5
'@types/babel__traverse': 7.20.6
'@types/doctrine': 0.0.9
@@ -27659,7 +27667,7 @@ snapshots:
react-error-boundary@3.1.4(react@19.0.0):
dependencies:
- '@babel/runtime': 7.26.10
+ '@babel/runtime': 7.27.0
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):
@@ -27799,8 +27807,8 @@ snapshots:
jest-environment-node: 29.7.0
jsc-android: 250231.0.0
memoize-one: 5.2.1
- metro-runtime: 0.81.3
- metro-source-map: 0.81.3
+ metro-runtime: 0.81.4
+ metro-source-map: 0.81.4
mkdirp: 0.5.6
nullthrows: 1.1.1
pretty-format: 29.7.0
@@ -28022,7 +28030,7 @@ snapshots:
regenerator-transform@0.15.2:
dependencies:
- '@babel/runtime': 7.26.10
+ '@babel/runtime': 7.27.0
regexp-tree@0.1.27: {}
@@ -28199,52 +28207,57 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
- rollup@4.36.0:
+ rollup@4.37.0:
dependencies:
'@types/estree': 1.0.6
optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.36.0
- '@rollup/rollup-android-arm64': 4.36.0
- '@rollup/rollup-darwin-arm64': 4.36.0
- '@rollup/rollup-darwin-x64': 4.36.0
- '@rollup/rollup-freebsd-arm64': 4.36.0
- '@rollup/rollup-freebsd-x64': 4.36.0
- '@rollup/rollup-linux-arm-gnueabihf': 4.36.0
- '@rollup/rollup-linux-arm-musleabihf': 4.36.0
- '@rollup/rollup-linux-arm64-gnu': 4.36.0
- '@rollup/rollup-linux-arm64-musl': 4.36.0
- '@rollup/rollup-linux-loongarch64-gnu': 4.36.0
- '@rollup/rollup-linux-powerpc64le-gnu': 4.36.0
- '@rollup/rollup-linux-riscv64-gnu': 4.36.0
- '@rollup/rollup-linux-s390x-gnu': 4.36.0
- '@rollup/rollup-linux-x64-gnu': 4.36.0
- '@rollup/rollup-linux-x64-musl': 4.36.0
- '@rollup/rollup-win32-arm64-msvc': 4.36.0
- '@rollup/rollup-win32-ia32-msvc': 4.36.0
- '@rollup/rollup-win32-x64-msvc': 4.36.0
+ '@rollup/rollup-android-arm-eabi': 4.37.0
+ '@rollup/rollup-android-arm64': 4.37.0
+ '@rollup/rollup-darwin-arm64': 4.37.0
+ '@rollup/rollup-darwin-x64': 4.37.0
+ '@rollup/rollup-freebsd-arm64': 4.37.0
+ '@rollup/rollup-freebsd-x64': 4.37.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.37.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.37.0
+ '@rollup/rollup-linux-arm64-gnu': 4.37.0
+ '@rollup/rollup-linux-arm64-musl': 4.37.0
+ '@rollup/rollup-linux-loongarch64-gnu': 4.37.0
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.37.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.37.0
+ '@rollup/rollup-linux-riscv64-musl': 4.37.0
+ '@rollup/rollup-linux-s390x-gnu': 4.37.0
+ '@rollup/rollup-linux-x64-gnu': 4.37.0
+ '@rollup/rollup-linux-x64-musl': 4.37.0
+ '@rollup/rollup-win32-arm64-msvc': 4.37.0
+ '@rollup/rollup-win32-ia32-msvc': 4.37.0
+ '@rollup/rollup-win32-x64-msvc': 4.37.0
fsevents: 2.3.3
rrweb-cssom@0.7.1: {}
rrweb-cssom@0.8.0: {}
- rspack-resolver@1.2.1:
+ rspack-resolver@1.3.0:
optionalDependencies:
- '@unrs/rspack-resolver-binding-darwin-arm64': 1.2.1
- '@unrs/rspack-resolver-binding-darwin-x64': 1.2.1
- '@unrs/rspack-resolver-binding-freebsd-x64': 1.2.1
- '@unrs/rspack-resolver-binding-linux-arm-gnueabihf': 1.2.1
- '@unrs/rspack-resolver-binding-linux-arm64-gnu': 1.2.1
- '@unrs/rspack-resolver-binding-linux-arm64-musl': 1.2.1
- '@unrs/rspack-resolver-binding-linux-x64-gnu': 1.2.1
- '@unrs/rspack-resolver-binding-linux-x64-musl': 1.2.1
- '@unrs/rspack-resolver-binding-wasm32-wasi': 1.2.1
- '@unrs/rspack-resolver-binding-win32-arm64-msvc': 1.2.1
- '@unrs/rspack-resolver-binding-win32-x64-msvc': 1.2.1
+ '@unrs/rspack-resolver-binding-darwin-arm64': 1.3.0
+ '@unrs/rspack-resolver-binding-darwin-x64': 1.3.0
+ '@unrs/rspack-resolver-binding-freebsd-x64': 1.3.0
+ '@unrs/rspack-resolver-binding-linux-arm-gnueabihf': 1.3.0
+ '@unrs/rspack-resolver-binding-linux-arm-musleabihf': 1.3.0
+ '@unrs/rspack-resolver-binding-linux-arm64-gnu': 1.3.0
+ '@unrs/rspack-resolver-binding-linux-arm64-musl': 1.3.0
+ '@unrs/rspack-resolver-binding-linux-ppc64-gnu': 1.3.0
+ '@unrs/rspack-resolver-binding-linux-s390x-gnu': 1.3.0
+ '@unrs/rspack-resolver-binding-linux-x64-gnu': 1.3.0
+ '@unrs/rspack-resolver-binding-linux-x64-musl': 1.3.0
+ '@unrs/rspack-resolver-binding-wasm32-wasi': 1.3.0
+ '@unrs/rspack-resolver-binding-win32-arm64-msvc': 1.3.0
+ '@unrs/rspack-resolver-binding-win32-ia32-msvc': 1.3.0
+ '@unrs/rspack-resolver-binding-win32-x64-msvc': 1.3.0
rtl-css-js@1.16.1:
dependencies:
- '@babel/runtime': 7.26.10
+ '@babel/runtime': 7.27.0
run-applescript@7.0.0: {}
@@ -28972,7 +28985,7 @@ snapshots:
synckit@0.9.2:
dependencies:
- '@pkgr/core': 0.1.1
+ '@pkgr/core': 0.1.2
tslib: 2.8.1
systeminformation@5.23.8: {}
@@ -29172,11 +29185,11 @@ snapshots:
tinyspy@3.0.2: {}
- tldts-core@6.1.84: {}
+ tldts-core@6.1.85: {}
- tldts@6.1.84:
+ tldts@6.1.85:
dependencies:
- tldts-core: 6.1.84
+ tldts-core: 6.1.85
tmp@0.0.33:
dependencies:
@@ -29198,7 +29211,7 @@ snapshots:
tough-cookie@5.1.2:
dependencies:
- tldts: 6.1.84
+ tldts: 6.1.85
tr46@0.0.3: {}
@@ -29220,7 +29233,7 @@ snapshots:
dependencies:
typescript: 5.8.2
- ts-api-utils@2.0.1(typescript@5.8.2):
+ ts-api-utils@2.1.0(typescript@5.8.2):
dependencies:
typescript: 5.8.2
@@ -29275,7 +29288,7 @@ snapshots:
tslib@2.8.1: {}
- tsup@8.3.5(@microsoft/api-extractor@7.52.1(@types/node@22.10.2))(jiti@2.4.1)(postcss@8.5.3)(tsx@4.19.2)(typescript@5.8.2)(yaml@2.7.0):
+ tsup@8.3.5(@microsoft/api-extractor@7.52.2(@types/node@22.10.2))(jiti@2.4.1)(postcss@8.5.3)(tsx@4.19.2)(typescript@5.8.2)(yaml@2.7.0):
dependencies:
bundle-require: 5.1.0(esbuild@0.24.2)
cac: 6.7.14
@@ -29287,14 +29300,14 @@ snapshots:
picocolors: 1.1.1
postcss-load-config: 6.0.1(jiti@2.4.1)(postcss@8.5.3)(tsx@4.19.2)(yaml@2.7.0)
resolve-from: 5.0.0
- rollup: 4.36.0
+ rollup: 4.37.0
source-map: 0.8.0-beta.0
sucrase: 3.35.0
tinyexec: 0.3.2
tinyglobby: 0.2.12
tree-kill: 1.2.2
optionalDependencies:
- '@microsoft/api-extractor': 7.52.1(@types/node@22.10.2)
+ '@microsoft/api-extractor': 7.52.2(@types/node@22.10.2)
postcss: 8.5.3
typescript: 5.8.2
transitivePeerDependencies:
@@ -29653,7 +29666,7 @@ snapshots:
debug: 4.4.0
es-module-lexer: 1.6.0
pathe: 1.1.2
- vite: 5.4.14(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0)
+ vite: 5.4.15(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0)
transitivePeerDependencies:
- '@types/node'
- less
@@ -29671,7 +29684,7 @@ snapshots:
debug: 4.4.0
es-module-lexer: 1.6.0
pathe: 2.0.3
- vite: 6.2.0(@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: 6.2.3(@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
@@ -29692,7 +29705,7 @@ snapshots:
debug: 4.4.0
es-module-lexer: 1.6.0
pathe: 2.0.3
- vite: 6.2.0(@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: 6.2.3(@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
@@ -29713,7 +29726,7 @@ snapshots:
debug: 4.4.0
es-module-lexer: 1.6.0
pathe: 2.0.3
- vite: 6.2.0(@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: 6.2.3(@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
@@ -29728,10 +29741,10 @@ snapshots:
- tsx
- yaml
- vite-plugin-dts@4.3.0(@types/node@22.10.2)(rollup@4.36.0)(typescript@5.8.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-dts@4.3.0(@types/node@22.10.2)(rollup@4.37.0)(typescript@5.8.2)(vite@6.0.12(@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.52.1(@types/node@22.10.2)
- '@rollup/pluginutils': 5.1.4(rollup@4.36.0)
+ '@microsoft/api-extractor': 7.52.2(@types/node@22.10.2)
+ '@rollup/pluginutils': 5.1.4(rollup@4.37.0)
'@volar/typescript': 2.4.12
'@vue/language-core': 2.1.6(typescript@5.8.2)
compare-versions: 6.1.1
@@ -29741,16 +29754,16 @@ snapshots:
magic-string: 0.30.17
typescript: 5.8.2
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)
+ vite: 6.0.12(@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'
- rollup
- supports-color
- vite-plugin-dts@4.3.0(@types/node@22.10.2)(rollup@4.36.0)(typescript@5.8.2)(vite@6.2.0(@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@4.3.0(@types/node@22.10.2)(rollup@4.37.0)(typescript@5.8.2)(vite@6.2.3(@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.52.1(@types/node@22.10.2)
- '@rollup/pluginutils': 5.1.4(rollup@4.36.0)
+ '@microsoft/api-extractor': 7.52.2(@types/node@22.10.2)
+ '@rollup/pluginutils': 5.1.4(rollup@4.37.0)
'@volar/typescript': 2.4.12
'@vue/language-core': 2.1.6(typescript@5.8.2)
compare-versions: 6.1.1
@@ -29760,58 +29773,58 @@ snapshots:
magic-string: 0.30.17
typescript: 5.8.2
optionalDependencies:
- vite: 6.2.0(@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: 6.2.3(@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'
- rollup
- supports-color
- vite-plugin-node-polyfills@0.22.0(rollup@4.36.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.37.0)(vite@6.0.12(@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.36.0)
+ '@rollup/plugin-inject': 5.0.5(rollup@4.37.0)
node-stdlib-browser: 1.3.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)
+ vite: 6.0.12(@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:
- rollup
- vite-tsconfig-paths@5.1.4(typescript@5.8.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@5.1.4(typescript@5.8.2)(vite@6.0.12(@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:
debug: 4.4.0
globrex: 0.1.2
tsconfck: 3.1.5(typescript@5.8.2)
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)
+ vite: 6.0.12(@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:
- supports-color
- typescript
- vite-tsconfig-paths@5.1.4(typescript@5.8.2)(vite@6.2.0(@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@5.1.4(typescript@5.8.2)(vite@6.2.3(@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:
debug: 4.4.0
globrex: 0.1.2
tsconfck: 3.1.5(typescript@5.8.2)
optionalDependencies:
- vite: 6.2.0(@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: 6.2.3(@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:
- supports-color
- typescript
- vite@5.4.14(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0):
+ vite@5.4.15(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0):
dependencies:
esbuild: 0.21.5
postcss: 8.4.49
- rollup: 4.36.0
+ rollup: 4.37.0
optionalDependencies:
'@types/node': 22.10.2
fsevents: 2.3.3
lightningcss: 1.27.0
terser: 5.37.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@6.0.12(@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:
esbuild: 0.24.2
postcss: 8.4.49
- rollup: 4.36.0
+ rollup: 4.37.0
optionalDependencies:
'@types/node': 22.10.2
fsevents: 2.3.3
@@ -29821,11 +29834,11 @@ snapshots:
tsx: 4.19.2
yaml: 2.7.0
- vite@6.2.0(@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@6.2.3(@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:
esbuild: 0.25.1
postcss: 8.5.3
- rollup: 4.36.0
+ rollup: 4.37.0
optionalDependencies:
'@types/node': 22.10.2
fsevents: 2.3.3
@@ -29850,7 +29863,7 @@ snapshots:
vitest@2.1.9(@types/node@22.10.2)(jsdom@25.0.1)(lightningcss@1.27.0)(terser@5.37.0):
dependencies:
'@vitest/expect': 2.1.9
- '@vitest/mocker': 2.1.9(vite@5.4.14(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0))
+ '@vitest/mocker': 2.1.9(vite@5.4.15(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0))
'@vitest/pretty-format': 2.1.9
'@vitest/runner': 2.1.9
'@vitest/snapshot': 2.1.9
@@ -29866,7 +29879,7 @@ snapshots:
tinyexec: 0.3.2
tinypool: 1.0.2
tinyrainbow: 1.2.0
- vite: 5.4.14(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0)
+ vite: 5.4.15(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0)
vite-node: 2.1.9(@types/node@22.10.2)(lightningcss@1.27.0)(terser@5.37.0)
why-is-node-running: 2.3.0
optionalDependencies:
@@ -29886,7 +29899,7 @@ snapshots:
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.2.0(@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.2.3(@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.9
'@vitest/runner': 3.0.5
'@vitest/snapshot': 3.0.5
@@ -29902,7 +29915,7 @@ snapshots:
tinyexec: 0.3.2
tinypool: 1.0.2
tinyrainbow: 2.0.0
- vite: 6.2.0(@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: 6.2.3(@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:
@@ -29926,7 +29939,7 @@ snapshots:
vitest@3.0.6(@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.6
- '@vitest/mocker': 3.0.6(vite@6.2.0(@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.6(vite@6.2.3(@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.9
'@vitest/runner': 3.0.6
'@vitest/snapshot': 3.0.6
@@ -29942,7 +29955,7 @@ snapshots:
tinyexec: 0.3.2
tinypool: 1.0.2
tinyrainbow: 2.0.0
- vite: 6.2.0(@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: 6.2.3(@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.6(@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:
@@ -29966,7 +29979,7 @@ snapshots:
vitest@3.0.7(@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.7
- '@vitest/mocker': 3.0.7(vite@6.2.0(@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.7(vite@6.2.3(@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.9
'@vitest/runner': 3.0.7
'@vitest/snapshot': 3.0.7
@@ -29982,7 +29995,7 @@ snapshots:
tinyexec: 0.3.2
tinypool: 1.0.2
tinyrainbow: 2.0.0
- vite: 6.2.0(@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: 6.2.3(@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.7(@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:
@@ -30055,7 +30068,7 @@ snapshots:
webpack@5.97.1:
dependencies:
'@types/eslint-scope': 3.7.7
- '@types/estree': 1.0.6
+ '@types/estree': 1.0.7
'@webassemblyjs/ast': 1.14.1
'@webassemblyjs/wasm-edit': 1.14.1
'@webassemblyjs/wasm-parser': 1.14.1
@@ -30345,7 +30358,7 @@ snapshots:
yjs@13.6.24:
dependencies:
- lib0: 0.2.99
+ lib0: 0.2.101
yn@3.1.1: {}
@@ -30363,7 +30376,7 @@ snapshots:
dependencies:
zod: 3.24.1
- zod-to-json-schema@3.24.4(zod@3.24.1):
+ zod-to-json-schema@3.24.5(zod@3.24.1):
dependencies:
zod: 3.24.1
diff --git a/sonar-project.properties b/sonar-project.properties
index e7a41cb5a7..3e6fde6dd1 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -21,5 +21,5 @@ sonar.scm.exclusions.disabled=false
sonar.sourceEncoding=UTF-8
# Coverage
-sonar.coverage.exclusions=**/*.test.*,**/*.spec.*,**/constants.ts,**/route.ts,modules/**/types/**,**/*.stories.*,**/mocks/**,**/openapi.ts,**/openapi-document.ts,playwright/**
-sonar.cpd.exclusions=**/*.test.*,**/*.spec.*,**/constants.ts,**/route.ts,modules/**/types/**,**/openapi.ts,**/openapi-document.ts
+sonar.coverage.exclusions=**/*.test.*,**/*.spec.*,**/*.mdx,**,**/*.config.mts,**/*.config.ts,**/constants.ts,**/route.ts,modules/**/types/**,**/*.stories.*,**/mocks/**,**/openapi.ts,**/openapi-document.ts,playwright/**, **/instrumentation.ts, scripts/merge-client-endpoints.ts
+sonar.cpd.exclusions=**/*.test.*,**/*.spec.*,**/*.mdx,**,**/*.config.mts,**/*.config.ts,**/constants.ts,**/route.ts,modules/**/types/**,**/openapi.ts,**/openapi-document.ts, **/instrumentation.ts, scripts/merge-client-endpoints.ts
\ No newline at end of file
diff --git a/turbo.json b/turbo.json
index 3e20580399..edf8e38705 100644
--- a/turbo.json
+++ b/turbo.json
@@ -92,10 +92,10 @@
"persistent": true
},
"@formbricks/web#test": {
- "dependsOn": ["@formbricks/logger#build"]
+ "dependsOn": ["@formbricks/js#build", "@formbricks/logger#build"]
},
"@formbricks/web#test:coverage": {
- "dependsOn": ["@formbricks/logger#build"]
+ "dependsOn": ["@formbricks/js#build", "@formbricks/logger#build"]
},
"build": {
"dependsOn": ["^build"],
@@ -159,7 +159,6 @@
"NEXT_PUBLIC_FORMBRICKS_ONBOARDING_SURVEY_ID",
"NEXT_PUBLIC_FORMBRICKS_PMF_FORM_ID",
"NEXT_PUBLIC_FORMBRICKS_URL",
- "NEXT_PUBLIC_SENTRY_DSN",
"NEXT_PUBLIC_FORMBRICKS_COM_API_HOST",
"NEXT_PUBLIC_FORMBRICKS_COM_ENVIRONMENT_ID",
"NEXT_PUBLIC_FORMBRICKS_COM_DOCS_FEEDBACK_SURVEY_ID",