mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-10 03:08:50 -06:00
Compare commits
6 Commits
typeerror-
...
feat/user-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ad3c009c2 | ||
|
|
202958cac2 | ||
|
|
8e901fb3c9 | ||
|
|
29afb3e4e9 | ||
|
|
38a3b31761 | ||
|
|
2bfb79d999 |
19
.github/workflows/translation-check.yml
vendored
19
.github/workflows/translation-check.yml
vendored
@@ -32,21 +32,20 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
node-version: 18
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0
|
||||
- name: Setup Node.js 22.x
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
||||
with:
|
||||
version: 9.15.9
|
||||
node-version: 22.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
|
||||
- name: Validate translation keys
|
||||
run: |
|
||||
|
||||
@@ -38,7 +38,7 @@ export const replaceAttributeRecall = (survey: TSurvey, attributes: TAttributes)
|
||||
}
|
||||
});
|
||||
}
|
||||
(surveyTemp.endings ?? []).forEach((ending) => {
|
||||
surveyTemp.endings.forEach((ending) => {
|
||||
if (ending.type === "endScreen") {
|
||||
languages.forEach((language) => {
|
||||
if (ending.headline && ending.headline[language]?.includes("recall:")) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ImageResponse } from "@vercel/og";
|
||||
import { ImageResponse } from "next/og";
|
||||
import { NextRequest } from "next/server";
|
||||
|
||||
export const GET = async (req: NextRequest) => {
|
||||
|
||||
@@ -110,7 +110,7 @@ export const QuotaModal = ({
|
||||
],
|
||||
},
|
||||
action: quota?.action || "endSurvey",
|
||||
endingCardId: quota?.endingCardId || survey.endings?.[0]?.id || null,
|
||||
endingCardId: quota?.endingCardId || survey.endings[0]?.id || null,
|
||||
countPartialSubmissions: quota?.countPartialSubmissions || false,
|
||||
surveyId: survey.id,
|
||||
};
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
import { FormControl, FormError, FormField, FormItem, FormLabel } from "@/modules/ui/components/form";
|
||||
import { IdBadge } from "@/modules/ui/components/id-badge";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { InputCombobox } from "@/modules/ui/components/input-combo-box";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -187,14 +188,16 @@ export const TeamSettingsModal = ({
|
||||
const currentMemberId = watchMembers[index]?.userId;
|
||||
return orgMembers
|
||||
.filter((om) => !selectedMemberIds.includes(om?.id) || om?.id === currentMemberId)
|
||||
.map((om) => ({ label: om?.name, value: om?.id }));
|
||||
.map((om) => ({ label: om?.name, value: om?.id }))
|
||||
.sort((a, b) => a.label.localeCompare(b.label));
|
||||
};
|
||||
|
||||
const getProjectOptionsForIndex = (index: number) => {
|
||||
const currentProjectId = watchProjects[index]?.projectId;
|
||||
return orgProjects
|
||||
.filter((op) => !selectedProjectIds.includes(op?.id) || op?.id === currentProjectId)
|
||||
.map((op) => ({ label: op?.name, value: op?.id }));
|
||||
.map((op) => ({ label: op?.name, value: op?.id }))
|
||||
.sort((a, b) => a.label.localeCompare(b.label));
|
||||
};
|
||||
|
||||
const handleMemberSelectionChange = (index: number, userId: string) => {
|
||||
@@ -278,29 +281,21 @@ export const TeamSettingsModal = ({
|
||||
|
||||
return (
|
||||
<FormItem className="flex-1">
|
||||
<Select
|
||||
onValueChange={(val) => {
|
||||
field.onChange(val);
|
||||
handleMemberSelectionChange(index, val);
|
||||
<InputCombobox
|
||||
id={`member-${index}-select`}
|
||||
options={memberOpts}
|
||||
value={member.userId || null}
|
||||
onChangeValue={(val) => {
|
||||
const userId = val as string;
|
||||
field.onChange(userId);
|
||||
handleMemberSelectionChange(index, userId);
|
||||
}}
|
||||
disabled={isSelectDisabled}
|
||||
value={member.userId}>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t("environments.settings.teams.select_member")}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{memberOpts.map((option) => (
|
||||
<SelectItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
id={`member-${index}-option`}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
disabled={!!isSelectDisabled}
|
||||
comboboxClasses="max-w-full"
|
||||
searchPlaceholder={t(
|
||||
"environments.settings.teams.select_member"
|
||||
)}
|
||||
/>
|
||||
{error?.message && (
|
||||
<FormError className="text-left">{error.message}</FormError>
|
||||
)}
|
||||
@@ -426,26 +421,19 @@ export const TeamSettingsModal = ({
|
||||
|
||||
return (
|
||||
<FormItem className="flex-1">
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
value={project.projectId}
|
||||
disabled={isSelectDisabled}>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={t("environments.settings.teams.select_workspace")}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{projectOpts.map((option) => (
|
||||
<SelectItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
id={`project-${index}-option`}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputCombobox
|
||||
id={`project-${index}-select`}
|
||||
options={projectOpts}
|
||||
value={project.projectId || null}
|
||||
onChangeValue={(val) => {
|
||||
field.onChange(val as string);
|
||||
}}
|
||||
disabled={!!isSelectDisabled}
|
||||
comboboxClasses="max-w-full"
|
||||
searchPlaceholder={t(
|
||||
"environments.settings.teams.select_workspace"
|
||||
)}
|
||||
/>
|
||||
{error?.message && (
|
||||
<FormError className="text-left">{error.message}</FormError>
|
||||
)}
|
||||
|
||||
@@ -115,7 +115,7 @@ export const ElementFormInput = ({
|
||||
: currentElement.id;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isWelcomeCard, isEndingCard, currentElement?.id]);
|
||||
const endingCard = (localSurvey.endings ?? []).find((ending) => ending.id === elementId);
|
||||
const endingCard = localSurvey.endings.find((ending) => ending.id === elementId);
|
||||
|
||||
const surveyLanguageCodes = useMemo(
|
||||
() => extractLanguageCodes(localSurvey.languages),
|
||||
|
||||
@@ -57,7 +57,7 @@ export const getEndingCardText = (
|
||||
questionIdx: number
|
||||
): TI18nString => {
|
||||
const endingCardIndex = questionIdx - questions.length;
|
||||
const card = survey.endings?.[endingCardIndex];
|
||||
const card = survey.endings[endingCardIndex];
|
||||
|
||||
if (card?.type === "endScreen") {
|
||||
return (card[id as keyof typeof card] as TI18nString) || createI18nString("", surveyLanguageCodes);
|
||||
|
||||
@@ -85,7 +85,7 @@ export const EditorCardMenu = ({
|
||||
|
||||
const elements = getElementsFromBlocks(survey.blocks);
|
||||
const isDeleteDisabled =
|
||||
cardType === "element" ? elements.length === 1 : survey.type === "link" && (survey.endings?.length ?? 0) === 1;
|
||||
cardType === "element" ? elements.length === 1 : survey.type === "link" && survey.endings.length === 1;
|
||||
|
||||
const availableElementTypes = isCxMode ? getCXElementNameMap(t) : getElementNameMap(t);
|
||||
|
||||
|
||||
@@ -102,42 +102,6 @@ describe("Survey Utils", () => {
|
||||
expect(result.segment).toBeNull();
|
||||
expect(result.id).toBe("surveyJs");
|
||||
});
|
||||
|
||||
test("should default endings to empty array when null", () => {
|
||||
const surveyPrisma = {
|
||||
id: "survey5",
|
||||
name: "Survey with null endings",
|
||||
displayPercentage: "100",
|
||||
segment: null,
|
||||
endings: null,
|
||||
};
|
||||
const result = transformPrismaSurvey<TSurvey>(surveyPrisma);
|
||||
expect(result.endings).toEqual([]);
|
||||
});
|
||||
|
||||
test("should default endings to empty array when undefined", () => {
|
||||
const surveyPrisma = {
|
||||
id: "survey6",
|
||||
name: "Survey with undefined endings",
|
||||
displayPercentage: "100",
|
||||
segment: null,
|
||||
endings: undefined,
|
||||
};
|
||||
const result = transformPrismaSurvey<TSurvey>(surveyPrisma);
|
||||
expect(result.endings).toEqual([]);
|
||||
});
|
||||
|
||||
test("should preserve endings when provided", () => {
|
||||
const surveyPrisma = {
|
||||
id: "survey7",
|
||||
name: "Survey with endings",
|
||||
displayPercentage: "100",
|
||||
segment: null,
|
||||
endings: [{ id: "ending1", type: "endScreen" }],
|
||||
};
|
||||
const result = transformPrismaSurvey<TSurvey>(surveyPrisma);
|
||||
expect(result.endings).toEqual([{ id: "ending1", type: "endScreen" }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildWhereClause", () => {
|
||||
|
||||
@@ -21,8 +21,6 @@ export const transformPrismaSurvey = <T extends TSurvey | TJsEnvironmentStateSur
|
||||
displayPercentage: Number(surveyPrisma.displayPercentage) || null,
|
||||
segment,
|
||||
customHeadScriptsMode: surveyPrisma.customHeadScriptsMode,
|
||||
// Ensure endings is always an array to prevent runtime errors
|
||||
endings: surveyPrisma.endings ?? [],
|
||||
} as T;
|
||||
|
||||
return transformedSurvey;
|
||||
|
||||
@@ -64,6 +64,7 @@ export interface InputComboboxProps {
|
||||
showCheckIcon?: boolean;
|
||||
comboboxClasses?: string;
|
||||
emptyDropdownText?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
// Helper to flatten all options and their children
|
||||
@@ -87,6 +88,7 @@ export const InputCombobox: React.FC<InputComboboxProps> = ({
|
||||
showCheckIcon = false,
|
||||
comboboxClasses,
|
||||
emptyDropdownText,
|
||||
disabled = false,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const resolvedSearchPlaceholder = searchPlaceholder ?? t("common.search");
|
||||
@@ -201,6 +203,7 @@ export const InputCombobox: React.FC<InputComboboxProps> = ({
|
||||
<div
|
||||
className={cn(
|
||||
"group/icon flex max-w-[440px] overflow-hidden rounded-md border border-slate-300 hover:border-slate-400",
|
||||
disabled && "pointer-events-none opacity-50",
|
||||
comboboxClasses
|
||||
)}>
|
||||
{withInput && inputType !== "dropdown" && (
|
||||
@@ -213,7 +216,7 @@ export const InputCombobox: React.FC<InputComboboxProps> = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenu open={open} onOpenChange={(o) => !disabled && setOpen(o)}>
|
||||
<DropdownMenuTrigger asChild className="z-10">
|
||||
<div
|
||||
id={id}
|
||||
|
||||
@@ -102,7 +102,7 @@ export const PreviewSurvey = ({
|
||||
}
|
||||
|
||||
// check the endings
|
||||
const ending = (survey.endings ?? []).find((e) => e.id === newElementId);
|
||||
const ending = survey.endings.find((e) => e.id === newElementId);
|
||||
if (ending) {
|
||||
setBlockId(ending.id);
|
||||
return;
|
||||
@@ -119,7 +119,7 @@ export const PreviewSurvey = ({
|
||||
|
||||
const onFinished = () => {
|
||||
// close modal if there are no elements left
|
||||
if (survey.type === "app" && (survey.endings?.length ?? 0) === 0) {
|
||||
if (survey.type === "app" && survey.endings.length === 0) {
|
||||
setIsModalOpen(false);
|
||||
setTimeout(() => {
|
||||
if (survey.blocks[0]) {
|
||||
|
||||
@@ -80,7 +80,8 @@
|
||||
"xm-and-surveys/surveys/general-features/email-followups",
|
||||
"xm-and-surveys/surveys/general-features/quota-management",
|
||||
"xm-and-surveys/surveys/general-features/spam-protection",
|
||||
"xm-and-surveys/surveys/general-features/tags"
|
||||
"xm-and-surveys/surveys/general-features/tags",
|
||||
"xm-and-surveys/surveys/general-features/validation-rules"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -161,6 +162,7 @@
|
||||
"xm-and-surveys/core-features/integrations/activepieces",
|
||||
"xm-and-surveys/core-features/integrations/airtable",
|
||||
"xm-and-surveys/core-features/integrations/google-sheets",
|
||||
"xm-and-surveys/core-features/integrations/hubspot",
|
||||
"xm-and-surveys/core-features/integrations/make",
|
||||
"xm-and-surveys/core-features/integrations/n8n",
|
||||
"xm-and-surveys/core-features/integrations/notion",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
183
docs/xm-and-surveys/core-features/integrations/hubspot.mdx
Normal file
183
docs/xm-and-surveys/core-features/integrations/hubspot.mdx
Normal file
@@ -0,0 +1,183 @@
|
||||
---
|
||||
title: "HubSpot"
|
||||
description: "Learn how to integrate Formbricks with HubSpot to automatically create or update contacts when survey responses are submitted."
|
||||
---
|
||||
|
||||
<Note>
|
||||
Formbricks doesn't have a native HubSpot integration yet. This guide shows you how to connect Formbricks with HubSpot using automation platforms (Make.com, n8n) or custom webhooks.
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before setting up the integration, you'll need:
|
||||
|
||||
1. **A Formbricks account** with at least one survey that collects email addresses
|
||||
2. **A HubSpot account** with API access
|
||||
3. **HubSpot API credentials** (see authentication options below)
|
||||
4. **Automation service** like Make, n8n, or ActivePieces (for no-code methods)
|
||||
|
||||
### HubSpot Authentication Options
|
||||
|
||||
HubSpot offers two main ways to authenticate API requests:
|
||||
|
||||
#### Option A: OAuth 2.0 via Public App (Recommended)
|
||||
|
||||
OAuth is the recommended approach for production integrations. When using Make.com or n8n, they handle OAuth authentication for you through their built-in HubSpot connectors.
|
||||
|
||||
#### Option B: Legacy Private Apps (Simple Setup)
|
||||
|
||||
For custom webhook handlers, you can use a Legacy Private App which provides a static access token. While marked as "legacy," these apps remain fully supported by HubSpot.
|
||||
|
||||
To create a Legacy Private App:
|
||||
|
||||
<Steps>
|
||||
<Step title="Open HubSpot Settings">
|
||||
Go to your HubSpot account **Settings** and navigate to **Integrations** → **Private Apps**.
|
||||
</Step>
|
||||
<Step title="Create the App">
|
||||
Click **Create a private app** and give it a name (e.g., "Formbricks Integration").
|
||||
</Step>
|
||||
<Step title="Configure Scopes">
|
||||
Under **Scopes**, add `crm.objects.contacts.write` and `crm.objects.contacts.read`.
|
||||
</Step>
|
||||
<Step title="Get Your Access Token">
|
||||
Click **Create app** and copy the access token.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Note>
|
||||
For more information on HubSpot's authentication options, see the [HubSpot Authentication Overview](https://developers.hubspot.com/docs/guides/apps/authentication/intro-to-auth).
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Method 1: Using Make.com (Recommended for No-Code)
|
||||
|
||||
<Note>
|
||||
Before starting, ensure your survey has clear `questionId` values set. You can only update these before publishing. If your survey is already published, duplicate it and update the question IDs in the copy.
|
||||
</Note>
|
||||
|
||||
<Steps>
|
||||
<Step title="Set Up Your Survey">
|
||||
Make sure your survey has meaningful `questionId` values for each question. This makes mapping responses to HubSpot fields easier.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Create a New Make.com Scenario">
|
||||
Go to [Make.com](https://make.com) and create a new scenario. Search for **Formbricks** and select it as your trigger, then choose **Response Finished** as the trigger event.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Connect Formbricks to Make">
|
||||
Click **Create a webhook**, enter your Formbricks API Host (default: `https://app.formbricks.com`), add your Formbricks API Key (see [API Key Setup](/api-reference/rest-api#how-to-generate-an-api-key)), and select the survey you want to connect.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Add the HubSpot Module">
|
||||
Click the **+** button after the Formbricks trigger, search for **HubSpot**, choose **Create or Update a Contact** as the action, and connect your HubSpot account.
|
||||
</Step>
|
||||
<Step title="Map Formbricks Fields to HubSpot">
|
||||
Map the Formbricks response fields to HubSpot contact properties:
|
||||
|
||||
| HubSpot Field | Formbricks Field |
|
||||
| ------------- | ---------------- |
|
||||
| Email | `data.email` (your email question ID) |
|
||||
| First Name | `data.firstName` (if collected) |
|
||||
| Last Name | `data.lastName` (if collected) |
|
||||
| Custom Property | Any other `data.*` field |
|
||||
|
||||
You can also map metadata: `meta.country`, `meta.userAgent.browser`, `survey.title`.
|
||||
</Step>
|
||||
<Step title="Test and Activate">
|
||||
Submit a test response to your Formbricks survey, verify the contact appears in HubSpot, and turn on your Make scenario.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Method 2: Using n8n (Self-Hosted Option)
|
||||
|
||||
<Note>
|
||||
The Formbricks n8n node is available as a community node. Install it via **Settings** → **Community Nodes** → install `@formbricks/n8n-nodes-formbricks`.
|
||||
</Note>
|
||||
|
||||
<Steps>
|
||||
<Step title="Set Up the Formbricks Trigger">
|
||||
Create a new workflow in n8n, add the **Formbricks** trigger node, connect it with your Formbricks API Key and host, select **Response Finished** as the event, and choose your survey.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Add the HubSpot Node">
|
||||
Add a new node and search for **HubSpot**, select **Create/Update Contact** as the operation, and connect your HubSpot account (n8n supports both OAuth and access token authentication).
|
||||
</Step>
|
||||
<Step title="Configure Field Mapping">
|
||||
In the HubSpot node, map the fields:
|
||||
|
||||
```
|
||||
Email: {{ $json.data.email }}
|
||||
First Name: {{ $json.data.firstName }}
|
||||
Last Name: {{ $json.data.lastName }}
|
||||
```
|
||||
|
||||
For custom HubSpot properties, use the **Additional Fields** section to add mappings like `survey_source`, `response_id`, and `submission_date`.
|
||||
</Step>
|
||||
<Step title="Test Your Workflow">
|
||||
Click **Listen for event** in the Formbricks trigger, submit a test survey response, verify the data flows through to HubSpot, and activate your workflow.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Method 3: Using Webhooks (Custom Integration)
|
||||
|
||||
For maximum flexibility, you can use Formbricks webhooks with a custom endpoint that calls the HubSpot API directly. This approach is ideal for developers who want full control.
|
||||
|
||||
<Note>
|
||||
This method requires a HubSpot access token. You can use a Legacy Private App token (simplest) or implement OAuth 2.0 for production applications.
|
||||
</Note>
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a Formbricks Webhook">
|
||||
Go to **Configuration** → **Integrations** in Formbricks, click **Manage Webhooks** → **Add Webhook**, enter your endpoint URL, select **Response Finished** as the trigger, and choose the surveys to monitor.
|
||||
|
||||

|
||||
</Step>
|
||||
<Step title="Build Your Webhook Handler">
|
||||
Your webhook handler needs to:
|
||||
- **Receive the Formbricks webhook** - Accept POST requests with the survey response payload
|
||||
- **Extract contact data** - Parse the email and other fields from `data.data` (keyed by your `questionId` values)
|
||||
- **Call the HubSpot API** - Use the [HubSpot Contacts API](https://developers.hubspot.com/docs/api/crm/contacts) to create or update contacts
|
||||
- **Handle duplicates** - HubSpot returns a 409 error if a contact with that email exists; search and update instead
|
||||
|
||||
You can deploy using serverless functions (Vercel, AWS Lambda, Cloudflare Workers), traditional servers, or low-code platforms. For webhook signature verification, see the [Webhooks documentation](/xm-and-surveys/core-features/integrations/webhooks).
|
||||
</Step>
|
||||
<Step title="Deploy and Test">
|
||||
Deploy your webhook handler to a publicly accessible URL, add the URL to your Formbricks webhook configuration, submit a test survey response, and verify the contact appears in HubSpot.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Contact Not Created in HubSpot
|
||||
|
||||
1. **Check the email field**: Ensure your survey has an email question and you're mapping the correct `questionId`
|
||||
2. **Verify API token**: Make sure your HubSpot access token has the required scopes (`crm.objects.contacts.write` and `crm.objects.contacts.read`)
|
||||
3. **Check for duplicates**: HubSpot returns a 409 error if a contact with that email already exists
|
||||
|
||||
### Webhook Not Triggering
|
||||
|
||||
1. Verify the webhook URL is publicly accessible
|
||||
2. Check that **Response Finished** trigger is selected
|
||||
3. Ensure the survey is linked to the webhook
|
||||
|
||||
### Testing Your Integration
|
||||
|
||||
1. Use a unique test email for each test
|
||||
2. Check HubSpot's **Contacts** page after submitting a response
|
||||
3. Review your webhook handler logs for errors
|
||||
|
||||
---
|
||||
|
||||
Still struggling or something not working as expected? [Join our GitHub Discussions](https://github.com/formbricks/formbricks/discussions) and we're happy to help!
|
||||
@@ -15,6 +15,8 @@ At Formbricks, we understand the importance of integrating with third-party appl
|
||||
|
||||
* [Google Sheets](/xm-and-surveys/core-features/integrations/google-sheets): Automatically send responses to a Google Sheet of your choice.
|
||||
|
||||
* [HubSpot](/xm-and-surveys/core-features/integrations/hubspot): Create or update HubSpot contacts automatically when survey responses are submitted.
|
||||
|
||||
* [Make](/xm-and-surveys/core-features/integrations/make): Leverage Make's powerful automation capabilities to automate your workflows.
|
||||
|
||||
* [n8n](/xm-and-surveys/core-features/integrations/n8n)(Open Source): Automate workflows with n8n's no-code automation tool
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
---
|
||||
title: "Validation Rules"
|
||||
description: "Validation rules help you ensure that respondents provide data in the correct format and within expected constraints"
|
||||
icon: "check-double"
|
||||
---
|
||||
|
||||
By adding validation rules to your questions, you can improve data quality, reduce errors, and create a better survey experience.
|
||||
|
||||

|
||||
|
||||
## How Validation Rules Work
|
||||
|
||||
Validation rules are evaluated when a respondent submits their answer. If the answer doesn't meet the validation criteria, an error message is displayed and the respondent must correct their input before proceeding.
|
||||
|
||||
You can combine multiple validation rules using **All are true** or **Any is true** logic:
|
||||
- **All are true**: All rules must pass for the response to be valid
|
||||
- **Any is true**: At least one rule must pass for the response to be valid
|
||||
|
||||
## Available Validation Rules by Question Type
|
||||
|
||||
### Free Text Questions
|
||||
|
||||
Free text questions support different validation rules based on the input type:
|
||||
|
||||
#### Text Input Type
|
||||
|
||||
| Rule | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| At least (characters) | Requires at least N characters | At least 10 characters for detailed feedback |
|
||||
| At most (characters) | Limits response to N characters | At most 500 characters for short answers |
|
||||
| Matches Regex Pattern | Matches a regular expression pattern | Custom format validation |
|
||||
| Is | Exact match required | Must equal "CONFIRM" |
|
||||
| Is not | Must not match the value | Cannot be "N/A" |
|
||||
| Contains | Must include the substring | Must contain "@company.com" |
|
||||
| Does not contain | Must not include the substring | Cannot contain profanity |
|
||||
|
||||
#### Email Input Type
|
||||
|
||||
Email input automatically validates email format. Additional rules available:
|
||||
- At least, At most (characters)
|
||||
- Matches Regex Pattern, Is, Is not
|
||||
- Contains, Does not contain
|
||||
|
||||
#### URL Input Type
|
||||
|
||||
URL input automatically validates URL format. Additional rules available:
|
||||
- At least, At most (characters)
|
||||
- Matches Regex Pattern, Is, Is not
|
||||
- Contains, Does not contain
|
||||
|
||||
#### Phone Input Type
|
||||
|
||||
Phone input automatically validates phone number format. Additional rules available:
|
||||
- At least, At most (characters)
|
||||
- Matches Regex Pattern, Is, Is not
|
||||
- Contains, Does not contain
|
||||
|
||||
#### Number Input Type
|
||||
|
||||
| Rule | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| At least | Number must be at least N | Age must be at least 18 |
|
||||
| At most | Number cannot exceed N | Quantity at most 100 |
|
||||
| Is | Number must equal N | Quantity is 1 |
|
||||
| Is not | Number must not equal N | Cannot be 0 |
|
||||
|
||||
### Multiple Choice (Multi-Select) Questions
|
||||
|
||||
| Rule | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| At least (options selected) | Require at least N selections | At least 2 options selected |
|
||||
| At most (options selected) | Limit to N selections | At most 3 options selected |
|
||||
|
||||
### Picture Selection Questions
|
||||
|
||||
| Rule | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| At least (options selected) | Require at least N pictures | At least 1 design selected |
|
||||
| At most (options selected) | Limit to N pictures | At most 2 favorites selected |
|
||||
|
||||
### Date Questions
|
||||
|
||||
| Rule | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| Is later than | Date must be after specified date | Must be after today |
|
||||
| Is earlier than | Date must be before specified date | Must be before Dec 31, 2025 |
|
||||
| Is between | Date must be within range | Between Jan 1 and Dec 31 |
|
||||
| Is not between | Date must be outside range | Cannot be during holidays |
|
||||
|
||||
<Note>
|
||||
Date values should be specified in YYYY-MM-DD format (e.g., 2025-01-15).
|
||||
</Note>
|
||||
|
||||
### Matrix Questions
|
||||
|
||||
| Rule | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| Minimum rows answered | Require at least N rows to be answered | Answer at least 3 rows |
|
||||
| Answer all rows | All rows must have a selection | Complete the entire matrix |
|
||||
|
||||
### Ranking Questions
|
||||
|
||||
| Rule | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| Minimum options ranked | Require at least N items to be ranked | Rank your top 3 |
|
||||
| Rank all options | All options must be ranked | Rank all 5 items |
|
||||
|
||||
### File Upload Questions
|
||||
|
||||
| Rule | Description | Example |
|
||||
|------|-------------|---------|
|
||||
| File extension is | Only allow specific file types | Only .pdf, .docx allowed |
|
||||
| File extension is not | Block specific file types | No .exe files |
|
||||
|
||||
<Note>
|
||||
File size limits are configured separately in the question settings using the "Maximum file size" option.
|
||||
</Note>
|
||||
|
||||
### Address Questions
|
||||
|
||||
Each address field (Address Line 1, Address Line 2, City, State, ZIP, Country) can have its own validation rules:
|
||||
- At least, At most (characters)
|
||||
- Matches Regex Pattern
|
||||
- Is, Is not
|
||||
- Contains, Does not contain
|
||||
|
||||
### Contact Info Questions
|
||||
|
||||
Each contact field can have specific validation rules:
|
||||
|
||||
**First Name, Last Name, Company**:
|
||||
- At least, At most (characters)
|
||||
- Matches Regex Pattern, Is, Is not
|
||||
- Contains, Does not contain
|
||||
|
||||
**Email**: Automatically validates email format, plus text rules above
|
||||
|
||||
**Phone**: Automatically validates phone format, plus text rules above
|
||||
|
||||
## Adding Validation Rules
|
||||
|
||||
<Steps>
|
||||
<Step title="Open the Question Settings">
|
||||
Click on the question you want to validate to open its settings panel.
|
||||
</Step>
|
||||
<Step title="Navigate to Validation Rules">
|
||||
Scroll down to find the "Validation Rules" section and click to expand it.
|
||||
</Step>
|
||||
<Step title="Add a Rule">
|
||||
Click the "Add rule" button to add a new validation rule.
|
||||
</Step>
|
||||
<Step title="Configure the Rule">
|
||||
Select the rule type from the dropdown and enter the required value (if applicable).
|
||||
</Step>
|
||||
<Step title="Set Logic (Optional)">
|
||||
If you have multiple rules, choose whether they should be combined with "All are true" or "Any is true" logic.
|
||||
</Step>
|
||||
<Step title="Save Your Survey">
|
||||
Click "Save" to apply the validation rules to your survey.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Error Messages
|
||||
|
||||
Formbricks automatically generates user-friendly error messages based on your validation rules. Error messages are displayed below the input field when validation fails.
|
||||
|
||||
Example error messages:
|
||||
- "Must be at least 10 characters"
|
||||
- "Must be a valid email address"
|
||||
- "Please select at least 2 options"
|
||||
- "Date must be after 2025-01-01"
|
||||
|
||||
## Multi-Language Support
|
||||
|
||||
Validation rules work with multi-language surveys. Error messages are automatically displayed in the respondent's selected language.
|
||||
|
||||
## Combining Multiple Rules
|
||||
|
||||
When using multiple validation rules:
|
||||
|
||||
**All are true**: Use when all conditions must be met.
|
||||
- Example: Text must be at least 10 characters AND contain "@email.com"
|
||||
|
||||
**Any is true**: Use when any condition is acceptable.
|
||||
- Example: Date is earlier than 2025-01-01 OR is later than 2025-12-31
|
||||
@@ -91,11 +91,12 @@
|
||||
"typeorm": ">=0.3.26",
|
||||
"systeminformation": "5.27.14",
|
||||
"qs": ">=6.14.1",
|
||||
"preact": ">=10.26.10",
|
||||
"fast-xml-parser": ">=5.3.4",
|
||||
"diff": ">=8.0.3"
|
||||
},
|
||||
"comments": {
|
||||
"overrides": "Security fixes for transitive dependencies. Remove when upstream packages update: axios (CVE-2025-58754) - awaiting @boxyhq/saml-jackson update | node-forge (Dependabot #230) - awaiting @boxyhq/saml-jackson update | tar-fs (Dependabot #205) - awaiting upstream dependency updates | typeorm (Dependabot #223) - awaiting @boxyhq/saml-jackson update | systeminformation (Dependabot #241) - awaiting @opentelemetry/host-metrics update | qs (Dependabot #245) - awaiting googleapis-common and stripe updates | fast-xml-parser (Dependabot #270) - awaiting @boxyhq/saml-jackson update | diff (Dependabot #269) - awaiting @microsoft/api-extractor update"
|
||||
"overrides": "Security fixes for transitive dependencies. Remove when upstream packages update: axios (CVE-2025-58754) - awaiting @boxyhq/saml-jackson update | node-forge (Dependabot #230) - awaiting @boxyhq/saml-jackson update | tar-fs (Dependabot #205) - awaiting upstream dependency updates | typeorm (Dependabot #223) - awaiting @boxyhq/saml-jackson update | systeminformation (Dependabot #241) - awaiting @opentelemetry/host-metrics update | qs (Dependabot #245) - awaiting googleapis-common and stripe updates | preact (Dependabot #247) - awaiting next-auth update | fast-xml-parser (Dependabot #270) - awaiting @boxyhq/saml-jackson update | diff (Dependabot #269) - awaiting @microsoft/api-extractor update"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"next-auth@4.24.12": "patches/next-auth@4.24.12.patch"
|
||||
|
||||
@@ -62,7 +62,7 @@ export const removeEmptyImageAndVideoUrlsFromElements: MigrationScript = {
|
||||
delete cleanedWelcomeCard.videoUrl;
|
||||
}
|
||||
|
||||
const cleanedEndings = (survey.endings ?? []).map((ending) => {
|
||||
const cleanedEndings = survey.endings.map((ending) => {
|
||||
const cleanedEnding = { ...ending };
|
||||
if (cleanedEnding.imageUrl === "") {
|
||||
delete cleanedEnding.imageUrl;
|
||||
|
||||
@@ -13,7 +13,7 @@ export function ProgressBar({ survey, blockId }: ProgressBarProps) {
|
||||
[survey.blocks, blockId]
|
||||
);
|
||||
|
||||
const endingCardIds = useMemo(() => (survey.endings ?? []).map((ending) => ending.id), [survey.endings]);
|
||||
const endingCardIds = useMemo(() => survey.endings.map((ending) => ending.id), [survey.endings]);
|
||||
|
||||
const calculateProgress = useCallback(
|
||||
(blockIndex: number) => {
|
||||
|
||||
@@ -77,7 +77,7 @@ export function RenderSurvey(props: SurveyContainerProps) {
|
||||
close();
|
||||
}
|
||||
},
|
||||
(props.survey.endings?.length ?? 0) ? 3000 : 0 // close modal automatically after 3 seconds if no ending is enabled; otherwise, close immediately
|
||||
props.survey.endings.length ? 3000 : 0 // close modal automatically after 3 seconds if no ending is enabled; otherwise, close immediately
|
||||
);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -422,7 +422,7 @@ export function Survey({
|
||||
const evaluateLogicAndGetNextBlockId = (
|
||||
data: TResponseData
|
||||
): { nextBlockId: string | undefined; calculatedVariables: TResponseVariables } => {
|
||||
const firstEndingId = (survey.endings?.length ?? 0) > 0 ? survey.endings[0].id : undefined;
|
||||
const firstEndingId = survey.endings.length > 0 ? survey.endings[0].id : undefined;
|
||||
|
||||
if (blockId === "start")
|
||||
return { nextBlockId: localSurvey.blocks[0]?.id || firstEndingId, calculatedVariables: {} };
|
||||
@@ -657,7 +657,7 @@ export function Survey({
|
||||
setIsSurveyFinished(finished);
|
||||
|
||||
const endingId = nextBlockId
|
||||
? (localSurvey.endings ?? []).find((ending) => ending.id === nextBlockId)?.id
|
||||
? localSurvey.endings.find((ending) => ending.id === nextBlockId)?.id
|
||||
: undefined;
|
||||
|
||||
onChange(surveyResponseData);
|
||||
@@ -776,7 +776,7 @@ export function Survey({
|
||||
/>
|
||||
);
|
||||
} else if (blockIdx >= localSurvey.blocks.length) {
|
||||
const endingCard = (localSurvey.endings ?? []).find((ending) => {
|
||||
const endingCard = localSurvey.endings.find((ending) => {
|
||||
return ending.id === blockId;
|
||||
});
|
||||
if (endingCard) {
|
||||
|
||||
@@ -86,7 +86,7 @@ export function WelcomeCard({
|
||||
const calculateTimeToComplete = () => {
|
||||
const questions = getElementsFromSurveyBlocks(survey.blocks);
|
||||
let totalCards = questions.length;
|
||||
if ((survey.endings?.length ?? 0) > 0) totalCards += 1;
|
||||
if (survey.endings.length > 0) totalCards += 1;
|
||||
let idx = calculateElementIdx(survey, 0, totalCards);
|
||||
if (idx === 0.5) {
|
||||
idx = 1;
|
||||
|
||||
@@ -164,7 +164,7 @@ export function StackedCardsContainer({
|
||||
) : (
|
||||
blockIdxTemp !== undefined &&
|
||||
[prevBlockIdx, currentBlockIdx, nextBlockIdx, nextBlockIdx + 1].map((dynamicBlockIndex, index) => {
|
||||
const hasEndingCard = (survey.endings?.length ?? 0) > 0;
|
||||
const hasEndingCard = survey.endings.length > 0;
|
||||
// Check for hiding extra card
|
||||
if (dynamicBlockIndex > survey.blocks.length + (hasEndingCard ? 0 : -1)) return;
|
||||
const offset = index - 1;
|
||||
|
||||
@@ -87,7 +87,7 @@ export const calculateElementIdx = (
|
||||
const currentQuestion = questions[currentQustionIdx];
|
||||
const middleIdx = Math.floor(totalCards / 2);
|
||||
const possibleNextBlockIds = getPossibleNextBlocks(survey.blocks, currentQuestion);
|
||||
const endingCardIds = (survey.endings ?? []).map((ending) => ending.id);
|
||||
const endingCardIds = survey.endings.map((ending) => ending.id);
|
||||
|
||||
// Convert block IDs to element IDs (get first element of each block)
|
||||
const possibleNextQuestionIds = possibleNextBlockIds
|
||||
|
||||
160
pnpm-lock.yaml
generated
160
pnpm-lock.yaml
generated
@@ -12,6 +12,7 @@ overrides:
|
||||
typeorm: '>=0.3.26'
|
||||
systeminformation: 5.27.14
|
||||
qs: '>=6.14.1'
|
||||
preact: '>=10.26.10'
|
||||
fast-xml-parser: '>=5.3.4'
|
||||
diff: '>=8.0.3'
|
||||
|
||||
@@ -986,7 +987,7 @@ importers:
|
||||
specifier: 2.33.0
|
||||
version: 2.33.0
|
||||
preact:
|
||||
specifier: 10.28.2
|
||||
specifier: '>=10.26.10'
|
||||
version: 10.28.2
|
||||
react-i18next:
|
||||
specifier: 15.7.3
|
||||
@@ -3349,7 +3350,7 @@ packages:
|
||||
'@prefresh/core@1.5.9':
|
||||
resolution: {integrity: sha512-IKBKCPaz34OFVC+adiQ2qaTF5qdztO2/4ZPf4KsRTgjKosWqxVXmEbxCiUydYZRY8GVie+DQlKzQr9gt6HQ+EQ==}
|
||||
peerDependencies:
|
||||
preact: ^10.0.0 || ^11.0.0-0
|
||||
preact: '>=10.26.10'
|
||||
|
||||
'@prefresh/utils@1.2.1':
|
||||
resolution: {integrity: sha512-vq/sIuN5nYfYzvyayXI4C2QkprfNaHUQ9ZX+3xLD8nL3rWyzpxOm1+K7RtMbhd+66QcaISViK7amjnheQ/4WZw==}
|
||||
@@ -3357,7 +3358,7 @@ packages:
|
||||
'@prefresh/vite@2.4.11':
|
||||
resolution: {integrity: sha512-/XjURQqdRiCG3NpMmWqE9kJwrg9IchIOWHzulCfqg2sRe/8oQ1g5De7xrk9lbqPIQLn7ntBkKdqWXIj4E9YXyg==}
|
||||
peerDependencies:
|
||||
preact: ^10.4.0 || ^11.0.0-0
|
||||
preact: '>=10.26.10'
|
||||
vite: '>=2.0.0'
|
||||
|
||||
'@prisma/client@6.14.0':
|
||||
@@ -4540,8 +4541,8 @@ packages:
|
||||
resolution: {integrity: sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/core@3.22.0':
|
||||
resolution: {integrity: sha512-6vjCHD6vaY8KubeNw2Fg3EK0KLGQYdldG4fYgQmA0xSW0dJ8G2xFhSOdrlUakWVoP5JuWHtFODg3PNd/DN3FDA==}
|
||||
'@smithy/core@3.22.1':
|
||||
resolution: {integrity: sha512-x3ie6Crr58MWrm4viHqqy2Du2rHYZjwu8BekasrQx4ca+Y24dzVAwq3yErdqIbc2G3I0kLQA13PQ+/rde+u65g==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/credential-provider-imds@4.2.7':
|
||||
@@ -4628,16 +4629,16 @@ packages:
|
||||
resolution: {integrity: sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/middleware-endpoint@4.4.12':
|
||||
resolution: {integrity: sha512-9JMKHVJtW9RysTNjcBZQHDwB0p3iTP6B1IfQV4m+uCevkVd/VuLgwfqk5cnI4RHcp4cPwoIvxQqN4B1sxeHo8Q==}
|
||||
'@smithy/middleware-endpoint@4.4.13':
|
||||
resolution: {integrity: sha512-x6vn0PjYmGdNuKh/juUJJewZh7MoQ46jYaJ2mvekF4EesMuFfrl4LaW/k97Zjf8PTCPQmPgMvwewg7eNoH9n5w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/middleware-retry@4.4.17':
|
||||
resolution: {integrity: sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/middleware-retry@4.4.29':
|
||||
resolution: {integrity: sha512-bmTn75a4tmKRkC5w61yYQLb3DmxNzB8qSVu9SbTYqW6GAL0WXO2bDZuMAn/GJSbOdHEdjZvWxe+9Kk015bw6Cg==}
|
||||
'@smithy/middleware-retry@4.4.30':
|
||||
resolution: {integrity: sha512-CBGyFvN0f8hlnqKH/jckRDz78Snrp345+PVk8Ux7pnkUCW97Iinse59lY78hBt04h1GZ6hjBN94BRwZy1xC8Bg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/middleware-serde@4.2.8':
|
||||
@@ -4668,8 +4669,8 @@ packages:
|
||||
resolution: {integrity: sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/node-http-handler@4.4.8':
|
||||
resolution: {integrity: sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==}
|
||||
'@smithy/node-http-handler@4.4.9':
|
||||
resolution: {integrity: sha512-KX5Wml5mF+luxm1szW4QDz32e3NObgJ4Fyw+irhph4I/2geXwUy4jkIMUs5ZPGflRBeR6BUkC2wqIab4Llgm3w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/property-provider@4.2.7':
|
||||
@@ -4732,8 +4733,8 @@ packages:
|
||||
resolution: {integrity: sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/smithy-client@4.11.1':
|
||||
resolution: {integrity: sha512-SERgNg5Z1U+jfR6/2xPYjSEHY1t3pyTHC/Ma3YQl6qWtmiL42bvNId3W/oMUWIwu7ekL2FMPdqAmwbQegM7HeQ==}
|
||||
'@smithy/smithy-client@4.11.2':
|
||||
resolution: {integrity: sha512-SCkGmFak/xC1n7hKRsUr6wOnBTJ3L22Qd4e8H1fQIuKTAjntwgU8lrdMe7uHdiT2mJAOWA/60qaW9tiMu69n1A==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/types@4.11.0':
|
||||
@@ -4780,16 +4781,16 @@ packages:
|
||||
resolution: {integrity: sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/util-defaults-mode-browser@4.3.28':
|
||||
resolution: {integrity: sha512-/9zcatsCao9h6g18p/9vH9NIi5PSqhCkxQ/tb7pMgRFnqYp9XUOyOlGPDMHzr8n5ih6yYgwJEY2MLEobUgi47w==}
|
||||
'@smithy/util-defaults-mode-browser@4.3.29':
|
||||
resolution: {integrity: sha512-nIGy3DNRmOjaYaaKcQDzmWsro9uxlaqUOhZDHQed9MW/GmkBZPtnU70Pu1+GT9IBmUXwRdDuiyaeiy9Xtpn3+Q==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/util-defaults-mode-node@4.2.19':
|
||||
resolution: {integrity: sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/util-defaults-mode-node@4.2.31':
|
||||
resolution: {integrity: sha512-JTvoApUXA5kbpceI2vuqQzRjeTbLpx1eoa5R/YEZbTgtxvIB7AQZxFJ0SEyfCpgPCyVV9IT7we+ytSeIB3CyWA==}
|
||||
'@smithy/util-defaults-mode-node@4.2.32':
|
||||
resolution: {integrity: sha512-7dtFff6pu5fsjqrVve0YMhrnzJtccCWDacNKOkiZjJ++fmjGExmmSu341x+WU6Oc1IccL7lDuaUj7SfrHpWc5Q==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/util-endpoints@3.2.7':
|
||||
@@ -4820,8 +4821,8 @@ packages:
|
||||
resolution: {integrity: sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/util-stream@4.5.10':
|
||||
resolution: {integrity: sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==}
|
||||
'@smithy/util-stream@4.5.11':
|
||||
resolution: {integrity: sha512-lKmZ0S/3Qj2OF5H1+VzvDLb6kRxGzZHq6f3rAsoSu5cTLGsn3v3VQBA8czkNNXlLjoFEtVu3OQT2jEeOtOE2CA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@smithy/util-stream@4.5.8':
|
||||
@@ -5278,7 +5279,7 @@ packages:
|
||||
resolution: {integrity: sha512-F+kJ243LP6VmEK1M809unzTE/ijg+bsMNuiRN0JEDIJBELKKDNhdgC/WrUSZ7klwJvtlO3wQZ9ix+jhObG07Fg==}
|
||||
engines: {node: '>= 12'}
|
||||
peerDependencies:
|
||||
preact: '>=10 || ^10.0.0-alpha.0 || ^10.0.0-beta.0'
|
||||
preact: '>=10.26.10'
|
||||
|
||||
'@testing-library/react@16.3.0':
|
||||
resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==}
|
||||
@@ -9329,10 +9330,7 @@ packages:
|
||||
preact-render-to-string@5.2.6:
|
||||
resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==}
|
||||
peerDependencies:
|
||||
preact: '>=10'
|
||||
|
||||
preact@10.26.6:
|
||||
resolution: {integrity: sha512-5SRRBinwpwkaD+OqlBDeITlRgvd8I8QlxHJw9AxSdMNV6O+LodN9nUyYGpSF7sadHjs6RzeFShMexC6DbtWr9g==}
|
||||
preact: '>=10.26.10'
|
||||
|
||||
preact@10.28.2:
|
||||
resolution: {integrity: sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==}
|
||||
@@ -11454,7 +11452,7 @@ snapshots:
|
||||
'@aws-sdk/util-user-agent-browser': 3.969.0
|
||||
'@aws-sdk/util-user-agent-node': 3.971.0
|
||||
'@smithy/config-resolver': 4.4.6
|
||||
'@smithy/core': 3.22.0
|
||||
'@smithy/core': 3.22.1
|
||||
'@smithy/eventstream-serde-browser': 4.2.8
|
||||
'@smithy/eventstream-serde-config-resolver': 4.3.8
|
||||
'@smithy/eventstream-serde-node': 4.2.8
|
||||
@@ -11465,25 +11463,25 @@ snapshots:
|
||||
'@smithy/invalid-dependency': 4.2.8
|
||||
'@smithy/md5-js': 4.2.8
|
||||
'@smithy/middleware-content-length': 4.2.8
|
||||
'@smithy/middleware-endpoint': 4.4.12
|
||||
'@smithy/middleware-retry': 4.4.29
|
||||
'@smithy/middleware-endpoint': 4.4.13
|
||||
'@smithy/middleware-retry': 4.4.30
|
||||
'@smithy/middleware-serde': 4.2.9
|
||||
'@smithy/middleware-stack': 4.2.8
|
||||
'@smithy/node-config-provider': 4.3.8
|
||||
'@smithy/node-http-handler': 4.4.8
|
||||
'@smithy/node-http-handler': 4.4.9
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/smithy-client': 4.11.1
|
||||
'@smithy/smithy-client': 4.11.2
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/url-parser': 4.2.8
|
||||
'@smithy/util-base64': 4.3.0
|
||||
'@smithy/util-body-length-browser': 4.2.0
|
||||
'@smithy/util-body-length-node': 4.2.1
|
||||
'@smithy/util-defaults-mode-browser': 4.3.28
|
||||
'@smithy/util-defaults-mode-node': 4.2.31
|
||||
'@smithy/util-defaults-mode-browser': 4.3.29
|
||||
'@smithy/util-defaults-mode-node': 4.2.32
|
||||
'@smithy/util-endpoints': 3.2.8
|
||||
'@smithy/util-middleware': 4.2.8
|
||||
'@smithy/util-retry': 4.2.8
|
||||
'@smithy/util-stream': 4.5.10
|
||||
'@smithy/util-stream': 4.5.11
|
||||
'@smithy/util-utf8': 4.2.0
|
||||
'@smithy/util-waiter': 4.2.8
|
||||
tslib: 2.8.1
|
||||
@@ -11636,26 +11634,26 @@ snapshots:
|
||||
'@aws-sdk/util-user-agent-browser': 3.969.0
|
||||
'@aws-sdk/util-user-agent-node': 3.971.0
|
||||
'@smithy/config-resolver': 4.4.6
|
||||
'@smithy/core': 3.22.0
|
||||
'@smithy/core': 3.22.1
|
||||
'@smithy/fetch-http-handler': 5.3.9
|
||||
'@smithy/hash-node': 4.2.8
|
||||
'@smithy/invalid-dependency': 4.2.8
|
||||
'@smithy/middleware-content-length': 4.2.8
|
||||
'@smithy/middleware-endpoint': 4.4.12
|
||||
'@smithy/middleware-retry': 4.4.29
|
||||
'@smithy/middleware-endpoint': 4.4.13
|
||||
'@smithy/middleware-retry': 4.4.30
|
||||
'@smithy/middleware-serde': 4.2.9
|
||||
'@smithy/middleware-stack': 4.2.8
|
||||
'@smithy/node-config-provider': 4.3.8
|
||||
'@smithy/node-http-handler': 4.4.8
|
||||
'@smithy/node-http-handler': 4.4.9
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/smithy-client': 4.11.1
|
||||
'@smithy/smithy-client': 4.11.2
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/url-parser': 4.2.8
|
||||
'@smithy/util-base64': 4.3.0
|
||||
'@smithy/util-body-length-browser': 4.2.0
|
||||
'@smithy/util-body-length-node': 4.2.1
|
||||
'@smithy/util-defaults-mode-browser': 4.3.28
|
||||
'@smithy/util-defaults-mode-node': 4.2.31
|
||||
'@smithy/util-defaults-mode-browser': 4.3.29
|
||||
'@smithy/util-defaults-mode-node': 4.2.32
|
||||
'@smithy/util-endpoints': 3.2.8
|
||||
'@smithy/util-middleware': 4.2.8
|
||||
'@smithy/util-retry': 4.2.8
|
||||
@@ -11698,12 +11696,12 @@ snapshots:
|
||||
dependencies:
|
||||
'@aws-sdk/types': 3.969.0
|
||||
'@aws-sdk/xml-builder': 3.969.0
|
||||
'@smithy/core': 3.22.0
|
||||
'@smithy/core': 3.22.1
|
||||
'@smithy/node-config-provider': 4.3.8
|
||||
'@smithy/property-provider': 4.2.8
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/signature-v4': 5.3.8
|
||||
'@smithy/smithy-client': 4.11.1
|
||||
'@smithy/smithy-client': 4.11.2
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/util-base64': 4.3.0
|
||||
'@smithy/util-middleware': 4.2.8
|
||||
@@ -11780,12 +11778,12 @@ snapshots:
|
||||
'@aws-sdk/core': 3.970.0
|
||||
'@aws-sdk/types': 3.969.0
|
||||
'@smithy/fetch-http-handler': 5.3.9
|
||||
'@smithy/node-http-handler': 4.4.8
|
||||
'@smithy/node-http-handler': 4.4.9
|
||||
'@smithy/property-provider': 4.2.8
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/smithy-client': 4.11.1
|
||||
'@smithy/smithy-client': 4.11.2
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/util-stream': 4.5.10
|
||||
'@smithy/util-stream': 4.5.11
|
||||
tslib: 2.8.1
|
||||
|
||||
'@aws-sdk/credential-provider-ini@3.817.0':
|
||||
@@ -12090,7 +12088,7 @@ snapshots:
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/util-middleware': 4.2.8
|
||||
'@smithy/util-stream': 4.5.10
|
||||
'@smithy/util-stream': 4.5.11
|
||||
'@smithy/util-utf8': 4.2.0
|
||||
tslib: 2.8.1
|
||||
|
||||
@@ -12184,15 +12182,15 @@ snapshots:
|
||||
'@aws-sdk/core': 3.970.0
|
||||
'@aws-sdk/types': 3.969.0
|
||||
'@aws-sdk/util-arn-parser': 3.968.0
|
||||
'@smithy/core': 3.22.0
|
||||
'@smithy/core': 3.22.1
|
||||
'@smithy/node-config-provider': 4.3.8
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/signature-v4': 5.3.8
|
||||
'@smithy/smithy-client': 4.11.1
|
||||
'@smithy/smithy-client': 4.11.2
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/util-config-provider': 4.2.0
|
||||
'@smithy/util-middleware': 4.2.8
|
||||
'@smithy/util-stream': 4.5.10
|
||||
'@smithy/util-stream': 4.5.11
|
||||
'@smithy/util-utf8': 4.2.0
|
||||
tslib: 2.8.1
|
||||
|
||||
@@ -12227,7 +12225,7 @@ snapshots:
|
||||
'@aws-sdk/core': 3.970.0
|
||||
'@aws-sdk/types': 3.969.0
|
||||
'@aws-sdk/util-endpoints': 3.970.0
|
||||
'@smithy/core': 3.22.0
|
||||
'@smithy/core': 3.22.1
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/types': 4.12.0
|
||||
tslib: 2.8.1
|
||||
@@ -12333,26 +12331,26 @@ snapshots:
|
||||
'@aws-sdk/util-user-agent-browser': 3.969.0
|
||||
'@aws-sdk/util-user-agent-node': 3.971.0
|
||||
'@smithy/config-resolver': 4.4.6
|
||||
'@smithy/core': 3.22.0
|
||||
'@smithy/core': 3.22.1
|
||||
'@smithy/fetch-http-handler': 5.3.9
|
||||
'@smithy/hash-node': 4.2.8
|
||||
'@smithy/invalid-dependency': 4.2.8
|
||||
'@smithy/middleware-content-length': 4.2.8
|
||||
'@smithy/middleware-endpoint': 4.4.12
|
||||
'@smithy/middleware-retry': 4.4.29
|
||||
'@smithy/middleware-endpoint': 4.4.13
|
||||
'@smithy/middleware-retry': 4.4.30
|
||||
'@smithy/middleware-serde': 4.2.9
|
||||
'@smithy/middleware-stack': 4.2.8
|
||||
'@smithy/node-config-provider': 4.3.8
|
||||
'@smithy/node-http-handler': 4.4.8
|
||||
'@smithy/node-http-handler': 4.4.9
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/smithy-client': 4.11.1
|
||||
'@smithy/smithy-client': 4.11.2
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/url-parser': 4.2.8
|
||||
'@smithy/util-base64': 4.3.0
|
||||
'@smithy/util-body-length-browser': 4.2.0
|
||||
'@smithy/util-body-length-node': 4.2.1
|
||||
'@smithy/util-defaults-mode-browser': 4.3.28
|
||||
'@smithy/util-defaults-mode-node': 4.2.31
|
||||
'@smithy/util-defaults-mode-browser': 4.3.29
|
||||
'@smithy/util-defaults-mode-node': 4.2.32
|
||||
'@smithy/util-endpoints': 3.2.8
|
||||
'@smithy/util-middleware': 4.2.8
|
||||
'@smithy/util-retry': 4.2.8
|
||||
@@ -12391,7 +12389,7 @@ snapshots:
|
||||
'@aws-sdk/client-s3': 3.971.0
|
||||
'@aws-sdk/types': 3.969.0
|
||||
'@aws-sdk/util-format-url': 3.969.0
|
||||
'@smithy/middleware-endpoint': 4.4.12
|
||||
'@smithy/middleware-endpoint': 4.4.13
|
||||
'@smithy/signature-v4': 5.3.8
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/util-hex-encoding': 4.2.0
|
||||
@@ -12405,9 +12403,9 @@ snapshots:
|
||||
'@aws-sdk/signature-v4-multi-region': 3.970.0
|
||||
'@aws-sdk/types': 3.969.0
|
||||
'@aws-sdk/util-format-url': 3.969.0
|
||||
'@smithy/middleware-endpoint': 4.4.12
|
||||
'@smithy/middleware-endpoint': 4.4.13
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/smithy-client': 4.11.1
|
||||
'@smithy/smithy-client': 4.11.2
|
||||
'@smithy/types': 4.12.0
|
||||
tslib: 2.8.1
|
||||
|
||||
@@ -16007,7 +16005,7 @@ snapshots:
|
||||
'@smithy/uuid': 1.1.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@smithy/core@3.22.0':
|
||||
'@smithy/core@3.22.1':
|
||||
dependencies:
|
||||
'@smithy/middleware-serde': 4.2.9
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
@@ -16015,7 +16013,7 @@ snapshots:
|
||||
'@smithy/util-base64': 4.3.0
|
||||
'@smithy/util-body-length-browser': 4.2.0
|
||||
'@smithy/util-middleware': 4.2.8
|
||||
'@smithy/util-stream': 4.5.10
|
||||
'@smithy/util-stream': 4.5.11
|
||||
'@smithy/util-utf8': 4.2.0
|
||||
'@smithy/uuid': 1.1.0
|
||||
tslib: 2.8.1
|
||||
@@ -16156,9 +16154,9 @@ snapshots:
|
||||
'@smithy/util-middleware': 4.2.7
|
||||
tslib: 2.8.1
|
||||
|
||||
'@smithy/middleware-endpoint@4.4.12':
|
||||
'@smithy/middleware-endpoint@4.4.13':
|
||||
dependencies:
|
||||
'@smithy/core': 3.22.0
|
||||
'@smithy/core': 3.22.1
|
||||
'@smithy/middleware-serde': 4.2.9
|
||||
'@smithy/node-config-provider': 4.3.8
|
||||
'@smithy/shared-ini-file-loader': 4.4.3
|
||||
@@ -16179,12 +16177,12 @@ snapshots:
|
||||
'@smithy/uuid': 1.1.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@smithy/middleware-retry@4.4.29':
|
||||
'@smithy/middleware-retry@4.4.30':
|
||||
dependencies:
|
||||
'@smithy/node-config-provider': 4.3.8
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/service-error-classification': 4.2.8
|
||||
'@smithy/smithy-client': 4.11.1
|
||||
'@smithy/smithy-client': 4.11.2
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/util-middleware': 4.2.8
|
||||
'@smithy/util-retry': 4.2.8
|
||||
@@ -16235,7 +16233,7 @@ snapshots:
|
||||
'@smithy/types': 4.11.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@smithy/node-http-handler@4.4.8':
|
||||
'@smithy/node-http-handler@4.4.9':
|
||||
dependencies:
|
||||
'@smithy/abort-controller': 4.2.8
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
@@ -16335,14 +16333,14 @@ snapshots:
|
||||
'@smithy/util-stream': 4.5.8
|
||||
tslib: 2.8.1
|
||||
|
||||
'@smithy/smithy-client@4.11.1':
|
||||
'@smithy/smithy-client@4.11.2':
|
||||
dependencies:
|
||||
'@smithy/core': 3.22.0
|
||||
'@smithy/middleware-endpoint': 4.4.12
|
||||
'@smithy/core': 3.22.1
|
||||
'@smithy/middleware-endpoint': 4.4.13
|
||||
'@smithy/middleware-stack': 4.2.8
|
||||
'@smithy/protocol-http': 5.3.8
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/util-stream': 4.5.10
|
||||
'@smithy/util-stream': 4.5.11
|
||||
tslib: 2.8.1
|
||||
|
||||
'@smithy/types@4.11.0':
|
||||
@@ -16400,10 +16398,10 @@ snapshots:
|
||||
'@smithy/types': 4.11.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@smithy/util-defaults-mode-browser@4.3.28':
|
||||
'@smithy/util-defaults-mode-browser@4.3.29':
|
||||
dependencies:
|
||||
'@smithy/property-provider': 4.2.8
|
||||
'@smithy/smithy-client': 4.11.1
|
||||
'@smithy/smithy-client': 4.11.2
|
||||
'@smithy/types': 4.12.0
|
||||
tslib: 2.8.1
|
||||
|
||||
@@ -16417,13 +16415,13 @@ snapshots:
|
||||
'@smithy/types': 4.11.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@smithy/util-defaults-mode-node@4.2.31':
|
||||
'@smithy/util-defaults-mode-node@4.2.32':
|
||||
dependencies:
|
||||
'@smithy/config-resolver': 4.4.6
|
||||
'@smithy/credential-provider-imds': 4.2.8
|
||||
'@smithy/node-config-provider': 4.3.8
|
||||
'@smithy/property-provider': 4.2.8
|
||||
'@smithy/smithy-client': 4.11.1
|
||||
'@smithy/smithy-client': 4.11.2
|
||||
'@smithy/types': 4.12.0
|
||||
tslib: 2.8.1
|
||||
|
||||
@@ -16465,10 +16463,10 @@ snapshots:
|
||||
'@smithy/types': 4.12.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@smithy/util-stream@4.5.10':
|
||||
'@smithy/util-stream@4.5.11':
|
||||
dependencies:
|
||||
'@smithy/fetch-http-handler': 5.3.9
|
||||
'@smithy/node-http-handler': 4.4.8
|
||||
'@smithy/node-http-handler': 4.4.9
|
||||
'@smithy/types': 4.12.0
|
||||
'@smithy/util-base64': 4.3.0
|
||||
'@smithy/util-buffer-from': 4.2.0
|
||||
@@ -20936,8 +20934,8 @@ snapshots:
|
||||
next: 16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
oauth: 0.9.15
|
||||
openid-client: 5.7.1
|
||||
preact: 10.26.6
|
||||
preact-render-to-string: 5.2.6(preact@10.26.6)
|
||||
preact: 10.28.2
|
||||
preact-render-to-string: 5.2.6(preact@10.28.2)
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
uuid: 11.1.0
|
||||
@@ -21487,13 +21485,11 @@ snapshots:
|
||||
dependencies:
|
||||
xtend: 4.0.2
|
||||
|
||||
preact-render-to-string@5.2.6(preact@10.26.6):
|
||||
preact-render-to-string@5.2.6(preact@10.28.2):
|
||||
dependencies:
|
||||
preact: 10.26.6
|
||||
preact: 10.28.2
|
||||
pretty-format: 3.8.0
|
||||
|
||||
preact@10.26.6: {}
|
||||
|
||||
preact@10.28.2: {}
|
||||
|
||||
prebuild-install@7.1.3:
|
||||
|
||||
Reference in New Issue
Block a user