chore: move js sdk and demo app to its own repository (#5668)

This commit is contained in:
Matti Nannt
2025-05-05 21:03:54 +02:00
committed by GitHub
parent e0e42d2eed
commit bd22aaaa86
56 changed files with 13 additions and 1513 deletions

View File

@@ -154,10 +154,6 @@ NOTION_OAUTH_CLIENT_SECRET=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
# Configure Formbricks usage within Formbricks
FORMBRICKS_API_HOST=
FORMBRICKS_ENVIRONMENT_ID=
# Oauth credentials for Google sheet integration
GOOGLE_SHEETS_CLIENT_ID=
GOOGLE_SHEETS_CLIENT_SECRET=
@@ -221,4 +217,4 @@ UNKEY_ROOT_KEY=
# SENTRY_AUTH_TOKEN=
# Disable the user management from UI
# DISABLE_USER_MANAGEMENT
# DISABLE_USER_MANAGEMENT=1

View File

@@ -1,5 +0,0 @@
NEXT_PUBLIC_FORMBRICKS_API_HOST=http://localhost:3000
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=YOUR_ENVIRONMENT_ID
# Copy the environment ID for the URL of your Formbricks App and
# paste it above to connect your Formbricks App with the Demo App.

View File

@@ -1,7 +0,0 @@
module.exports = {
extends: ["@formbricks/eslint-config/next.js"],
parserOptions: {
project: "tsconfig.json",
tsconfigRootDir: __dirname,
},
};

36
apps/demo/.gitignore vendored
View File

@@ -1,36 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@@ -1,13 +0,0 @@
import { Sidebar } from "./sidebar";
export function LayoutApp({ children }: { children: React.ReactNode }): React.JSX.Element {
return (
<div className="min-h-full">
{/* Static sidebar for desktop */}
<div className="hidden lg:fixed lg:inset-y-0 lg:flex lg:w-64 lg:flex-col">
<Sidebar />
</div>
<div className="flex flex-1 flex-col lg:pl-64">{children}</div>
</div>
);
}

View File

@@ -1,65 +0,0 @@
import {
ClockIcon,
CogIcon,
CreditCardIcon,
FileBarChartIcon,
HelpCircleIcon,
HomeIcon,
ScaleIcon,
ShieldCheckIcon,
UsersIcon,
} from "lucide-react";
import { classNames } from "../lib/utils";
const navigation = [
{ name: "Home", href: "#", icon: HomeIcon, current: true },
{ name: "History", href: "#", icon: ClockIcon, current: false },
{ name: "Balances", href: "#", icon: ScaleIcon, current: false },
{ name: "Cards", href: "#", icon: CreditCardIcon, current: false },
{ name: "Recipients", href: "#", icon: UsersIcon, current: false },
{ name: "Reports", href: "#", icon: FileBarChartIcon, current: false },
];
const secondaryNavigation = [
{ name: "Settings", href: "#", icon: CogIcon },
{ name: "Help", href: "#", icon: HelpCircleIcon },
{ name: "Privacy", href: "#", icon: ShieldCheckIcon },
];
export function Sidebar(): React.JSX.Element {
return (
<div className="flex grow flex-col overflow-y-auto bg-cyan-700 pt-5 pb-4">
<nav
className="mt-5 flex flex-1 flex-col divide-y divide-cyan-800 overflow-y-auto"
aria-label="Sidebar">
<div className="space-y-1 px-2">
{navigation.map((item) => (
<a
key={item.name}
href={item.href}
className={classNames(
item.current ? "bg-cyan-800 text-white" : "text-cyan-100 hover:bg-cyan-600 hover:text-white",
"group flex items-center rounded-md px-2 py-2 text-sm leading-6 font-medium"
)}
aria-current={item.current ? "page" : undefined}>
<item.icon className="mr-4 h-6 w-6 shrink-0 text-cyan-200" aria-hidden="true" />
{item.name}
</a>
))}
</div>
<div className="mt-6 pt-6">
<div className="space-y-1 px-2">
{secondaryNavigation.map((item) => (
<a
key={item.name}
href={item.href}
className="group flex items-center rounded-md px-2 py-2 text-sm leading-6 font-medium text-cyan-100 hover:bg-cyan-600 hover:text-white">
<item.icon className="mr-4 h-6 w-6 text-cyan-200" aria-hidden="true" />
{item.name}
</a>
))}
</div>
</div>
</nav>
</div>
);
}

View File

@@ -1,23 +0,0 @@
@import 'tailwindcss';
@plugin '@tailwindcss/forms';
@custom-variant dark (&:is(.dark *));
/*
The default border color has changed to `currentcolor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}
}

View File

@@ -1,3 +0,0 @@
export function classNames(...classes: string[]): string {
return classes.filter(Boolean).join(" ");
}

View File

@@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.

View File

@@ -1,17 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "tailwindui.com",
},
{
protocol: "https",
hostname: "images.unsplash.com",
},
],
},
};
export default nextConfig;

View File

@@ -1,28 +0,0 @@
{
"name": "@formbricks/demo",
"version": "0.0.0",
"private": true,
"scripts": {
"clean": "rimraf .turbo node_modules .next",
"dev": "next dev -p 3002 --turbopack",
"go": "next dev -p 3002 --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@formbricks/js": "workspace:*",
"@tailwindcss/forms": "0.5.9",
"@tailwindcss/postcss": "4.1.3",
"lucide-react": "0.486.0",
"next": "15.2.4",
"postcss": "8.5.3",
"react": "19.1.0",
"react-dom": "19.1.0",
"tailwindcss": "4.1.3"
},
"devDependencies": {
"@formbricks/config-typescript": "workspace:*",
"@formbricks/eslint-config": "workspace:*"
}
}

View File

@@ -1,20 +0,0 @@
import type { AppProps } from "next/app";
import Head from "next/head";
import "../globals.css";
export default function App({ Component, pageProps }: AppProps): React.JSX.Element {
return (
<>
<Head>
<title>Demo App</title>
</Head>
{(!process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID ||
!process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) && (
<div className="w-full bg-red-500 p-3 text-center text-sm text-white">
Please set Formbricks environment variables in apps/demo/.env
</div>
)}
<Component {...pageProps} />
</>
);
}

View File

@@ -1,13 +0,0 @@
import { Head, Html, Main, NextScript } from "next/document";
export default function Document(): React.JSX.Element {
return (
<Html lang="en" className="h-full bg-slate-50">
<Head />
<body className="h-full">
<Main />
<NextScript />
</body>
</Html>
);
}

View File

@@ -1,359 +0,0 @@
import Image from "next/image";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import formbricks from "@formbricks/js";
import fbsetup from "../public/fb-setup.png";
declare const window: Window;
export default function AppPage(): React.JSX.Element {
const [darkMode, setDarkMode] = useState(false);
const router = useRouter();
const userId = "THIS-IS-A-VERY-LONG-USER-ID-FOR-TESTING";
const userAttributes = {
"Attribute 1": "one",
"Attribute 2": "two",
"Attribute 3": "three",
};
useEffect(() => {
if (darkMode) {
document.body.classList.add("dark");
} else {
document.body.classList.remove("dark");
}
}, [darkMode]);
useEffect(() => {
const initFormbricks = () => {
// enable Formbricks debug mode by adding formbricksDebug=true GET parameter
const addFormbricksDebugParam = (): void => {
const urlParams = new URLSearchParams(window.location.search);
if (!urlParams.has("formbricksDebug")) {
urlParams.set("formbricksDebug", "true");
const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
window.history.replaceState({}, "", newUrl);
}
};
addFormbricksDebugParam();
if (process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID && process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) {
void formbricks.setup({
environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
appUrl: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
});
}
// Connect next.js router to Formbricks
if (process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID && process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) {
const handleRouteChange = formbricks.registerRouteChange;
router.events.on("routeChangeComplete", () => {
void handleRouteChange();
});
return () => {
router.events.off("routeChangeComplete", () => {
void handleRouteChange();
});
};
}
};
initFormbricks();
}, [router.events]);
return (
<div className="min-h-screen bg-white px-12 py-6 dark:bg-slate-800">
<div className="flex flex-col justify-between md:flex-row">
<div className="flex flex-col items-center gap-2 sm:flex-row">
<div>
<h1 className="text-2xl font-bold text-slate-900 dark:text-white">
Formbricks In-product Survey Demo App
</h1>
<p className="text-slate-700 dark:text-slate-300">
This app helps you test your app surveys. You can create and test user actions, create and
update user attributes, etc.
</p>
</div>
</div>
<button
type="button"
className="mt-2 rounded-lg bg-slate-200 px-6 py-1 dark:bg-slate-700 dark:text-slate-100"
onClick={() => {
setDarkMode(!darkMode);
}}>
{darkMode ? "Toggle Light Mode" : "Toggle Dark Mode"}
</button>
</div>
<div className="my-4 grid grid-cols-1 gap-6 md:grid-cols-2">
<div>
<div className="rounded-lg border border-slate-300 bg-slate-100 p-6 dark:border-slate-600 dark:bg-slate-900">
<h3 className="text-lg font-semibold text-slate-900 dark:text-white">1. Setup .env</h3>
<p className="text-slate-700 dark:text-slate-300">
Copy the environment ID of your Formbricks app to the env variable in /apps/demo/.env
</p>
<Image src={fbsetup} alt="fb setup" className="mt-4 rounded-xs" priority />
<div className="mt-4 flex-col items-start text-sm text-slate-700 sm:flex sm:items-center sm:text-base dark:text-slate-300">
<p className="mb-1 sm:mr-2 sm:mb-0">You&apos;re connected with env:</p>
<div className="flex items-center">
<strong className="w-32 truncate sm:w-auto">
{process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID}
</strong>
<span className="relative ml-2 flex h-3 w-3">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-500 opacity-75" />
<span className="relative inline-flex h-3 w-3 rounded-full bg-green-500" />
</span>
</div>
</div>
</div>
<div className="mt-4 rounded-lg border border-slate-300 bg-slate-100 p-6 dark:border-slate-600 dark:bg-slate-900">
<h3 className="text-lg font-semibold text-slate-900 dark:text-white">2. Widget Logs</h3>
<p className="text-slate-700 dark:text-slate-300">
Look at the logs to understand how the widget works.{" "}
<strong className="dark:text-white">Open your browser console</strong> to see the logs.
</p>
</div>
</div>
<div className="md:grid md:grid-cols-3">
<div className="col-span-3 self-start rounded-lg border border-slate-300 bg-slate-100 p-6 dark:border-slate-600 dark:bg-slate-900">
<h3 className="text-lg font-semibold dark:text-white">
Set a user ID / pull data from Formbricks app
</h3>
<p className="text-slate-700 dark:text-slate-300">
On formbricks.setUserId() the user state will <strong>be fetched from Formbricks</strong> and
the local state gets <strong>updated with the user state</strong>.
</p>
<button
className="my-4 rounded-lg bg-slate-500 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
type="button"
onClick={() => {
void formbricks.setUserId(userId);
}}>
Set user ID
</button>
<p className="text-xs text-slate-700 dark:text-slate-300">
If you made a change in Formbricks app and it does not seem to work, hit &apos;Reset&apos; and
try again.
</p>
</div>
<div className="p-6">
<div>
<button
type="button"
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
No-Code Action
</button>
</div>
<div>
<p className="text-xs text-slate-700 dark:text-slate-300">
This button sends a{" "}
<a
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/actions#setting-up-no-code-actions"
rel="noopener noreferrer"
className="underline dark:text-blue-500"
target="_blank">
No Code Action
</a>{" "}
as long as you created it beforehand in the Formbricks App.{" "}
<a
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/actions#setting-up-no-code-actions"
rel="noopener noreferrer"
target="_blank"
className="underline dark:text-blue-500">
Here are instructions on how to do it.
</a>
</p>
</div>
</div>
<div className="p-6">
<div>
<button
type="button"
onClick={() => {
void formbricks.setAttribute("Plan", "Free");
}}
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
Set Plan to &apos;Free&apos;
</button>
</div>
<div>
<p className="text-xs text-slate-700 dark:text-slate-300">
This button sets the{" "}
<a
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/user-identification#setting-custom-user-attributes"
target="_blank"
rel="noopener noreferrer"
className="underline dark:text-blue-500">
attribute
</a>{" "}
&apos;Plan&apos; to &apos;Free&apos;. If the attribute does not exist, it creates it.
</p>
</div>
</div>
<div className="p-6">
<div>
<button
type="button"
onClick={() => {
void formbricks.setAttribute("Plan", "Paid");
}}
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
Set Plan to &apos;Paid&apos;
</button>
</div>
<div>
<p className="text-xs text-slate-700 dark:text-slate-300">
This button sets the{" "}
<a
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/user-identification#setting-custom-user-attributes"
target="_blank"
rel="noopener noreferrer"
className="underline dark:text-blue-500">
attribute
</a>{" "}
&apos;Plan&apos; to &apos;Paid&apos;. If the attribute does not exist, it creates it.
</p>
</div>
</div>
<div className="p-6">
<div>
<button
type="button"
onClick={() => {
void formbricks.setEmail("test@web.com");
}}
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
Set Email
</button>
</div>
<div>
<p className="text-xs text-slate-700 dark:text-slate-300">
This button sets the{" "}
<a
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/user-identification"
target="_blank"
rel="noopener noreferrer"
className="underline dark:text-blue-500">
user email
</a>{" "}
&apos;test@web.com&apos;
</p>
</div>
</div>
<div className="p-6">
<div>
<button
type="button"
onClick={() => {
void formbricks.setAttributes(userAttributes);
}}
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
Set Multiple Attributes
</button>
</div>
<div>
<p className="text-xs text-slate-700 dark:text-slate-300">
This button sets the{" "}
<a
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/user-identification#setting-custom-user-attributes"
target="_blank"
rel="noopener noreferrer"
className="underline dark:text-blue-500">
user attributes
</a>{" "}
to &apos;one&apos;, &apos;two&apos;, &apos;three&apos;.
</p>
</div>
</div>
<div className="p-6">
<div>
<button
type="button"
onClick={() => {
void formbricks.setLanguage("de");
}}
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
Set Language to &apos;de&apos;
</button>
</div>
<div>
<p className="text-xs text-slate-700 dark:text-slate-300">
This button sets the{" "}
<a
href="https://formbricks.com/docs/xm-and-surveys/surveys/general-features/multi-language-surveys"
target="_blank"
rel="noopener noreferrer"
className="underline dark:text-blue-500">
language
</a>{" "}
to &apos;de&apos;.
</p>
</div>
</div>
<div className="p-6">
<div>
<button
type="button"
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
onClick={() => {
void formbricks.track("code");
}}>
Code Action
</button>
</div>
<div>
<p className="text-xs text-slate-700 dark:text-slate-300">
This button sends a{" "}
<a
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/actions#setting-up-code-actions"
rel="noopener noreferrer"
className="underline dark:text-blue-500"
target="_blank">
Code Action
</a>{" "}
as long as you created it beforehand in the Formbricks App.{" "}
<a
href="https://formbricks.com/docs/xm-and-surveys/surveys/website-app-surveys/actions#setting-up-code-actions"
rel="noopener noreferrer"
target="_blank"
className="underline dark:text-blue-500">
Here are instructions on how to do it.
</a>
</p>
</div>
</div>
<div className="p-6">
<div>
<button
type="button"
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
onClick={() => {
void formbricks.logout();
}}>
Logout
</button>
</div>
<div>
<p className="text-xs text-slate-700 dark:text-slate-300">
This button logs out the user and syncs the local state with Formbricks. (Only works if a
userId is set)
</p>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,5 +0,0 @@
module.exports = {
plugins: {
"@tailwindcss/postcss": {},
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="31" fill="none"><g opacity=".9"><path fill="url(#a)" d="M13 .4v29.3H7V6.3h-.2L0 10.5V5L7.2.4H13Z"/><path fill="url(#b)" d="M28.8 30.1c-2.2 0-4-.3-5.7-1-1.7-.8-3-1.8-4-3.1a7.7 7.7 0 0 1-1.4-4.6h6.2c0 .8.3 1.4.7 2 .4.5 1 .9 1.7 1.2.7.3 1.6.4 2.5.4 1 0 1.7-.2 2.5-.5.7-.3 1.3-.8 1.7-1.4.4-.6.6-1.2.6-2s-.2-1.5-.7-2.1c-.4-.6-1-1-1.8-1.4-.8-.4-1.8-.5-2.9-.5h-2.7v-4.6h2.7a6 6 0 0 0 2.5-.5 4 4 0 0 0 1.7-1.3c.4-.6.6-1.3.6-2a3.5 3.5 0 0 0-2-3.3 5.6 5.6 0 0 0-4.5 0 4 4 0 0 0-1.7 1.2c-.4.6-.6 1.2-.6 2h-6c0-1.7.6-3.2 1.5-4.5 1-1.3 2.2-2.3 3.8-3C25 .4 26.8 0 28.8 0s3.8.4 5.3 1.1c1.5.7 2.7 1.7 3.6 3a7.2 7.2 0 0 1 1.2 4.2c0 1.6-.5 3-1.5 4a7 7 0 0 1-4 2.2v.2c2.2.3 3.8 1 5 2.2a6.4 6.4 0 0 1 1.6 4.6c0 1.7-.5 3.1-1.4 4.4a9.7 9.7 0 0 1-4 3.1c-1.7.8-3.7 1.1-5.8 1.1Z"/></g><defs><linearGradient id="a" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient><linearGradient id="b" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient></defs></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

Before

Width:  |  Height:  |  Size: 629 B

View File

@@ -1,5 +0,0 @@
{
"exclude": ["node_modules"],
"extends": "@formbricks/config-typescript/nextjs.json",
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
}

View File

@@ -1,6 +1,5 @@
"use client";
import { formbricksLogout } from "@/app/lib/formbricks";
import FBLogo from "@/images/formbricks-wordmark.svg";
import { cn } from "@/lib/cn";
import { capitalizeFirstLetter } from "@/lib/utils/strings";
@@ -125,7 +124,6 @@ export const LandingSidebar = ({
<DropdownMenuItem
onClick={async () => {
await signOut({ callbackUrl: "/auth/login" });
await formbricksLogout();
}}
icon={<LogOutIcon className="mr-2 h-4 w-4" strokeWidth={1.5} />}>
{t("common.logout")}

View File

@@ -1,81 +0,0 @@
import { render } from "@testing-library/react";
import { 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 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", () => {
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(
<FormbricksClient
userId="user-123"
email="test@example.com"
formbricksEnvironmentId="env-test"
formbricksApiHost="https://api.test.com"
formbricksEnabled={true}
/>
);
// 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(
<FormbricksClient
userId=""
email="test@example.com"
formbricksEnvironmentId="env-test"
formbricksApiHost="https://api.test.com"
formbricksEnabled={true}
/>
);
// 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();
});
});

View File

@@ -1,44 +0,0 @@
"use client";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";
import formbricks from "@formbricks/js";
interface FormbricksClientProps {
userId: string;
email: string;
formbricksEnvironmentId?: string;
formbricksApiHost?: string;
formbricksEnabled?: boolean;
}
export const FormbricksClient = ({
userId,
email,
formbricksEnvironmentId,
formbricksApiHost,
formbricksEnabled,
}: FormbricksClientProps) => {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
if (formbricksEnabled && userId) {
formbricks.setup({
environmentId: formbricksEnvironmentId ?? "",
appUrl: formbricksApiHost ?? "",
});
formbricks.setUserId(userId);
formbricks.setEmail(email);
}
}, [userId, email, formbricksEnvironmentId, formbricksApiHost, formbricksEnabled]);
useEffect(() => {
if (formbricksEnabled) {
formbricks.registerRouteChange();
}
}, [pathname, searchParams, formbricksEnabled]);
return null;
};

View File

@@ -2,7 +2,6 @@
import { getLatestStableFbReleaseAction } from "@/app/(app)/environments/[environmentId]/actions/actions";
import { NavigationLink } from "@/app/(app)/environments/[environmentId]/components/NavigationLink";
import { formbricksLogout } from "@/app/lib/formbricks";
import FBLogo from "@/images/formbricks-wordmark.svg";
import { cn } from "@/lib/cn";
import { getAccessFlags } from "@/lib/membership/utils";
@@ -265,7 +264,7 @@ export const MainNavigation = ({
size="icon"
onClick={toggleSidebar}
className={cn(
"rounded-xl bg-slate-50 p-1 text-slate-600 transition-all hover:bg-slate-100 focus:ring-0 focus:ring-transparent focus:outline-none"
"rounded-xl bg-slate-50 p-1 text-slate-600 transition-all hover:bg-slate-100 focus:outline-none focus:ring-0 focus:ring-transparent"
)}>
{isCollapsed ? (
<PanelLeftOpenIcon strokeWidth={1.5} />
@@ -392,7 +391,6 @@ export const MainNavigation = ({
onClick={async () => {
const route = await signOut({ redirect: false, callbackUrl: "/auth/login" });
router.push(route.url);
await formbricksLogout();
}}
icon={<LogOutIcon className="mr-2 h-4 w-4" strokeWidth={1.5} />}>
{t("common.logout")}

View File

@@ -26,13 +26,6 @@ vi.mock("@/modules/ui/components/environmentId-base-layout", () => ({
vi.mock("@/modules/ui/components/toaster-client", () => ({
ToasterClient: () => <div data-testid="ToasterClient" />,
}));
vi.mock("../../components/FormbricksClient", () => ({
FormbricksClient: ({ userId, email }: any) => (
<div data-testid="FormbricksClient">
{userId}-{email}
</div>
),
}));
vi.mock("./components/EnvironmentStorageHandler", () => ({
default: ({ environmentId }: any) => <div data-testid="EnvironmentStorageHandler">{environmentId}</div>,
}));

View File

@@ -1,6 +1,5 @@
"use client";
import { formbricksLogout } from "@/app/lib/formbricks";
import { DeleteAccountModal } from "@/modules/account/components/DeleteAccountModal";
import { Button } from "@/modules/ui/components/button";
import { TooltipRenderer } from "@/modules/ui/components/tooltip";
@@ -37,7 +36,6 @@ export const DeleteAccount = ({
setOpen={setModalOpen}
user={user}
isFormbricksCloud={IS_FORMBRICKS_CLOUD}
formbricksLogout={formbricksLogout}
organizationsWithSingleOwner={organizationsWithSingleOwner}
/>
<p className="text-sm text-slate-700">

View File

@@ -36,14 +36,10 @@ vi.mock("@/lib/constants", () => ({
IS_POSTHOG_CONFIGURED: true,
POSTHOG_API_HOST: "test-posthog-api-host",
POSTHOG_API_KEY: "test-posthog-api-key",
FORMBRICKS_API_HOST: "mock-formbricks-api-host",
FORMBRICKS_ENVIRONMENT_ID: "mock-formbricks-environment-id",
IS_FORMBRICKS_ENABLED: true,
}));
vi.mock("@/app/(app)/components/FormbricksClient", () => ({
FormbricksClient: () => <div data-testid="formbricks-client" />,
}));
vi.mock("@/app/intercom/IntercomClientWrapper", () => ({
IntercomClientWrapper: () => <div data-testid="mock-intercom-wrapper" />,
}));
@@ -74,17 +70,5 @@ describe("(app) AppLayout", () => {
expect(screen.getByTestId("mock-intercom-wrapper")).toBeInTheDocument();
expect(screen.getByTestId("toaster-client")).toBeInTheDocument();
expect(screen.getByTestId("child-content")).toHaveTextContent("Hello from children");
expect(screen.getByTestId("formbricks-client")).toBeInTheDocument();
});
test("skips FormbricksClient if no user is present", async () => {
vi.mocked(getServerSession).mockResolvedValueOnce(null);
const element = await AppLayout({
children: <div data-testid="child-content">Hello from children</div>,
});
render(element);
expect(screen.queryByTestId("formbricks-client")).not.toBeInTheDocument();
});
});

View File

@@ -1,13 +1,5 @@
import { FormbricksClient } from "@/app/(app)/components/FormbricksClient";
import { IntercomClientWrapper } from "@/app/intercom/IntercomClientWrapper";
import {
FORMBRICKS_API_HOST,
FORMBRICKS_ENVIRONMENT_ID,
IS_FORMBRICKS_ENABLED,
IS_POSTHOG_CONFIGURED,
POSTHOG_API_HOST,
POSTHOG_API_KEY,
} from "@/lib/constants";
import { IS_POSTHOG_CONFIGURED, POSTHOG_API_HOST, POSTHOG_API_KEY } from "@/lib/constants";
import { getUser } from "@/lib/user/service";
import { authOptions } from "@/modules/auth/lib/authOptions";
import { ClientLogout } from "@/modules/ui/components/client-logout";
@@ -38,15 +30,6 @@ const AppLayout = async ({ children }) => {
</Suspense>
<PHProvider posthogEnabled={IS_POSTHOG_CONFIGURED}>
<>
{user ? (
<FormbricksClient
userId={user.id}
email={user.email}
formbricksApiHost={FORMBRICKS_API_HOST}
formbricksEnvironmentId={FORMBRICKS_ENVIRONMENT_ID}
formbricksEnabled={IS_FORMBRICKS_ENABLED}
/>
) : null}
<IntercomClientWrapper user={user} />
<ToasterClient />
{children}

View File

@@ -1,11 +0,0 @@
import { FORMBRICKS_LOGGED_IN_WITH_LS } from "@/lib/localStorage";
import formbricks from "@formbricks/js";
export const formbricksLogout = async () => {
const loggedInWith = localStorage.getItem(FORMBRICKS_LOGGED_IN_WITH_LS);
localStorage.clear();
if (loggedInWith) {
localStorage.setItem(FORMBRICKS_LOGGED_IN_WITH_LS, loggedInWith);
}
return await formbricks.logout();
};

View File

@@ -3,9 +3,6 @@ import { TUserLocale } from "@formbricks/types/user";
import { env } from "./env";
export const IS_FORMBRICKS_CLOUD = env.IS_FORMBRICKS_CLOUD === "1";
export const FORMBRICKS_API_HOST = env.FORMBRICKS_API_HOST;
export const FORMBRICKS_ENVIRONMENT_ID = env.FORMBRICKS_ENVIRONMENT_ID;
export const IS_FORMBRICKS_ENABLED = !!(env.FORMBRICKS_API_HOST && env.FORMBRICKS_ENVIRONMENT_ID);
export const IS_PRODUCTION = env.NODE_ENV === "production";

View File

@@ -24,12 +24,6 @@ export const env = createEnv({
EMAIL_VERIFICATION_DISABLED: z.enum(["1", "0"]).optional(),
ENCRYPTION_KEY: z.string(),
ENTERPRISE_LICENSE_KEY: z.string().optional(),
FORMBRICKS_API_HOST: z
.string()
.url()
.optional()
.or(z.string().refine((str) => str === "")),
FORMBRICKS_ENVIRONMENT_ID: z.string().optional(),
GITHUB_ID: z.string().optional(),
GITHUB_SECRET: z.string().optional(),
GOOGLE_CLIENT_ID: z.string().optional(),
@@ -138,8 +132,6 @@ export const env = createEnv({
EMAIL_VERIFICATION_DISABLED: process.env.EMAIL_VERIFICATION_DISABLED,
ENCRYPTION_KEY: process.env.ENCRYPTION_KEY,
ENTERPRISE_LICENSE_KEY: process.env.ENTERPRISE_LICENSE_KEY,
FORMBRICKS_API_HOST: process.env.FORMBRICKS_API_HOST,
FORMBRICKS_ENVIRONMENT_ID: process.env.FORMBRICKS_ENVIRONMENT_ID,
GITHUB_ID: process.env.GITHUB_ID,
GITHUB_SECRET: process.env.GITHUB_SECRET,
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,

View File

@@ -26,7 +26,6 @@ describe("DeleteAccountModal", () => {
const mockOrgs: TOrganization[] = [{ name: "Org1" }, { name: "Org2" }] as TOrganization[];
const mockSetOpen = vi.fn();
const mockLogout = vi.fn();
afterEach(() => {
cleanup();
@@ -40,7 +39,6 @@ describe("DeleteAccountModal", () => {
user={mockUser}
isFormbricksCloud={false}
organizationsWithSingleOwner={mockOrgs}
formbricksLogout={mockLogout}
/>
);
@@ -56,7 +54,6 @@ describe("DeleteAccountModal", () => {
user={mockUser}
isFormbricksCloud={false}
organizationsWithSingleOwner={[]}
formbricksLogout={mockLogout}
/>
);
@@ -78,7 +75,6 @@ describe("DeleteAccountModal", () => {
user={mockUser}
isFormbricksCloud={false}
organizationsWithSingleOwner={[]}
formbricksLogout={mockLogout}
/>
);
@@ -90,7 +86,6 @@ describe("DeleteAccountModal", () => {
await waitFor(() => {
expect(deleteUserAction).toHaveBeenCalled();
expect(mockLogout).toHaveBeenCalled();
expect(signOut).toHaveBeenCalledWith({ callbackUrl: "/auth/login" });
expect(mockSetOpen).toHaveBeenCalledWith(false);
});
@@ -114,7 +109,6 @@ describe("DeleteAccountModal", () => {
user={mockUser}
isFormbricksCloud={true}
organizationsWithSingleOwner={[]}
formbricksLogout={mockLogout}
/>
);
@@ -126,7 +120,6 @@ describe("DeleteAccountModal", () => {
await waitFor(() => {
expect(deleteUserAction).toHaveBeenCalled();
expect(mockLogout).toHaveBeenCalled();
expect(signOut).toHaveBeenCalledWith({ redirect: true });
expect(window.location.replace).toHaveBeenCalled();
expect(mockSetOpen).toHaveBeenCalledWith(false);
@@ -143,7 +136,6 @@ describe("DeleteAccountModal", () => {
user={mockUser}
isFormbricksCloud={false}
organizationsWithSingleOwner={[]}
formbricksLogout={mockLogout}
/>
);

View File

@@ -16,7 +16,6 @@ interface DeleteAccountModalProps {
user: TUser;
isFormbricksCloud: boolean;
organizationsWithSingleOwner: TOrganization[];
formbricksLogout: () => Promise<void>;
}
export const DeleteAccountModal = ({
@@ -24,7 +23,6 @@ export const DeleteAccountModal = ({
open,
user,
isFormbricksCloud,
formbricksLogout,
organizationsWithSingleOwner,
}: DeleteAccountModalProps) => {
const { t } = useTranslate();
@@ -38,7 +36,6 @@ export const DeleteAccountModal = ({
try {
setDeleting(true);
await deleteUserAction();
await formbricksLogout();
// redirect to account deletion survey in Formbricks Cloud
if (isFormbricksCloud) {
await signOut({ redirect: true });

View File

@@ -1,6 +1,5 @@
"use client";
import { formbricksLogout } from "@/app/lib/formbricks";
import { DeleteAccountModal } from "@/modules/account/components/DeleteAccountModal";
import { Alert, AlertDescription, AlertTitle } from "@/modules/ui/components/alert";
import { Button } from "@/modules/ui/components/button";
@@ -29,7 +28,6 @@ export const RemovedFromOrganization = ({ user, isFormbricksCloud }: RemovedFrom
setOpen={setIsModalOpen}
user={user}
isFormbricksCloud={isFormbricksCloud}
formbricksLogout={formbricksLogout}
organizationsWithSingleOwner={[]}
/>
<Button

View File

@@ -9,8 +9,6 @@ import { QuestionFormInput } from "./index";
// Mock all the modules that might cause server-side environment variable access issues
vi.mock("@/lib/constants", () => ({
IS_FORMBRICKS_CLOUD: false,
FORMBRICKS_API_HOST: "http://localhost:3000",
FORMBRICKS_ENVIRONMENT_ID: "test-env-id",
ENCRYPTION_KEY: "test-encryption-key",
WEBAPP_URL: "http://localhost:3000",
DEFAULT_BRAND_COLOR: "#64748b",
@@ -45,8 +43,6 @@ vi.mock("@/lib/constants", () => ({
vi.mock("@/lib/env", () => ({
env: {
IS_FORMBRICKS_CLOUD: "0",
FORMBRICKS_API_HOST: "http://localhost:3000",
FORMBRICKS_ENVIRONMENT_ID: "test-env-id",
ENCRYPTION_KEY: "test-encryption-key",
NODE_ENV: "test",
ENTERPRISE_LICENSE_KEY: "test-license-key",

View File

@@ -16,8 +16,6 @@ vi.mock("@/modules/ui/components/code-action-form", () => ({
// Mock constants
vi.mock("@/lib/constants", () => ({
IS_FORMBRICKS_CLOUD: false,
FORMBRICKS_API_HOST: "http://localhost:3000",
FORMBRICKS_ENVIRONMENT_ID: "test-env-id",
}));
// Mock the createActionClassAction function

View File

@@ -1,12 +1,10 @@
"use client";
import { formbricksLogout } from "@/app/lib/formbricks";
import { signOut } from "next-auth/react";
import { useEffect } from "react";
export const ClientLogout = () => {
useEffect(() => {
formbricksLogout();
signOut();
});
return null;

View File

@@ -27,8 +27,6 @@ vi.mock("@/lib/constants", () => ({
WEBAPP_URL: "test-webapp-url",
IS_PRODUCTION: false,
SENTRY_DSN: "mock-sentry-dsn",
FORMBRICKS_API_HOST: "test-formbricks-api-host",
FORMBRICKS_ENVIRONMENT_ID: "test-formbricks-environment-id",
IS_FORMBRICKS_ENABLED: true,
}));
@@ -36,13 +34,6 @@ vi.mock("@/lib/constants", () => ({
vi.mock("@/app/(app)/environments/[environmentId]/components/ResponseFilterContext", () => ({
ResponseFilterProvider: ({ children }: any) => <div data-testid="ResponseFilterProvider">{children}</div>,
}));
vi.mock("@/app/(app)/components/FormbricksClient", () => ({
FormbricksClient: ({ userId, email }: any) => (
<div data-testid="FormbricksClient">
{userId}-{email}
</div>
),
}));
vi.mock("@/modules/ui/components/toaster-client", () => ({
ToasterClient: () => <div data-testid="ToasterClient" />,
}));
@@ -69,7 +60,6 @@ describe("EnvironmentIdBaseLayout", () => {
expect(screen.getByTestId("ResponseFilterProvider")).toBeInTheDocument();
expect(screen.getByTestId("PosthogIdentify")).toHaveTextContent("org1");
expect(screen.getByTestId("FormbricksClient")).toHaveTextContent("user1-user1@example.com");
expect(screen.getByTestId("ToasterClient")).toBeInTheDocument();
expect(screen.getByTestId("child")).toHaveTextContent("Test Content");
});

View File

@@ -1,12 +1,6 @@
import { FormbricksClient } from "@/app/(app)/components/FormbricksClient";
import { PosthogIdentify } from "@/app/(app)/environments/[environmentId]/components/PosthogIdentify";
import { ResponseFilterProvider } from "@/app/(app)/environments/[environmentId]/components/ResponseFilterContext";
import {
FORMBRICKS_API_HOST,
FORMBRICKS_ENVIRONMENT_ID,
IS_FORMBRICKS_ENABLED,
IS_POSTHOG_CONFIGURED,
} from "@/lib/constants";
import { IS_POSTHOG_CONFIGURED } from "@/lib/constants";
import { ToasterClient } from "@/modules/ui/components/toaster-client";
import { Session } from "next-auth";
import { TOrganization } from "@formbricks/types/organizations";
@@ -38,13 +32,6 @@ export const EnvironmentIdBaseLayout = async ({
organizationBilling={organization.billing}
isPosthogEnabled={IS_POSTHOG_CONFIGURED}
/>
<FormbricksClient
userId={user.id}
email={user.email}
formbricksApiHost={FORMBRICKS_API_HOST}
formbricksEnvironmentId={FORMBRICKS_ENVIRONMENT_ID}
formbricksEnabled={IS_FORMBRICKS_ENABLED}
/>
<ToasterClient />
{children}
</ResponseFilterProvider>

View File

@@ -26,8 +26,8 @@
"@dnd-kit/sortable": "10.0.0",
"@dnd-kit/utilities": "3.2.2",
"@formbricks/database": "workspace:*",
"@formbricks/js": "workspace:*",
"@formbricks/i18n-utils": "workspace:*",
"@formbricks/js-core": "workspace:*",
"@formbricks/logger": "workspace:*",
"@formbricks/surveys": "workspace:*",
"@formbricks/types": "workspace:*",

View File

@@ -46,7 +46,6 @@ export default defineConfig({
"app/(app)/environments/**/components/PosthogIdentify.tsx",
"app/(app)/(onboarding)/organizations/**/layout.tsx",
"app/(app)/(survey-editor)/environments/**/layout.tsx",
"app/(app)/components/FormbricksClient.tsx",
"app/(auth)/layout.tsx",
"app/(app)/layout.tsx",
"app/layout.tsx",
@@ -169,7 +168,7 @@ export default defineConfig({
"**/openapi.ts", // Exclude openapi configuration files
"**/openapi-document.ts", // Exclude openapi document files
"modules/**/types/**", // Exclude types
"**/stories.tsx" // Exclude story files
"**/stories.tsx", // Exclude story files
],
},
},

View File

@@ -78,10 +78,6 @@ x-environment: &environment
# Set the below to your Survey Domain(default is WEBAPP_URL)
# SURVEY_URL:
# Configure Formbricks usage within Formbricks.
# FORMBRICKS_API_HOST:
# FORMBRICKS_ENVIRONMENT_ID:
# The SENTRY_DSN is used for error tracking and performance monitoring with Sentry.
# SENTRY_DSN:
# It's used for authentication when uploading source maps to Sentry, to make errors more readable.

View File

@@ -26,7 +26,6 @@
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"generate": "turbo run generate",
"lint": "turbo run lint",
"release": "turbo run build --filter=@formbricks/js... && changeset publish",
"test": "turbo run test --no-cache",
"test:coverage": "turbo run test:coverage --no-cache",
"test:e2e": "playwright test",

View File

@@ -1,7 +0,0 @@
module.exports = {
extends: ["@formbricks/eslint-config/library.js"],
parserOptions: {
project: "tsconfig.json",
tsconfigRootDir: __dirname,
},
};

View File

@@ -1,4 +0,0 @@
node_modules
.vscode
build
dist

View File

@@ -1,9 +0,0 @@
MIT License
Copyright (c) 2024 Formbricks GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,36 +0,0 @@
# Formbricks Browser JS Library
[![npm package](https://img.shields.io/npm/v/@formbricks/js?style=flat-square)](https://www.npmjs.com/package/@formbricks/js)
[![MIT License](https://img.shields.io/badge/License-MIT-red.svg?style=flat-square)](https://opensource.org/licenses/MIT)
Please see [Formbricks Docs](https://formbricks.com/docs).
Specifically, [Quickstart/Implementation details](https://formbricks.com/docs/getting-started/quickstart-in-app-survey).
## What is Formbricks
Formbricks is your go-to solution for in-product micro-surveys that will supercharge your product experience! 🚀 For more information please check out [formbricks.com](https://formbricks.com).
## How to use this library
1. Install the Formbricks package inside your project using npm:
```bash
npm install @formbricks/js
```
1. Import Formbricks and initialize the widget in your main component (e.g., App.tsx or App.js):
```javascript
import formbricks from "@formbricks/js";
if (typeof window !== "undefined") {
formbricks.setup({
environmentId: "your-environment-id",
appUrl: "https://app.formbricks.com",
});
}
```
Replace your-environment-id with your actual environment ID. You can find your environment ID in the **Setup Checklist** in the Formbricks settings. If you want to use the user identification feature, please check out [our docs for details](https://formbricks.com/docs/app-surveys/user-identification).
For more detailed guides for different frameworks, check out our [Framework Guides](https://formbricks.com/docs/getting-started/framework-guides).

View File

@@ -1,50 +0,0 @@
{
"name": "@formbricks/js",
"license": "MIT",
"version": "4.1.0",
"description": "Formbricks-js allows you to connect your index to Formbricks, display surveys and trigger events.",
"homepage": "https://formbricks.com",
"repository": {
"type": "git",
"url": "https://github.com/formbricks/formbricks"
},
"keywords": [
"Formbricks",
"surveys",
"experience management"
],
"sideEffects": false,
"files": [
"dist"
],
"type": "module",
"exports": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"typesVersions": {
"*": {
"*": [
"./dist/index.d.ts"
]
}
},
"scripts": {
"dev": "vite build --watch --mode dev",
"build": "tsc && vite build",
"build:dev": "tsc && vite build --mode dev",
"go": "vite build --watch --mode dev",
"lint": "eslint . --ext .ts,.js,.tsx,.jsx",
"clean": "rimraf .turbo node_modules dist coverage"
},
"author": "Formbricks <hola@formbricks.com>",
"devDependencies": {
"@formbricks/js-core": "workspace:*",
"@formbricks/config-typescript": "workspace:*",
"@formbricks/eslint-config": "workspace:*",
"terser": "5.39.0",
"vite": "6.3.5",
"vite-plugin-dts": "4.5.3"
}
}

View File

@@ -1,23 +0,0 @@
import { type TFormbricks as TFormbricksCore } from "@formbricks/js-core";
import { loadFormbricksToProxy } from "./lib/load-formbricks";
type TFormbricks = Omit<TFormbricksCore, "track"> & {
track: (code: string) => Promise<void>;
};
declare global {
interface Window {
formbricks: TFormbricks | undefined;
}
}
const formbricksProxyHandler: ProxyHandler<TFormbricks> = {
get(_target, prop, _receiver) {
return (...args: unknown[]) => loadFormbricksToProxy(prop as string, ...args);
},
};
const formbricks: TFormbricksCore = new Proxy({} as TFormbricks, formbricksProxyHandler);
// eslint-disable-next-line import/no-default-export -- Required for UMD
export default formbricks;

View File

@@ -1,108 +0,0 @@
/*
eslint-disable no-console --
* Required for logging errors
*/
type Result<T, E = Error> = { ok: true; data: T } | { ok: false; error: E };
let isInitializing = false;
let isInitialized = false;
// Load the SDK, return the result
const loadFormbricksSDK = async (apiHostParam: string): Promise<Result<void>> => {
if (!window.formbricks) {
const scriptTag = document.createElement("script");
scriptTag.type = "text/javascript";
scriptTag.src = `${apiHostParam}/js/formbricks.umd.cjs`;
scriptTag.async = true;
const getFormbricks = async (): Promise<void> =>
new Promise<void>((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new Error(`Formbricks SDK loading timed out`));
}, 10000);
scriptTag.onload = () => {
clearTimeout(timeoutId);
resolve();
};
scriptTag.onerror = () => {
clearTimeout(timeoutId);
reject(new Error(`Failed to load Formbricks SDK`));
};
});
document.head.appendChild(scriptTag);
try {
await getFormbricks();
return { ok: true, data: undefined };
} catch (error) {
const err = error as { message?: string };
return {
ok: false,
error: new Error(err.message ?? `Failed to load Formbricks SDK`),
};
}
}
return { ok: true, data: undefined };
};
const functionsToProcess: { prop: string; args: unknown[] }[] = [];
export const loadFormbricksToProxy = async (prop: string, ...args: unknown[]): Promise<void> => {
// all of this should happen when not initialized:
if (!isInitialized) {
// We need to still support init for backwards compatibility
// but we should log a warning that the init method is deprecated
if (prop === "setup") {
if (isInitializing) {
console.warn("🧱 Formbricks - Warning: Formbricks is already initializing.");
return;
}
// reset the initialization state
isInitializing = true;
const argsTyped = args[0] as { appUrl: string; environmentId: string };
const { appUrl, environmentId } = argsTyped;
if (!appUrl) {
console.error("🧱 Formbricks - Error: appUrl is required");
return;
}
if (!environmentId) {
console.error("🧱 Formbricks - Error: environmentId is required");
return;
}
const loadSDKResult = await loadFormbricksSDK(appUrl);
if (loadSDKResult.ok) {
if (window.formbricks) {
const formbricksInstance = window.formbricks;
// @ts-expect-error -- Required for dynamic function calls
void formbricksInstance.setup(...args);
isInitializing = false;
isInitialized = true;
// process the queued functions
for (const { prop: functionProp, args: functionArgs } of functionsToProcess) {
if (typeof formbricksInstance[functionProp as keyof typeof formbricksInstance] !== "function") {
console.error(`🧱 Formbricks - Error: Method ${functionProp} does not exist on formbricks`);
continue;
}
// @ts-expect-error -- Required for dynamic function calls
(formbricksInstance[functionProp] as unknown)(...functionArgs);
}
}
}
} else {
console.warn(
"🧱 Formbricks - Warning: Formbricks not initialized. This method will be queued and executed after initialization."
);
functionsToProcess.push({ prop, args });
}
} else if (window.formbricks) {
// Access the default export for initialized state too
const formbricksInstance = window.formbricks;
type Formbricks = typeof formbricksInstance;
type FunctionProp = keyof Formbricks;
const functionPropTyped = prop as FunctionProp;
// @ts-expect-error -- Required for dynamic function calls
await formbricksInstance[functionPropTyped](...args);
}
};

View File

@@ -1 +0,0 @@
/// <reference types="vite/client" />

View File

@@ -1,15 +0,0 @@
{
"compilerOptions": {
"allowImportingTsExtensions": true,
"declaration": true,
"isolatedModules": true,
"noEmit": true,
"paths": {
"@formbricks/js-core/*": ["../js-core/dist/*"]
},
"resolveJsonModule": true,
"strict": true
},
"extends": "@formbricks/config-typescript/js-library.json",
"include": ["src", "package.json"]
}

View File

@@ -1,23 +0,0 @@
import { resolve } from "path";
import { defineConfig } from "vite";
import dts from "vite-plugin-dts";
const config = () => {
return defineConfig({
build: {
emptyOutDir: false, // keep the dist folder to avoid errors with pnpm go when folder is empty during build
minify: "terser",
sourcemap: true,
lib: {
// Could also be a dictionary or array of multiple entry points
entry: resolve(__dirname, "src/index.ts"),
name: "formbricksJsWrapper",
formats: ["es", "cjs"],
fileName: "index",
},
},
plugins: [dts({ rollupTypes: true, bundledPackages: ["@formbricks/js-core"] })],
});
};
export default config;

350
pnpm-lock.yaml generated
View File

@@ -40,43 +40,6 @@ importers:
specifier: 2.5.2
version: 2.5.2
apps/demo:
dependencies:
'@formbricks/js':
specifier: workspace:*
version: link:../../packages/js
'@tailwindcss/forms':
specifier: 0.5.9
version: 0.5.9(tailwindcss@4.1.3)
'@tailwindcss/postcss':
specifier: 4.1.3
version: 4.1.3
lucide-react:
specifier: 0.486.0
version: 0.486.0(react@19.1.0)
next:
specifier: 15.2.4
version: 15.2.4(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
postcss:
specifier: 8.5.3
version: 8.5.3
react:
specifier: 19.1.0
version: 19.1.0
react-dom:
specifier: 19.1.0
version: 19.1.0(react@19.1.0)
tailwindcss:
specifier: 4.1.3
version: 4.1.3
devDependencies:
'@formbricks/config-typescript':
specifier: workspace:*
version: link:../../packages/config-typescript
'@formbricks/eslint-config':
specifier: workspace:*
version: link:../../packages/config-eslint
apps/storybook:
dependencies:
eslint-plugin-react-refresh:
@@ -176,9 +139,9 @@ importers:
'@formbricks/i18n-utils':
specifier: workspace:*
version: link:../../packages/i18n-utils
'@formbricks/js':
'@formbricks/js-core':
specifier: workspace:*
version: link:../../packages/js
version: link:../../packages/js-core
'@formbricks/logger':
specifier: workspace:*
version: link:../../packages/logger
@@ -710,27 +673,6 @@ importers:
specifier: 3.1.1
version: 3.1.1(@types/debug@4.1.12)(@types/node@22.14.0)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
packages/js:
devDependencies:
'@formbricks/config-typescript':
specifier: workspace:*
version: link:../config-typescript
'@formbricks/eslint-config':
specifier: workspace:*
version: link:../config-eslint
'@formbricks/js-core':
specifier: workspace:*
version: link:../js-core
terser:
specifier: 5.39.0
version: 5.39.0
vite:
specifier: 6.3.5
version: 6.3.5(@types/node@22.14.0)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
vite-plugin-dts:
specifier: 4.5.3
version: 4.5.3(@types/node@22.14.0)(rollup@4.40.0)(typescript@5.8.3)(vite@6.3.5(@types/node@22.14.0)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1))
packages/js-core:
devDependencies:
'@formbricks/config-typescript':
@@ -2081,105 +2023,54 @@ packages:
next: '>= 13.5.1 < 15'
redis: '>= 4.6'
'@next/env@15.2.4':
resolution: {integrity: sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==}
'@next/env@15.2.5':
resolution: {integrity: sha512-uWkCf9C8wKTyQjqrNk+BA7eL3LOQdhL+xlmJUf2O85RM4lbzwBwot3Sqv2QGe/RGnc3zysIf1oJdtq9S00pkmQ==}
'@next/eslint-plugin-next@15.3.0':
resolution: {integrity: sha512-511UUcpWw5GWTyKfzW58U2F/bYJyjLE9e3SlnGK/zSXq7RqLlqFO8B9bitJjumLpj317fycC96KZ2RZsjGNfBw==}
'@next/swc-darwin-arm64@15.2.4':
resolution: {integrity: sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-arm64@15.2.5':
resolution: {integrity: sha512-4OimvVlFTbgzPdA0kh8A1ih6FN9pQkL4nPXGqemEYgk+e7eQhsst/p35siNNqA49eQA6bvKZ1ASsDtu9gtXuog==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-x64@15.2.4':
resolution: {integrity: sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-darwin-x64@15.2.5':
resolution: {integrity: sha512-ohzRaE9YbGt1ctE0um+UGYIDkkOxHV44kEcHzLqQigoRLaiMtZzGrA11AJh2Lu0lv51XeiY1ZkUvkThjkVNBMA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-linux-arm64-gnu@15.2.4':
resolution: {integrity: sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-gnu@15.2.5':
resolution: {integrity: sha512-FMSdxSUt5bVXqqOoZCc/Seg4LQep9w/fXTazr/EkpXW2Eu4IFI9FD7zBDlID8TJIybmvKk7mhd9s+2XWxz4flA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-musl@15.2.4':
resolution: {integrity: sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-musl@15.2.5':
resolution: {integrity: sha512-4ZNKmuEiW5hRKkGp2HWwZ+JrvK4DQLgf8YDaqtZyn7NYdl0cHfatvlnLFSWUayx9yFAUagIgRGRk8pFxS8Qniw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-x64-gnu@15.2.4':
resolution: {integrity: sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-gnu@15.2.5':
resolution: {integrity: sha512-bE6lHQ9GXIf3gCDE53u2pTl99RPZW5V1GLHSRMJ5l/oB/MT+cohu9uwnCK7QUph2xIOu2a6+27kL0REa/kqwZw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-musl@15.2.4':
resolution: {integrity: sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-musl@15.2.5':
resolution: {integrity: sha512-y7EeQuSkQbTAkCEQnJXm1asRUuGSWAchGJ3c+Qtxh8LVjXleZast8Mn/rL7tZOm7o35QeIpIcid6ufG7EVTTcA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-win32-arm64-msvc@15.2.4':
resolution: {integrity: sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-arm64-msvc@15.2.5':
resolution: {integrity: sha512-gQMz0yA8/dskZM2Xyiq2FRShxSrsJNha40Ob/M2n2+JGRrZ0JwTVjLdvtN6vCxuq4ByhOd4a9qEf60hApNR2gQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-x64-msvc@15.2.4':
resolution: {integrity: sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@next/swc-win32-x64-msvc@15.2.5':
resolution: {integrity: sha512-tBDNVUcI7U03+3oMvJ11zrtVin5p0NctiuKmTGyaTIEAVj9Q77xukLXGXRnWxKRIIdFG4OTA2rUVGZDYOwgmAA==}
engines: {node: '>= 10'}
@@ -4094,87 +3985,6 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1'
'@tailwindcss/forms@0.5.9':
resolution: {integrity: sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==}
peerDependencies:
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20'
'@tailwindcss/node@4.1.3':
resolution: {integrity: sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA==}
'@tailwindcss/oxide-android-arm64@4.1.3':
resolution: {integrity: sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
'@tailwindcss/oxide-darwin-arm64@4.1.3':
resolution: {integrity: sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@tailwindcss/oxide-darwin-x64@4.1.3':
resolution: {integrity: sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@tailwindcss/oxide-freebsd-x64@4.1.3':
resolution: {integrity: sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.3':
resolution: {integrity: sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@tailwindcss/oxide-linux-arm64-gnu@4.1.3':
resolution: {integrity: sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@tailwindcss/oxide-linux-arm64-musl@4.1.3':
resolution: {integrity: sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@tailwindcss/oxide-linux-x64-gnu@4.1.3':
resolution: {integrity: sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@tailwindcss/oxide-linux-x64-musl@4.1.3':
resolution: {integrity: sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@tailwindcss/oxide-win32-arm64-msvc@4.1.3':
resolution: {integrity: sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@tailwindcss/oxide-win32-x64-msvc@4.1.3':
resolution: {integrity: sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@tailwindcss/oxide@4.1.3':
resolution: {integrity: sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ==}
engines: {node: '>= 10'}
'@tailwindcss/postcss@4.1.3':
resolution: {integrity: sha512-6s5nJODm98F++QT49qn8xJKHQRamhYHfMi3X7/ltxiSQ9dyRsaFSfFkfaMsanWzf+TMYQtbk8mt5f6cCVXJwfg==}
'@tailwindcss/typography@0.5.16':
resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==}
peerDependencies:
@@ -7543,11 +7353,6 @@ packages:
resolution: {integrity: sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==}
engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'}
lucide-react@0.486.0:
resolution: {integrity: sha512-xWop/wMsC1ikiEVLZrxXjPKw4vU/eAip33G2mZHgbWnr4Nr5Rt4Vx4s/q1D3B/rQVbxjOuqASkEZcUxDEKzecw==}
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
lucide-react@0.487.0:
resolution: {integrity: sha512-aKqhOQ+YmFnwq8dWgGjOuLc8V1R9/c/yOd+zDY4+ohsR2Jo05lSGc3WsstYPIzcTpeosN7LoCkLReUUITvaIvw==}
peerDependencies:
@@ -7920,27 +7725,6 @@ packages:
zod:
optional: true
next@15.2.4:
resolution: {integrity: sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
'@playwright/test': ^1.41.2
babel-plugin-react-compiler: '*'
react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
'@playwright/test':
optional: true
babel-plugin-react-compiler:
optional: true
sass:
optional: true
next@15.2.5:
resolution: {integrity: sha512-LlqS8ljc7RWR3riUwxB5+14v7ULAa5EuLUyarD/sFgXPd6Hmmscg8DXcu9hDdh5atybrIDVBrFhjDpRIQo/4pQ==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
@@ -9503,9 +9287,6 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
tailwindcss@4.1.3:
resolution: {integrity: sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==}
tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
@@ -12375,59 +12156,33 @@ snapshots:
next: 15.2.5(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
redis: 4.7.0
'@next/env@15.2.4': {}
'@next/env@15.2.5': {}
'@next/eslint-plugin-next@15.3.0':
dependencies:
fast-glob: 3.3.1
'@next/swc-darwin-arm64@15.2.4':
optional: true
'@next/swc-darwin-arm64@15.2.5':
optional: true
'@next/swc-darwin-x64@15.2.4':
optional: true
'@next/swc-darwin-x64@15.2.5':
optional: true
'@next/swc-linux-arm64-gnu@15.2.4':
optional: true
'@next/swc-linux-arm64-gnu@15.2.5':
optional: true
'@next/swc-linux-arm64-musl@15.2.4':
optional: true
'@next/swc-linux-arm64-musl@15.2.5':
optional: true
'@next/swc-linux-x64-gnu@15.2.4':
optional: true
'@next/swc-linux-x64-gnu@15.2.5':
optional: true
'@next/swc-linux-x64-musl@15.2.4':
optional: true
'@next/swc-linux-x64-musl@15.2.5':
optional: true
'@next/swc-win32-arm64-msvc@15.2.4':
optional: true
'@next/swc-win32-arm64-msvc@15.2.5':
optional: true
'@next/swc-win32-x64-msvc@15.2.4':
optional: true
'@next/swc-win32-x64-msvc@15.2.5':
optional: true
@@ -14597,73 +14352,6 @@ snapshots:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.4.16(ts-node@10.9.2(@types/node@22.14.0)(typescript@5.8.3))
'@tailwindcss/forms@0.5.9(tailwindcss@4.1.3)':
dependencies:
mini-svg-data-uri: 1.4.4
tailwindcss: 4.1.3
'@tailwindcss/node@4.1.3':
dependencies:
enhanced-resolve: 5.18.1
jiti: 2.4.2
lightningcss: 1.29.2
tailwindcss: 4.1.3
'@tailwindcss/oxide-android-arm64@4.1.3':
optional: true
'@tailwindcss/oxide-darwin-arm64@4.1.3':
optional: true
'@tailwindcss/oxide-darwin-x64@4.1.3':
optional: true
'@tailwindcss/oxide-freebsd-x64@4.1.3':
optional: true
'@tailwindcss/oxide-linux-arm-gnueabihf@4.1.3':
optional: true
'@tailwindcss/oxide-linux-arm64-gnu@4.1.3':
optional: true
'@tailwindcss/oxide-linux-arm64-musl@4.1.3':
optional: true
'@tailwindcss/oxide-linux-x64-gnu@4.1.3':
optional: true
'@tailwindcss/oxide-linux-x64-musl@4.1.3':
optional: true
'@tailwindcss/oxide-win32-arm64-msvc@4.1.3':
optional: true
'@tailwindcss/oxide-win32-x64-msvc@4.1.3':
optional: true
'@tailwindcss/oxide@4.1.3':
optionalDependencies:
'@tailwindcss/oxide-android-arm64': 4.1.3
'@tailwindcss/oxide-darwin-arm64': 4.1.3
'@tailwindcss/oxide-darwin-x64': 4.1.3
'@tailwindcss/oxide-freebsd-x64': 4.1.3
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.3
'@tailwindcss/oxide-linux-arm64-gnu': 4.1.3
'@tailwindcss/oxide-linux-arm64-musl': 4.1.3
'@tailwindcss/oxide-linux-x64-gnu': 4.1.3
'@tailwindcss/oxide-linux-x64-musl': 4.1.3
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.3
'@tailwindcss/oxide-win32-x64-msvc': 4.1.3
'@tailwindcss/postcss@4.1.3':
dependencies:
'@alloc/quick-lru': 5.2.0
'@tailwindcss/node': 4.1.3
'@tailwindcss/oxide': 4.1.3
postcss: 8.5.3
tailwindcss: 4.1.3
'@tailwindcss/typography@0.5.16(tailwindcss@3.4.16(ts-node@10.9.2(@types/node@22.14.0)(typescript@5.8.3)))':
dependencies:
lodash.castarray: 4.4.0
@@ -18399,6 +18087,7 @@ snapshots:
lightningcss-linux-x64-musl: 1.29.2
lightningcss-win32-arm64-msvc: 1.29.2
lightningcss-win32-x64-msvc: 1.29.2
optional: true
lilconfig@3.1.3: {}
@@ -18519,10 +18208,6 @@ snapshots:
lru.min@1.1.2: {}
lucide-react@0.486.0(react@19.1.0):
dependencies:
react: 19.1.0
lucide-react@0.487.0(react@19.1.0):
dependencies:
react: 19.1.0
@@ -18899,33 +18584,6 @@ snapshots:
optionalDependencies:
zod: 3.24.1
next@15.2.4(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@next/env': 15.2.4
'@swc/counter': 0.1.3
'@swc/helpers': 0.5.15
busboy: 1.6.0
caniuse-lite: 1.0.30001715
postcss: 8.4.31
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
styled-jsx: 5.1.6(react@19.1.0)
optionalDependencies:
'@next/swc-darwin-arm64': 15.2.4
'@next/swc-darwin-x64': 15.2.4
'@next/swc-linux-arm64-gnu': 15.2.4
'@next/swc-linux-arm64-musl': 15.2.4
'@next/swc-linux-x64-gnu': 15.2.4
'@next/swc-linux-x64-musl': 15.2.4
'@next/swc-win32-arm64-msvc': 15.2.4
'@next/swc-win32-x64-msvc': 15.2.4
'@opentelemetry/api': 1.9.0
'@playwright/test': 1.52.0
sharp: 0.33.5
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
next@15.2.5(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@next/env': 15.2.5
@@ -20681,8 +20339,6 @@ snapshots:
transitivePeerDependencies:
- ts-node
tailwindcss@4.1.3: {}
tapable@2.2.1: {}
tar-fs@2.1.2:

View File

@@ -11,14 +11,6 @@
"@formbricks/database#setup": {
"dependsOn": ["db:up"]
},
"@formbricks/demo#go": {
"cache": false,
"dependsOn": ["@formbricks/js#build"],
"persistent": true
},
"@formbricks/demo#lint": {
"dependsOn": ["@formbricks/js#build"]
},
"@formbricks/i18n-utils#build": {
"dependsOn": ["^build"],
"outputs": ["packages/i18n-utils/dist/**"]
@@ -29,29 +21,13 @@
"@formbricks/i18n-utils#test": {
"dependsOn": ["@formbricks/i18n-utils#build"]
},
"@formbricks/js#build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"@formbricks/js#build:dev": {
"dependsOn": ["^build:dev"],
"outputs": ["dist/**"]
},
"@formbricks/js#go": {
"cache": false,
"dependsOn": ["@formbricks/js#build"],
"persistent": true
},
"@formbricks/js#lint": {
"dependsOn": ["@formbricks/js-core#build"]
},
"@formbricks/js-core#build": {
"dependsOn": ["^build", "@formbricks/database#build"],
"outputs": ["dist/**"]
},
"@formbricks/js-core#go": {
"cache": false,
"dependsOn": ["@formbricks/database#db:setup", "@formbricks/js#build"],
"dependsOn": ["@formbricks/database#db:setup"],
"persistent": true
},
"@formbricks/react-native#build": {
@@ -78,14 +54,14 @@
},
"@formbricks/web#go": {
"cache": false,
"dependsOn": ["@formbricks/database#db:setup", "@formbricks/js#build"],
"dependsOn": ["@formbricks/database#db:setup"],
"persistent": true
},
"@formbricks/web#test": {
"dependsOn": ["@formbricks/js#build", "@formbricks/logger#build"]
"dependsOn": ["@formbricks/logger#build"]
},
"@formbricks/web#test:coverage": {
"dependsOn": ["@formbricks/js#build", "@formbricks/logger#build"]
"dependsOn": ["@formbricks/logger#build"]
},
"build": {
"dependsOn": ["^build"],
@@ -109,8 +85,6 @@
"EMAIL_VERIFICATION_DISABLED",
"ENCRYPTION_KEY",
"ENTERPRISE_LICENSE_KEY",
"FORMBRICKS_API_HOST",
"FORMBRICKS_ENVIRONMENT_ID",
"GITHUB_ID",
"GITHUB_SECRET",
"GOOGLE_CLIENT_ID",