From c108cd4780637b568b34dcb0d327e7b5f8688c4f Mon Sep 17 00:00:00 2001 From: Anshuman Pandey <54475686+pandeymangg@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:42:30 +0530 Subject: [PATCH 01/19] fix: js command queue support for late initialization (#2974) --- apps/demo/pages/website/index.tsx | 1 + packages/js/src/method-queue.ts | 38 ------- packages/js/src/shared/load-formbricks.ts | 117 ++++++++-------------- turbo.json | 6 ++ 4 files changed, 51 insertions(+), 111 deletions(-) delete mode 100644 packages/js/src/method-queue.ts diff --git a/apps/demo/pages/website/index.tsx b/apps/demo/pages/website/index.tsx index 40ea72dac5..28a01898f6 100644 --- a/apps/demo/pages/website/index.tsx +++ b/apps/demo/pages/website/index.tsx @@ -128,6 +128,7 @@ const AppPage = ({}) => { }}> Reset +

If you made a change in Formbricks app and it does not seem to work, hit 'Reset' and try again. diff --git a/packages/js/src/method-queue.ts b/packages/js/src/method-queue.ts deleted file mode 100644 index a2fab1ce6d..0000000000 --- a/packages/js/src/method-queue.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Simple queue for formbricks methods - -export class MethodQueue { - private queue: (() => Promise)[] = []; - private isExecuting = false; - - add = (method: () => Promise): void => { - this.queue.push(method); - void this.run(); - }; - - private runNext = async (): Promise => { - if (this.isExecuting) return; - - const method = this.queue.shift(); - if (method) { - this.isExecuting = true; - try { - await method(); - } finally { - this.isExecuting = false; - if (this.queue.length > 0) { - void this.runNext(); - } - } - } - }; - - run = async (): Promise => { - if (!this.isExecuting && this.queue.length > 0) { - await this.runNext(); - } - }; - - clear = (): void => { - this.queue = []; - }; -} diff --git a/packages/js/src/shared/load-formbricks.ts b/packages/js/src/shared/load-formbricks.ts index 12b2cbd598..6e89e2b96a 100644 --- a/packages/js/src/shared/load-formbricks.ts +++ b/packages/js/src/shared/load-formbricks.ts @@ -1,26 +1,16 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access -- - * Required for dynamic function calls - */ - -/* eslint-disable @typescript-eslint/no-unsafe-call -- - * Required for dynamic function calls - */ - /* - eslint-disable no-console -- + eslint-disable no-console -- * Required for logging errors */ -import { type Result, wrapThrowsAsync } from "@formbricks/types/error-handlers"; -import { MethodQueue } from "../method-queue"; +import { type Result } from "@formbricks/types/error-handlers"; let isInitializing = false; let isInitialized = false; -const methodQueue = new MethodQueue(); // Load the SDK, return the result -const loadFormbricksSDK = async (apiHost: string, sdkType: "app" | "website"): Promise> => { +const loadFormbricksSDK = async (apiHostParam: string, sdkType: "app" | "website"): Promise> => { if (!window.formbricks) { - const res = await fetch(`${apiHost}/api/packages/${sdkType}`); + const res = await fetch(`${apiHostParam}/api/packages/${sdkType}`); // Failed to fetch the app package if (!res.ok) { @@ -63,81 +53,62 @@ const loadFormbricksSDK = async (apiHost: string, sdkType: "app" | "website"): P return { ok: true, data: undefined }; }; -// TODO: @pandeymangg - Fix these types -// type FormbricksAppMethods = { -// [K in keyof TFormbricksApp]: TFormbricksApp[K] extends Function ? K : never; -// }[keyof TFormbricksApp]; - -// type FormbricksWebsiteMethods = { -// [K in keyof TFormbricksWebsite]: TFormbricksWebsite[K] extends Function ? K : never; -// }[keyof TFormbricksWebsite]; +const functionsToProcess: { prop: string; args: unknown[] }[] = []; export const loadFormbricksToProxy = async ( prop: string, sdkType: "app" | "website", ...args: unknown[] - // eslint-disable-next-line @typescript-eslint/require-await -- Required for dynamic function calls ): Promise => { - const executeMethod = async (): Promise => { - try { - if (window.formbricks) { - // @ts-expect-error -- window.formbricks is a dynamic function - return (await window.formbricks[prop](...args)) as unknown; - } - } catch (error: unknown) { - console.error("🧱 Formbricks - Global error: ", error); - throw error; - } - }; - + // all of this should happen when not initialized: if (!isInitialized) { - if (isInitializing) { - methodQueue.add(executeMethod); - } else if (prop === "init") { + if (prop === "init") { + if (isInitializing) { + console.warn("🧱 Formbricks - Warning: Formbricks is already initializing."); + return; + } + + // reset the initialization state isInitializing = true; - const initialize = async (): Promise => { - const { apiHost } = args[0] as { apiHost: string }; - const loadSDKResult = (await wrapThrowsAsync(loadFormbricksSDK)(apiHost, sdkType)) as unknown as { - ok: boolean; - error: Error; - }; + const apiHost = (args[0] as { apiHost: string }).apiHost; + const loadSDKResult = await loadFormbricksSDK(apiHost, sdkType); + + if (loadSDKResult.ok) { + if (window.formbricks) { + // @ts-expect-error -- Required for dynamic function calls + void window.formbricks.init(...args); - if (!loadSDKResult.ok) { isInitializing = false; - console.error(`🧱 Formbricks - Global error: ${loadSDKResult.error.message}`); - return; - } + isInitialized = true; - try { - if (window.formbricks) { - // @ts-expect-error -- args is an array - await window.formbricks[prop](...args); - isInitialized = true; - isInitializing = false; + // process the queued functions + for (const { prop: functionProp, args: functionArgs } of functionsToProcess) { + type FormbricksProp = keyof typeof window.formbricks; + + if (typeof window.formbricks[functionProp as FormbricksProp] !== "function") { + console.error(`🧱 Formbricks - Error: Method ${functionProp} does not exist on formbricks`); + continue; + } + + // @ts-expect-error -- Required for dynamic function calls + (window.formbricks[functionProp] as unknown)(...functionArgs); } - } catch (error) { - isInitializing = false; - console.error("🧱 Formbricks - Global error: ", error); - throw error; } - }; - - methodQueue.add(initialize); + } } else { - console.error( - "🧱 Formbricks - Global error: You need to call formbricks.init before calling any other method" + console.warn( + "🧱 Formbricks - Warning: Formbricks not initialized. This method will be queued and executed after initialization." ); - } - } else { - // @ts-expect-error -- window.formbricks is a dynamic function - if (window.formbricks && typeof window.formbricks[prop] !== "function") { - console.error( - `🧱 Formbricks - Global error: Formbricks ${sdkType} SDK does not support method ${String(prop)}` - ); - return; - } - methodQueue.add(executeMethod); + functionsToProcess.push({ prop, args }); + } + } else if (window.formbricks) { + type Formbricks = typeof window.formbricks; + type FunctionProp = keyof Formbricks; + const functionPropTyped = prop as FunctionProp; + + // @ts-expect-error -- Required for dynamic function calls + await window.formbricks[functionPropTyped](...args); } }; diff --git a/turbo.json b/turbo.json index 1e854de355..c1c6d95cf6 100644 --- a/turbo.json +++ b/turbo.json @@ -47,6 +47,12 @@ "persistent": true, "dependsOn": ["@formbricks/api#build"] }, + "@formbricks/js#lint": { + "dependsOn": ["@formbricks/js-core#build"] + }, + "@formbricks/database#lint": { + "dependsOn": ["@formbricks/database#build"] + }, "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**"], From afe042ecfc0ab99f69c856b5e00017ea1ae55b19 Mon Sep 17 00:00:00 2001 From: Dhruwang Jariwala <67850763+Dhruwang@users.noreply.github.com> Date: Thu, 22 Aug 2024 14:25:44 +0530 Subject: [PATCH 02/19] fix: member invite issues (#3028) Co-authored-by: Matti Nannt --- apps/web/app/(app)/(onboarding)/organizations/actions.ts | 2 +- .../[environmentId]/surveys/[surveyId]/edit/actions.ts | 2 +- .../[environmentId]/(people)/attributes/actions.ts | 2 +- .../[environmentId]/(people)/people/[personId]/actions.ts | 2 +- .../[environmentId]/(people)/segments/actions.ts | 2 +- apps/web/app/(app)/environments/[environmentId]/actions.ts | 2 +- .../(app)/environments/[environmentId]/actions/actions.ts | 2 +- .../environments/[environmentId]/integrations/actions.ts | 2 +- .../[environmentId]/integrations/slack/actions.ts | 2 +- .../[environmentId]/integrations/webhooks/actions.ts | 2 +- .../(app)/environments/[environmentId]/product/actions.ts | 2 +- .../[environmentId]/product/api-keys/actions.ts | 2 +- .../environments/[environmentId]/product/general/actions.ts | 2 +- .../environments/[environmentId]/product/tags/actions.ts | 2 +- .../[environmentId]/settings/(account)/profile/actions.ts | 2 +- .../settings/(organization)/billing/actions.ts | 2 +- .../settings/(organization)/members/actions.ts | 6 +++--- .../surveys/[surveyId]/(analysis)/actions.ts | 2 +- .../surveys/[surveyId]/(analysis)/summary/actions.ts | 2 +- .../[environmentId]/surveys/[surveyId]/actions.ts | 2 +- .../app/api/v1/client/[environmentId]/responses/route.ts | 2 +- apps/web/app/s/[surveyId]/actions.ts | 2 +- apps/web/app/s/[surveyId]/page.tsx | 2 +- .../setup/organization/[organizationId]/invite/actions.ts | 2 +- apps/web/app/share/[sharingKey]/actions.ts | 2 +- packages/ee/advanced-targeting/lib/actions.ts | 2 +- packages/ee/multi-language/lib/actions.ts | 2 +- packages/ee/role-management/lib/actions.ts | 4 ++-- packages/lib/action/service.ts | 2 +- packages/lib/actionClass/auth.ts | 2 +- packages/lib/actionClass/service.ts | 2 +- packages/lib/apiKey/auth.ts | 2 +- packages/lib/apiKey/service.ts | 2 +- packages/lib/attribute/service.ts | 2 +- packages/lib/attributeClass/auth.ts | 2 +- packages/lib/attributeClass/service.ts | 2 +- packages/lib/display/service.ts | 2 +- packages/lib/environment/auth.ts | 2 +- packages/lib/environment/service.ts | 2 +- packages/lib/integration/auth.ts | 2 +- packages/lib/integration/service.ts | 2 +- packages/lib/language/service.ts | 2 +- packages/lib/organization/auth.ts | 2 +- packages/lib/organization/hooks/actions.ts | 2 +- packages/lib/organization/service.ts | 2 +- packages/lib/person/auth.ts | 2 +- packages/lib/person/service.ts | 2 +- packages/lib/product/auth.ts | 2 +- packages/lib/product/service.ts | 2 +- packages/lib/response/auth.ts | 2 +- packages/lib/response/service.ts | 2 +- packages/lib/responseNote/auth.ts | 2 +- packages/lib/responseNote/service.ts | 2 +- packages/lib/segment/service.ts | 2 +- packages/lib/storage/service.ts | 2 +- packages/lib/survey/auth.ts | 2 +- packages/lib/survey/service.ts | 3 ++- packages/lib/tag/auth.ts | 2 +- packages/lib/tag/service.ts | 2 +- packages/lib/tagOnResponse/auth.ts | 2 +- packages/lib/tagOnResponse/service.ts | 2 +- packages/lib/user/service.ts | 2 +- packages/lib/webhook/auth.ts | 2 +- packages/lib/webhook/service.ts | 2 +- packages/types/actions.ts | 2 +- packages/types/common.ts | 4 ++++ packages/types/environment.ts | 2 -- packages/types/responses.ts | 2 +- packages/types/surveys/types.ts | 3 +-- packages/ui/ShareSurveyLink/actions.ts | 2 +- packages/ui/SingleResponseCard/actions.ts | 2 +- packages/ui/SurveysList/actions.ts | 2 +- packages/ui/TemplateList/actions.ts | 2 +- 73 files changed, 79 insertions(+), 77 deletions(-) diff --git a/apps/web/app/(app)/(onboarding)/organizations/actions.ts b/apps/web/app/(app)/(onboarding)/organizations/actions.ts index 9d165e84e1..a3e3befaa8 100644 --- a/apps/web/app/(app)/(onboarding)/organizations/actions.ts +++ b/apps/web/app/(app)/(onboarding)/organizations/actions.ts @@ -6,7 +6,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { INVITE_DISABLED } from "@formbricks/lib/constants"; import { inviteUser } from "@formbricks/lib/invite/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { AuthenticationError } from "@formbricks/types/errors"; import { ZMembershipRole } from "@formbricks/types/memberships"; diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/actions.ts b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/actions.ts index c54d52cffb..47ed02dc8d 100644 --- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/actions.ts +++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/actions.ts @@ -21,7 +21,7 @@ import { import { surveyCache } from "@formbricks/lib/survey/cache"; import { loadNewSegmentInSurvey, updateSurvey } from "@formbricks/lib/survey/service"; import { ZActionClassInput } from "@formbricks/types/action-classes"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ZBaseFilters, ZSegmentFilters, ZSegmentUpdateInput } from "@formbricks/types/segment"; import { ZSurvey } from "@formbricks/types/surveys/types"; diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/actions.ts b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/actions.ts index 2ccdc320af..a9ebeacc9d 100644 --- a/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/(people)/attributes/actions.ts @@ -9,7 +9,7 @@ import { } from "@formbricks/lib/organization/utils"; import { getSegmentsByAttributeClassName } from "@formbricks/lib/segment/service"; import { ZAttributeClass } from "@formbricks/types/attribute-classes"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; const ZGetSegmentsByAttributeClassAction = z.object({ environmentId: ZId, diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/actions.ts b/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/actions.ts index 6174f60f57..071378add9 100644 --- a/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/(people)/people/[personId]/actions.ts @@ -5,7 +5,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromPersonId } from "@formbricks/lib/organization/utils"; import { deletePerson } from "@formbricks/lib/person/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; const ZPersonDeleteAction = z.object({ personId: ZId, diff --git a/apps/web/app/(app)/environments/[environmentId]/(people)/segments/actions.ts b/apps/web/app/(app)/environments/[environmentId]/(people)/segments/actions.ts index fac8a1012c..a998ef92fc 100644 --- a/apps/web/app/(app)/environments/[environmentId]/(people)/segments/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/(people)/segments/actions.ts @@ -5,7 +5,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromSegmentId } from "@formbricks/lib/organization/utils"; import { deleteSegment, updateSegment } from "@formbricks/lib/segment/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ZSegmentFilters, ZSegmentUpdateInput } from "@formbricks/types/segment"; const ZDeleteBasicSegmentAction = z.object({ diff --git a/apps/web/app/(app)/environments/[environmentId]/actions.ts b/apps/web/app/(app)/environments/[environmentId]/actions.ts index a5d1fc8a86..76e7b33e6c 100644 --- a/apps/web/app/(app)/environments/[environmentId]/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/actions.ts @@ -8,7 +8,7 @@ import { createMembership } from "@formbricks/lib/membership/service"; import { createOrganization } from "@formbricks/lib/organization/service"; import { createProduct } from "@formbricks/lib/product/service"; import { updateUser } from "@formbricks/lib/user/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { OperationNotAllowedError } from "@formbricks/types/errors"; import { ZProductUpdateInput } from "@formbricks/types/product"; import { TUserNotificationSettings } from "@formbricks/types/user"; diff --git a/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts b/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts index 953797f02e..3ff5965856 100644 --- a/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts @@ -7,7 +7,7 @@ import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromActionClassId } from "@formbricks/lib/organization/utils"; import { getSurveysByActionClassId } from "@formbricks/lib/survey/service"; import { ZActionClassInput } from "@formbricks/types/action-classes"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ResourceNotFoundError } from "@formbricks/types/errors"; const ZDeleteActionClassAction = z.object({ diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/actions.ts b/apps/web/app/(app)/environments/[environmentId]/integrations/actions.ts index a962579a6f..beeba66026 100644 --- a/apps/web/app/(app)/environments/[environmentId]/integrations/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/integrations/actions.ts @@ -5,7 +5,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { createOrUpdateIntegration, deleteIntegration } from "@formbricks/lib/integration/service"; import { getOrganizationIdFromEnvironmentId } from "@formbricks/lib/organization/utils"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ZIntegrationInput } from "@formbricks/types/integration"; const ZCreateOrUpdateIntegrationAction = z.object({ diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/slack/actions.ts b/apps/web/app/(app)/environments/[environmentId]/integrations/slack/actions.ts index 3bbbb6a732..b96fcb68e0 100644 --- a/apps/web/app/(app)/environments/[environmentId]/integrations/slack/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/integrations/slack/actions.ts @@ -5,7 +5,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromEnvironmentId } from "@formbricks/lib/organization/utils"; import { getSlackChannels } from "@formbricks/lib/slack/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; const ZRefreshChannelsAction = z.object({ environmentId: ZId, diff --git a/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/actions.ts b/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/actions.ts index 380ceaa288..4d0d0f1c40 100644 --- a/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/integrations/webhooks/actions.ts @@ -9,7 +9,7 @@ import { } from "@formbricks/lib/organization/utils"; import { createWebhook, deleteWebhook, updateWebhook } from "@formbricks/lib/webhook/service"; import { testEndpoint } from "@formbricks/lib/webhook/utils"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ZWebhookInput } from "@formbricks/types/webhooks"; const ZCreateWebhookAction = z.object({ diff --git a/apps/web/app/(app)/environments/[environmentId]/product/actions.ts b/apps/web/app/(app)/environments/[environmentId]/product/actions.ts index c9ec85c4c2..d610f773a7 100644 --- a/apps/web/app/(app)/environments/[environmentId]/product/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/product/actions.ts @@ -5,7 +5,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromProductId } from "@formbricks/lib/organization/utils"; import { updateProduct } from "@formbricks/lib/product/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ZProductUpdateInput } from "@formbricks/types/product"; const ZUpdateProductAction = z.object({ diff --git a/apps/web/app/(app)/environments/[environmentId]/product/api-keys/actions.ts b/apps/web/app/(app)/environments/[environmentId]/product/api-keys/actions.ts index 0ee2a1c848..225f5785ec 100644 --- a/apps/web/app/(app)/environments/[environmentId]/product/api-keys/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/product/api-keys/actions.ts @@ -9,7 +9,7 @@ import { getOrganizationIdFromEnvironmentId, } from "@formbricks/lib/organization/utils"; import { ZApiKeyCreateInput } from "@formbricks/types/api-keys"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; const ZDeleteApiKeyAction = z.object({ id: ZId, diff --git a/apps/web/app/(app)/environments/[environmentId]/product/general/actions.ts b/apps/web/app/(app)/environments/[environmentId]/product/general/actions.ts index 75818d75a3..712518245d 100644 --- a/apps/web/app/(app)/environments/[environmentId]/product/general/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/product/general/actions.ts @@ -5,7 +5,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromProductId } from "@formbricks/lib/organization/utils"; import { deleteProduct, getProducts } from "@formbricks/lib/product/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; const ZProductDeleteAction = z.object({ productId: ZId, diff --git a/apps/web/app/(app)/environments/[environmentId]/product/tags/actions.ts b/apps/web/app/(app)/environments/[environmentId]/product/tags/actions.ts index a3b821a9f5..cb37fa90da 100644 --- a/apps/web/app/(app)/environments/[environmentId]/product/tags/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/product/tags/actions.ts @@ -5,7 +5,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromTagId } from "@formbricks/lib/organization/utils"; import { deleteTag, mergeTags, updateTagName } from "@formbricks/lib/tag/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; const ZDeleteTagAction = z.object({ tagId: ZId, diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/profile/actions.ts b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/profile/actions.ts index c6ea517f46..9fe04d1d0f 100644 --- a/apps/web/app/(app)/environments/[environmentId]/settings/(account)/profile/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/settings/(account)/profile/actions.ts @@ -8,7 +8,7 @@ import { getOrganizationIdFromEnvironmentId } from "@formbricks/lib/organization import { deleteFile } from "@formbricks/lib/storage/service"; import { getFileNameWithIdFromUrl } from "@formbricks/lib/storage/utils"; import { updateUser } from "@formbricks/lib/user/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ZUserUpdateInput } from "@formbricks/types/user"; export const updateUserAction = authenticatedActionClient diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/billing/actions.ts b/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/billing/actions.ts index 980fe8d3bb..14bdc9d988 100644 --- a/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/billing/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/billing/actions.ts @@ -9,7 +9,7 @@ import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { STRIPE_PRICE_LOOKUP_KEYS } from "@formbricks/lib/constants"; import { WEBAPP_URL } from "@formbricks/lib/constants"; import { getOrganization } from "@formbricks/lib/organization/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { AuthorizationError, ResourceNotFoundError } from "@formbricks/types/errors"; const ZUpgradePlanAction = z.object({ diff --git a/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/members/actions.ts b/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/members/actions.ts index a737a9ebb8..d1e7f29e80 100644 --- a/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/members/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/settings/(organization)/members/actions.ts @@ -15,7 +15,7 @@ import { } from "@formbricks/lib/membership/service"; import { deleteOrganization, updateOrganization } from "@formbricks/lib/organization/service"; import { getOrganizationIdFromInviteId } from "@formbricks/lib/organization/utils"; -import { ZId } from "@formbricks/types/environment"; +import { ZId, ZUuid } from "@formbricks/types/common"; import { AuthenticationError, OperationNotAllowedError, ValidationError } from "@formbricks/types/errors"; import { ZMembershipRole } from "@formbricks/types/memberships"; import { ZOrganizationUpdateInput } from "@formbricks/types/organizations"; @@ -39,7 +39,7 @@ export const updateOrganizationNameAction = authenticatedActionClient }); const ZDeleteInviteAction = z.object({ - inviteId: ZId, + inviteId: ZUuid, organizationId: ZId, }); @@ -130,7 +130,7 @@ export const createInviteTokenAction = authenticatedActionClient }); const ZResendInviteAction = z.object({ - inviteId: ZId, + inviteId: ZUuid, organizationId: ZId, }); diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions.ts index 05095a3ae4..6edf88643d 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions.ts @@ -6,7 +6,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromSurveyId } from "@formbricks/lib/organization/utils"; import { getResponseCountBySurveyId, getResponses, getSurveySummary } from "@formbricks/lib/response/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ZResponseFilterCriteria } from "@formbricks/types/responses"; export const revalidateSurveyIdPath = async (environmentId: string, surveyId: string) => { diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/actions.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/actions.ts index 1b14aa6a31..7c1b0b4f19 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/actions.ts @@ -8,7 +8,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromSurveyId } from "@formbricks/lib/organization/utils"; import { getSurvey, updateSurvey } from "@formbricks/lib/survey/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ResourceNotFoundError } from "@formbricks/types/errors"; const ZSendEmbedSurveyPreviewEmailAction = z.object({ diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/actions.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/actions.ts index fd4952bd72..9bbe3a2c9a 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/actions.ts @@ -7,7 +7,7 @@ import { getOrganizationIdFromSurveyId } from "@formbricks/lib/organization/util import { getResponseDownloadUrl, getResponseFilteringValues } from "@formbricks/lib/response/service"; import { getSurvey, updateSurvey } from "@formbricks/lib/survey/service"; import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ResourceNotFoundError } from "@formbricks/types/errors"; import { ZResponseFilterCriteria } from "@formbricks/types/responses"; import { ZSurvey } from "@formbricks/types/surveys/types"; diff --git a/apps/web/app/api/v1/client/[environmentId]/responses/route.ts b/apps/web/app/api/v1/client/[environmentId]/responses/route.ts index ffd086f65e..b619a06dae 100644 --- a/apps/web/app/api/v1/client/[environmentId]/responses/route.ts +++ b/apps/web/app/api/v1/client/[environmentId]/responses/route.ts @@ -7,7 +7,7 @@ import { getPerson } from "@formbricks/lib/person/service"; import { capturePosthogEnvironmentEvent } from "@formbricks/lib/posthogServer"; import { createResponse } from "@formbricks/lib/response/service"; import { getSurvey } from "@formbricks/lib/survey/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { InvalidInputError } from "@formbricks/types/errors"; import { TResponse, TResponseInput, ZResponseInput } from "@formbricks/types/responses"; diff --git a/apps/web/app/s/[surveyId]/actions.ts b/apps/web/app/s/[surveyId]/actions.ts index e5258ef046..0825735348 100644 --- a/apps/web/app/s/[surveyId]/actions.ts +++ b/apps/web/app/s/[surveyId]/actions.ts @@ -6,8 +6,8 @@ import { sendLinkSurveyToVerifiedEmail } from "@formbricks/email"; import { actionClient } from "@formbricks/lib/actionClient"; import { verifyTokenForLinkSurvey } from "@formbricks/lib/jwt"; import { getSurvey } from "@formbricks/lib/survey/service"; +import { ZId } from "@formbricks/types/common"; import { ZLinkSurveyEmailData } from "@formbricks/types/email"; -import { ZId } from "@formbricks/types/environment"; export const sendLinkSurveyEmailAction = actionClient .schema(ZLinkSurveyEmailData) diff --git a/apps/web/app/s/[surveyId]/page.tsx b/apps/web/app/s/[surveyId]/page.tsx index 5b2cff989f..2ebf817c83 100644 --- a/apps/web/app/s/[surveyId]/page.tsx +++ b/apps/web/app/s/[surveyId]/page.tsx @@ -13,7 +13,7 @@ import { createPerson, getPersonByUserId } from "@formbricks/lib/person/service" import { getProductByEnvironmentId } from "@formbricks/lib/product/service"; import { getResponseBySingleUseId, getResponseCountBySurveyId } from "@formbricks/lib/response/service"; import { getSurvey } from "@formbricks/lib/survey/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { TResponse } from "@formbricks/types/responses"; import { getEmailVerificationDetails } from "./lib/helpers"; diff --git a/apps/web/app/setup/organization/[organizationId]/invite/actions.ts b/apps/web/app/setup/organization/[organizationId]/invite/actions.ts index ac869c07c3..c7bb5e6fee 100644 --- a/apps/web/app/setup/organization/[organizationId]/invite/actions.ts +++ b/apps/web/app/setup/organization/[organizationId]/invite/actions.ts @@ -7,7 +7,7 @@ import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { INVITE_DISABLED } from "@formbricks/lib/constants"; import { inviteUser } from "@formbricks/lib/invite/service"; import { getOrganizationsByUserId } from "@formbricks/lib/organization/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { AuthenticationError } from "@formbricks/types/errors"; const ZInviteOrganizationMemberAction = z.object({ diff --git a/apps/web/app/share/[sharingKey]/actions.ts b/apps/web/app/share/[sharingKey]/actions.ts index fdbe5e2a60..1d9add65a7 100644 --- a/apps/web/app/share/[sharingKey]/actions.ts +++ b/apps/web/app/share/[sharingKey]/actions.ts @@ -10,7 +10,7 @@ import { } from "@formbricks/lib/response/service"; import { getSurveyIdByResultShareKey } from "@formbricks/lib/survey/service"; import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { AuthorizationError } from "@formbricks/types/errors"; import { ZResponseFilterCriteria } from "@formbricks/types/responses"; diff --git a/packages/ee/advanced-targeting/lib/actions.ts b/packages/ee/advanced-targeting/lib/actions.ts index db571b6cfa..2366330b0d 100644 --- a/packages/ee/advanced-targeting/lib/actions.ts +++ b/packages/ee/advanced-targeting/lib/actions.ts @@ -16,7 +16,7 @@ import { updateSegment, } from "@formbricks/lib/segment/service"; import { loadNewSegmentInSurvey } from "@formbricks/lib/survey/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ZSegmentCreateInput, ZSegmentFilters, ZSegmentUpdateInput } from "@formbricks/types/segment"; export const createSegmentAction = authenticatedActionClient diff --git a/packages/ee/multi-language/lib/actions.ts b/packages/ee/multi-language/lib/actions.ts index a22fd16054..9455aec55d 100644 --- a/packages/ee/multi-language/lib/actions.ts +++ b/packages/ee/multi-language/lib/actions.ts @@ -14,7 +14,7 @@ import { getOrganizationIdFromLanguageId, getOrganizationIdFromProductId, } from "@formbricks/lib/organization/utils"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ResourceNotFoundError } from "@formbricks/types/errors"; import { ZLanguageInput } from "@formbricks/types/product"; diff --git a/packages/ee/role-management/lib/actions.ts b/packages/ee/role-management/lib/actions.ts index dbcfd4910f..a3c32b7968 100644 --- a/packages/ee/role-management/lib/actions.ts +++ b/packages/ee/role-management/lib/actions.ts @@ -10,7 +10,7 @@ import { transferOwnership, updateMembership, } from "@formbricks/lib/membership/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId, ZUuid } from "@formbricks/types/common"; import { AuthorizationError, ValidationError } from "@formbricks/types/errors"; import { ZInviteUpdateInput } from "@formbricks/types/invites"; import { ZMembershipUpdateInput } from "@formbricks/types/memberships"; @@ -44,7 +44,7 @@ export const transferOwnershipAction = authenticatedActionClient }); const ZUpdateInviteAction = z.object({ - inviteId: ZId, + inviteId: ZUuid, organizationId: ZId, data: ZInviteUpdateInput, }); diff --git a/packages/lib/action/service.ts b/packages/lib/action/service.ts index 81393cb7c2..ef594b3468 100644 --- a/packages/lib/action/service.ts +++ b/packages/lib/action/service.ts @@ -5,7 +5,7 @@ import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { TAction, TActionInput, ZActionInput } from "@formbricks/types/actions"; import { ZOptionalNumber } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, OperationNotAllowedError } from "@formbricks/types/errors"; import { actionClassCache } from "../actionClass/cache"; import { getActionClassByEnvironmentIdAndName } from "../actionClass/service"; diff --git a/packages/lib/actionClass/auth.ts b/packages/lib/actionClass/auth.ts index 0be6a21859..358311f85e 100644 --- a/packages/lib/actionClass/auth.ts +++ b/packages/lib/actionClass/auth.ts @@ -1,5 +1,5 @@ import "server-only"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { hasUserEnvironmentAccess } from "../environment/auth"; import { getMembershipByUserIdOrganizationId } from "../membership/service"; diff --git a/packages/lib/actionClass/service.ts b/packages/lib/actionClass/service.ts index d69bfbe60a..c87b09795c 100644 --- a/packages/lib/actionClass/service.ts +++ b/packages/lib/actionClass/service.ts @@ -6,7 +6,7 @@ import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { TActionClass, TActionClassInput, ZActionClassInput } from "@formbricks/types/action-classes"; import { ZOptionalNumber, ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors"; import { cache } from "../cache"; import { ITEMS_PER_PAGE } from "../constants"; diff --git a/packages/lib/apiKey/auth.ts b/packages/lib/apiKey/auth.ts index 40ed5ace37..89b525c12f 100644 --- a/packages/lib/apiKey/auth.ts +++ b/packages/lib/apiKey/auth.ts @@ -1,5 +1,5 @@ import "server-only"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { hasUserEnvironmentAccess } from "../environment/auth"; import { validateInputs } from "../utils/validate"; diff --git a/packages/lib/apiKey/service.ts b/packages/lib/apiKey/service.ts index 31dd253aaf..285430e90f 100644 --- a/packages/lib/apiKey/service.ts +++ b/packages/lib/apiKey/service.ts @@ -5,7 +5,7 @@ import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { TApiKey, TApiKeyCreateInput, ZApiKeyCreateInput } from "@formbricks/types/api-keys"; import { ZOptionalNumber, ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, InvalidInputError } from "@formbricks/types/errors"; import { cache } from "../cache"; import { ITEMS_PER_PAGE } from "../constants"; diff --git a/packages/lib/attribute/service.ts b/packages/lib/attribute/service.ts index 6d0961f0d4..3f6a15fd86 100644 --- a/packages/lib/attribute/service.ts +++ b/packages/lib/attribute/service.ts @@ -4,7 +4,7 @@ import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { TAttributes, ZAttributes } from "@formbricks/types/attributes"; import { ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, OperationNotAllowedError } from "@formbricks/types/errors"; import { attributeCache } from "../attribute/cache"; import { attributeClassCache } from "../attributeClass/cache"; diff --git a/packages/lib/attributeClass/auth.ts b/packages/lib/attributeClass/auth.ts index f25c2e5cba..78e8bb1631 100644 --- a/packages/lib/attributeClass/auth.ts +++ b/packages/lib/attributeClass/auth.ts @@ -1,5 +1,5 @@ import "server-only"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { hasUserEnvironmentAccess } from "../environment/auth"; import { validateInputs } from "../utils/validate"; diff --git a/packages/lib/attributeClass/service.ts b/packages/lib/attributeClass/service.ts index e464245384..1317349c96 100644 --- a/packages/lib/attributeClass/service.ts +++ b/packages/lib/attributeClass/service.ts @@ -12,7 +12,7 @@ import { ZAttributeClassUpdateInput, } from "@formbricks/types/attribute-classes"; import { ZOptionalNumber, ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, OperationNotAllowedError } from "@formbricks/types/errors"; import { cache } from "../cache"; import { ITEMS_PER_PAGE, MAX_ATTRIBUTE_CLASSES_PER_ENVIRONMENT } from "../constants"; diff --git a/packages/lib/display/service.ts b/packages/lib/display/service.ts index 95c3a0b807..a925bf61a0 100644 --- a/packages/lib/display/service.ts +++ b/packages/lib/display/service.ts @@ -3,6 +3,7 @@ import { Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { ZOptionalNumber } from "@formbricks/types/common"; +import { ZId } from "@formbricks/types/common"; import { TDisplay, TDisplayCreateInput, @@ -11,7 +12,6 @@ import { ZDisplayCreateInput, ZDisplayUpdateInput, } from "@formbricks/types/displays"; -import { ZId } from "@formbricks/types/environment"; import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors"; import { TPerson } from "@formbricks/types/people"; import { cache } from "../cache"; diff --git a/packages/lib/environment/auth.ts b/packages/lib/environment/auth.ts index 7bee822adb..8ae4a2b371 100644 --- a/packages/lib/environment/auth.ts +++ b/packages/lib/environment/auth.ts @@ -1,6 +1,6 @@ import { Prisma } from "@prisma/client"; import { prisma } from "@formbricks/database"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError } from "@formbricks/types/errors"; import { cache } from "../cache"; import { organizationCache } from "../organization/cache"; diff --git a/packages/lib/environment/service.ts b/packages/lib/environment/service.ts index 201e8e8f2c..8a07284748 100644 --- a/packages/lib/environment/service.ts +++ b/packages/lib/environment/service.ts @@ -3,6 +3,7 @@ import { Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; import { z } from "zod"; import { prisma } from "@formbricks/database"; +import { ZId } from "@formbricks/types/common"; import type { TEnvironment, TEnvironmentCreateInput, @@ -12,7 +13,6 @@ import { ZEnvironment, ZEnvironmentCreateInput, ZEnvironmentUpdateInput, - ZId, } from "@formbricks/types/environment"; import { DatabaseError, ResourceNotFoundError, ValidationError } from "@formbricks/types/errors"; import { cache } from "../cache"; diff --git a/packages/lib/integration/auth.ts b/packages/lib/integration/auth.ts index 5183113ead..852e7f6b34 100644 --- a/packages/lib/integration/auth.ts +++ b/packages/lib/integration/auth.ts @@ -1,5 +1,5 @@ import "server-only"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { hasUserEnvironmentAccess } from "../environment/auth"; import { validateInputs } from "../utils/validate"; diff --git a/packages/lib/integration/service.ts b/packages/lib/integration/service.ts index 2e6cc7198a..a5f06e1135 100644 --- a/packages/lib/integration/service.ts +++ b/packages/lib/integration/service.ts @@ -3,7 +3,7 @@ import { Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { ZOptionalNumber, ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError } from "@formbricks/types/errors"; import { TIntegration, TIntegrationInput, ZIntegrationType } from "@formbricks/types/integration"; import { cache } from "../cache"; diff --git a/packages/lib/language/service.ts b/packages/lib/language/service.ts index 2fedf490bb..081dd689ca 100644 --- a/packages/lib/language/service.ts +++ b/packages/lib/language/service.ts @@ -1,7 +1,7 @@ import { Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, ResourceNotFoundError, ValidationError } from "@formbricks/types/errors"; import { TLanguage, diff --git a/packages/lib/organization/auth.ts b/packages/lib/organization/auth.ts index da227de64a..6cb7731057 100644 --- a/packages/lib/organization/auth.ts +++ b/packages/lib/organization/auth.ts @@ -1,5 +1,5 @@ import "server-only"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { getMembershipByUserIdOrganizationId } from "../membership/service"; import { getAccessFlags } from "../membership/utils"; diff --git a/packages/lib/organization/hooks/actions.ts b/packages/lib/organization/hooks/actions.ts index 63db66fb14..f2aac75d3e 100644 --- a/packages/lib/organization/hooks/actions.ts +++ b/packages/lib/organization/hooks/actions.ts @@ -2,7 +2,7 @@ import "server-only"; import { z } from "zod"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { authenticatedActionClient } from "../../actionClient"; import { checkAuthorization } from "../../actionClient/utils"; import { getOrganization } from "../service"; diff --git a/packages/lib/organization/service.ts b/packages/lib/organization/service.ts index 72da377f94..6ef87ff5b1 100644 --- a/packages/lib/organization/service.ts +++ b/packages/lib/organization/service.ts @@ -3,7 +3,7 @@ import { Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { ZOptionalNumber, ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors"; import { TOrganization, diff --git a/packages/lib/person/auth.ts b/packages/lib/person/auth.ts index eee3de32b6..2dc7ebbbbe 100644 --- a/packages/lib/person/auth.ts +++ b/packages/lib/person/auth.ts @@ -1,5 +1,5 @@ import "server-only"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { hasUserEnvironmentAccess } from "../environment/auth"; import { validateInputs } from "../utils/validate"; diff --git a/packages/lib/person/service.ts b/packages/lib/person/service.ts index 0666068944..3ad71cf4d5 100644 --- a/packages/lib/person/service.ts +++ b/packages/lib/person/service.ts @@ -3,7 +3,7 @@ import { Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { ZOptionalNumber, ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError } from "@formbricks/types/errors"; import { TPerson } from "@formbricks/types/people"; import { cache } from "../cache"; diff --git a/packages/lib/product/auth.ts b/packages/lib/product/auth.ts index ab9926f4f1..7d4212d895 100644 --- a/packages/lib/product/auth.ts +++ b/packages/lib/product/auth.ts @@ -1,4 +1,4 @@ -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { getMembershipByUserIdOrganizationId } from "../membership/service"; import { getAccessFlags } from "../membership/utils"; diff --git a/packages/lib/product/service.ts b/packages/lib/product/service.ts index 7bc988f087..de43d89ec4 100644 --- a/packages/lib/product/service.ts +++ b/packages/lib/product/service.ts @@ -4,7 +4,7 @@ import { cache as reactCache } from "react"; import { z } from "zod"; import { prisma } from "@formbricks/database"; import { ZOptionalNumber, ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, ValidationError } from "@formbricks/types/errors"; import type { TProduct, TProductUpdateInput } from "@formbricks/types/product"; import { ZProduct, ZProductUpdateInput } from "@formbricks/types/product"; diff --git a/packages/lib/response/auth.ts b/packages/lib/response/auth.ts index a5f7122f0e..689c3c64af 100644 --- a/packages/lib/response/auth.ts +++ b/packages/lib/response/auth.ts @@ -1,5 +1,5 @@ import "server-only"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { hasUserEnvironmentAccess } from "../environment/auth"; import { getSurvey } from "../survey/service"; diff --git a/packages/lib/response/service.ts b/packages/lib/response/service.ts index 6dabaadbae..bed519ebd6 100644 --- a/packages/lib/response/service.ts +++ b/packages/lib/response/service.ts @@ -4,7 +4,7 @@ import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { TAttributes } from "@formbricks/types/attributes"; import { ZOptionalNumber, ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors"; import { TPerson } from "@formbricks/types/people"; import { diff --git a/packages/lib/responseNote/auth.ts b/packages/lib/responseNote/auth.ts index 7ad756aa5e..289c162ca6 100644 --- a/packages/lib/responseNote/auth.ts +++ b/packages/lib/responseNote/auth.ts @@ -1,4 +1,4 @@ -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { canUserAccessResponse } from "../response/auth"; import { getResponse } from "../response/service"; diff --git a/packages/lib/responseNote/service.ts b/packages/lib/responseNote/service.ts index 4afb548663..8882777c0a 100644 --- a/packages/lib/responseNote/service.ts +++ b/packages/lib/responseNote/service.ts @@ -3,7 +3,7 @@ import { Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors"; import { TResponseNote } from "@formbricks/types/responses"; import { cache } from "../cache"; diff --git a/packages/lib/segment/service.ts b/packages/lib/segment/service.ts index a7f0996596..ac77666a23 100644 --- a/packages/lib/segment/service.ts +++ b/packages/lib/segment/service.ts @@ -2,7 +2,7 @@ import { Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, OperationNotAllowedError, diff --git a/packages/lib/storage/service.ts b/packages/lib/storage/service.ts index 461efceec3..510584065f 100644 --- a/packages/lib/storage/service.ts +++ b/packages/lib/storage/service.ts @@ -20,12 +20,12 @@ import { S3_ACCESS_KEY, S3_BUCKET_NAME, S3_ENDPOINT_URL, + S3_FORCE_PATH_STYLE, S3_REGION, S3_SECRET_KEY, UPLOADS_DIR, WEBAPP_URL, isS3Configured, - S3_FORCE_PATH_STYLE, } from "../constants"; import { generateLocalSignedUrl } from "../crypto"; import { env } from "../env"; diff --git a/packages/lib/survey/auth.ts b/packages/lib/survey/auth.ts index 951da37ea3..810573a8a7 100644 --- a/packages/lib/survey/auth.ts +++ b/packages/lib/survey/auth.ts @@ -1,4 +1,4 @@ -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { hasUserEnvironmentAccess } from "../environment/auth"; import { getMembershipByUserIdOrganizationId } from "../membership/service"; diff --git a/packages/lib/survey/service.ts b/packages/lib/survey/service.ts index 2078f08ecb..77cbc3dba6 100644 --- a/packages/lib/survey/service.ts +++ b/packages/lib/survey/service.ts @@ -5,7 +5,8 @@ import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { TActionClass } from "@formbricks/types/action-classes"; import { ZOptionalNumber } from "@formbricks/types/common"; -import { TEnvironment, ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; +import { TEnvironment } from "@formbricks/types/environment"; import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors"; import { TPerson } from "@formbricks/types/people"; import { TProduct } from "@formbricks/types/product"; diff --git a/packages/lib/tag/auth.ts b/packages/lib/tag/auth.ts index f2b9b06d9e..1e75bd7d58 100644 --- a/packages/lib/tag/auth.ts +++ b/packages/lib/tag/auth.ts @@ -1,5 +1,5 @@ import "server-only"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { hasUserEnvironmentAccess } from "../environment/auth"; import { getMembershipByUserIdOrganizationId } from "../membership/service"; import { getAccessFlags } from "../membership/utils"; diff --git a/packages/lib/tag/service.ts b/packages/lib/tag/service.ts index 44a9351851..df6ff44175 100644 --- a/packages/lib/tag/service.ts +++ b/packages/lib/tag/service.ts @@ -2,7 +2,7 @@ import "server-only"; import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; import { ZOptionalNumber, ZString } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { TTag } from "@formbricks/types/tags"; import { cache } from "../cache"; import { ITEMS_PER_PAGE } from "../constants"; diff --git a/packages/lib/tagOnResponse/auth.ts b/packages/lib/tagOnResponse/auth.ts index 67e8cb3cf7..15be2c7ddf 100644 --- a/packages/lib/tagOnResponse/auth.ts +++ b/packages/lib/tagOnResponse/auth.ts @@ -1,5 +1,5 @@ import "server-only"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { getMembershipByUserIdOrganizationId } from "../membership/service"; import { getAccessFlags } from "../membership/utils"; diff --git a/packages/lib/tagOnResponse/service.ts b/packages/lib/tagOnResponse/service.ts index 7db0a254ae..3406543ee0 100644 --- a/packages/lib/tagOnResponse/service.ts +++ b/packages/lib/tagOnResponse/service.ts @@ -2,7 +2,7 @@ import "server-only"; import { Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; import { prisma } from "@formbricks/database"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError } from "@formbricks/types/errors"; import { TTagsCount, TTagsOnResponses } from "@formbricks/types/tags"; import { cache } from "../cache"; diff --git a/packages/lib/user/service.ts b/packages/lib/user/service.ts index 5b8adcbdf0..241a8b8154 100644 --- a/packages/lib/user/service.ts +++ b/packages/lib/user/service.ts @@ -3,7 +3,7 @@ import { Prisma } from "@prisma/client"; import { cache as reactCache } from "react"; import { z } from "zod"; import { prisma } from "@formbricks/database"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors"; import { TMembership } from "@formbricks/types/memberships"; import { TUser, TUserCreateInput, TUserUpdateInput, ZUserUpdateInput } from "@formbricks/types/user"; diff --git a/packages/lib/webhook/auth.ts b/packages/lib/webhook/auth.ts index f3b4626118..f88839aa61 100644 --- a/packages/lib/webhook/auth.ts +++ b/packages/lib/webhook/auth.ts @@ -1,4 +1,4 @@ -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { cache } from "../cache"; import { hasUserEnvironmentAccess } from "../environment/auth"; import { validateInputs } from "../utils/validate"; diff --git a/packages/lib/webhook/service.ts b/packages/lib/webhook/service.ts index 4a10edc198..c80b47ae72 100644 --- a/packages/lib/webhook/service.ts +++ b/packages/lib/webhook/service.ts @@ -2,7 +2,7 @@ import "server-only"; import { Prisma } from "@prisma/client"; import { prisma } from "@formbricks/database"; import { ZOptionalNumber } from "@formbricks/types/common"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors"; import { TWebhook, TWebhookInput, ZWebhookInput } from "@formbricks/types/webhooks"; import { cache } from "../cache"; diff --git a/packages/types/actions.ts b/packages/types/actions.ts index 8524228f85..7418abd1e1 100644 --- a/packages/types/actions.ts +++ b/packages/types/actions.ts @@ -1,6 +1,6 @@ import { z } from "zod"; import { ZActionClass } from "./action-classes"; -import { ZId } from "./environment"; +import { ZId } from "./common"; export const ZAction = z.object({ id: ZId, diff --git a/packages/types/common.ts b/packages/types/common.ts index e6b7397fef..eb45cff676 100644 --- a/packages/types/common.ts +++ b/packages/types/common.ts @@ -37,3 +37,7 @@ export const ZAllowedFileExtension = z.enum([ ]); export type TAllowedFileExtension = z.infer; + +export const ZId = z.string().cuid2(); + +export const ZUuid = z.string().uuid(); diff --git a/packages/types/environment.ts b/packages/types/environment.ts index aa45c2500c..9111ad3901 100644 --- a/packages/types/environment.ts +++ b/packages/types/environment.ts @@ -25,8 +25,6 @@ export const ZEnvironmentUpdateInput = z.object({ websiteSetupCompleted: z.boolean(), }); -export const ZId = z.string().cuid2(); - export const ZEnvironmentCreateInput = z.object({ type: z.enum(["development", "production"]).optional(), appSetupCompleted: z.boolean().optional(), diff --git a/packages/types/responses.ts b/packages/types/responses.ts index 2e7c637e0b..d31a5fda07 100644 --- a/packages/types/responses.ts +++ b/packages/types/responses.ts @@ -1,6 +1,6 @@ import { z } from "zod"; import { ZAttributes } from "./attributes"; -import { ZId } from "./environment"; +import { ZId } from "./common"; import { ZSurvey, ZSurveyLogicCondition } from "./surveys/types"; import { ZTag } from "./tags"; diff --git a/packages/types/surveys/types.ts b/packages/types/surveys/types.ts index 8b279b748a..48671fea35 100644 --- a/packages/types/surveys/types.ts +++ b/packages/types/surveys/types.ts @@ -1,8 +1,7 @@ import { z } from "zod"; import { ZActionClass, ZActionClassNoCodeConfig } from "../action-classes"; import { ZAttributes } from "../attributes"; -import { ZAllowedFileExtension, ZColor, ZPlacement } from "../common"; -import { ZId } from "../environment"; +import { ZAllowedFileExtension, ZColor, ZPlacement , ZId } from "../common"; import { ZLanguage } from "../product"; import { ZSegment } from "../segment"; import { ZBaseStyling } from "../styling"; diff --git a/packages/ui/ShareSurveyLink/actions.ts b/packages/ui/ShareSurveyLink/actions.ts index c68331e0d7..f176441958 100644 --- a/packages/ui/ShareSurveyLink/actions.ts +++ b/packages/ui/ShareSurveyLink/actions.ts @@ -5,7 +5,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromSurveyId } from "@formbricks/lib/organization/utils"; import { generateSurveySingleUseId } from "@formbricks/lib/utils/singleUseSurveys"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; const ZGenerateSingleUseIdAction = z.object({ surveyId: ZId, diff --git a/packages/ui/SingleResponseCard/actions.ts b/packages/ui/SingleResponseCard/actions.ts index 7643f02f31..029a40d9a9 100644 --- a/packages/ui/SingleResponseCard/actions.ts +++ b/packages/ui/SingleResponseCard/actions.ts @@ -17,7 +17,7 @@ import { } from "@formbricks/lib/responseNote/service"; import { createTag } from "@formbricks/lib/tag/service"; import { addTagToRespone, deleteTagOnResponse } from "@formbricks/lib/tagOnResponse/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; const ZCreateTagAction = z.object({ environmentId: ZId, diff --git a/packages/ui/SurveysList/actions.ts b/packages/ui/SurveysList/actions.ts index abd917f8b4..7404bf541f 100644 --- a/packages/ui/SurveysList/actions.ts +++ b/packages/ui/SurveysList/actions.ts @@ -15,7 +15,7 @@ import { getSurveys, } from "@formbricks/lib/survey/service"; import { generateSurveySingleUseId } from "@formbricks/lib/utils/singleUseSurveys"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ZSurveyFilterCriteria } from "@formbricks/types/surveys/types"; const ZGetSurveyAction = z.object({ diff --git a/packages/ui/TemplateList/actions.ts b/packages/ui/TemplateList/actions.ts index 182c7a4284..2fb8c92e63 100644 --- a/packages/ui/TemplateList/actions.ts +++ b/packages/ui/TemplateList/actions.ts @@ -5,7 +5,7 @@ import { authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; import { getOrganizationIdFromEnvironmentId } from "@formbricks/lib/organization/utils"; import { createSurvey } from "@formbricks/lib/survey/service"; -import { ZId } from "@formbricks/types/environment"; +import { ZId } from "@formbricks/types/common"; import { ZSurveyCreateInput } from "@formbricks/types/surveys/types"; const ZCreateSurveyAction = z.object({ From f4a367d2de6290f632288c9f3586d4086ce274ab Mon Sep 17 00:00:00 2001 From: Anshuman Pandey <54475686+pandeymangg@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:13:39 +0530 Subject: [PATCH 03/19] feat: survey variables (#3013) Co-authored-by: Matti Nannt --- .../edit/components/HiddenFieldsCard.tsx | 31 ++- .../edit/components/QuestionsView.tsx | 24 +- .../edit/components/SurveyVariablesCard.tsx | 79 ++++++ .../components/SurveyVariablesCardItem.tsx | 224 ++++++++++++++++++ .../surveys/lib/minimalSurvey.ts | 1 + .../lib/notificationResponse.ts | 8 +- packages/database/json-types.ts | 2 + .../migration.sql | 2 + packages/database/schema.prisma | 3 + packages/database/zod-utils.ts | 1 + packages/lib/styling/constants.ts | 1 + packages/lib/survey/service.ts | 1 + .../lib/survey/tests/__mock__/survey.mock.ts | 1 + packages/lib/utils/recall.ts | 48 +++- .../src/components/general/EndingCard.tsx | 15 +- .../surveys/src/components/general/Survey.tsx | 3 +- .../src/components/general/WelcomeCard.tsx | 16 +- packages/surveys/src/lib/recall.ts | 25 +- packages/types/surveys/types.ts | 55 ++++- packages/ui/Input/index.tsx | 2 +- packages/ui/PreviewSurvey/index.tsx | 8 +- .../components/RecallItemSelect.tsx | 50 +++- .../components/SingleResponseCardBody.tsx | 2 + 23 files changed, 554 insertions(+), 48 deletions(-) create mode 100644 apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyVariablesCard.tsx create mode 100644 apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyVariablesCardItem.tsx create mode 100644 packages/database/migrations/20240813094711_added_variables_to_survey_model/migration.sql diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/HiddenFieldsCard.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/HiddenFieldsCard.tsx index c4472acf0b..b21fafaba9 100644 --- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/HiddenFieldsCard.tsx +++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/HiddenFieldsCard.tsx @@ -4,6 +4,7 @@ import * as Collapsible from "@radix-ui/react-collapsible"; import { useState } from "react"; import { toast } from "react-hot-toast"; import { cn } from "@formbricks/lib/cn"; +import { extractRecallInfo } from "@formbricks/lib/utils/recall"; import { TSurvey, TSurveyHiddenFields } from "@formbricks/types/surveys/types"; import { validateId } from "@formbricks/types/surveys/validation"; import { Button } from "@formbricks/ui/Button"; @@ -36,9 +37,26 @@ export const HiddenFieldsCard = ({ } }; - const updateSurvey = (data: TSurveyHiddenFields) => { + const updateSurvey = (data: TSurveyHiddenFields, currentFieldId?: string) => { + const questions = [...localSurvey.questions]; + + // Remove recall info from question headlines + if (currentFieldId) { + questions.forEach((question) => { + for (const [languageCode, headline] of Object.entries(question.headline)) { + if (headline.includes(`recall:${currentFieldId}`)) { + const recallInfo = extractRecallInfo(headline); + if (recallInfo) { + question.headline[languageCode] = headline.replace(recallInfo, ""); + } + } + } + }); + } + setLocalSurvey({ ...localSurvey, + questions, hiddenFields: { ...localSurvey.hiddenFields, ...data, @@ -93,10 +111,13 @@ export const HiddenFieldsCard = ({ { - updateSurvey({ - enabled: true, - fieldIds: localSurvey.hiddenFields?.fieldIds?.filter((q) => q !== fieldId), - }); + updateSurvey( + { + enabled: true, + fieldIds: localSurvey.hiddenFields?.fieldIds?.filter((q) => q !== fieldId), + }, + fieldId + ); }} tagId={fieldId} tagName={fieldId} diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx index 3212b17b72..446984c12d 100644 --- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx +++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsView.tsx @@ -14,7 +14,7 @@ import { createId } from "@paralleldrive/cuid2"; import React, { SetStateAction, useEffect, useMemo, useState } from "react"; import toast from "react-hot-toast"; import { MultiLanguageCard } from "@formbricks/ee/multi-language/components/multi-language-card"; -import { addMultiLanguageLabels, extractLanguageCodes, getLocalizedValue } from "@formbricks/lib/i18n/utils"; +import { addMultiLanguageLabels, extractLanguageCodes } from "@formbricks/lib/i18n/utils"; import { structuredClone } from "@formbricks/lib/pollyfills/structuredClone"; import { getDefaultEndingCard } from "@formbricks/lib/templates"; import { checkForEmptyFallBackValue, extractRecallInfo } from "@formbricks/lib/utils/recall"; @@ -209,15 +209,14 @@ export const QuestionsView = ({ const activeQuestionIdTemp = activeQuestionId ?? localSurvey.questions[0].id; let updatedSurvey: TSurvey = { ...localSurvey }; - // check if we are recalling from this question + // check if we are recalling from this question for every language updatedSurvey.questions.forEach((question) => { - if (question.headline[selectedLanguageCode].includes(`recall:${questionId}`)) { - const recallInfo = extractRecallInfo(getLocalizedValue(question.headline, selectedLanguageCode)); - if (recallInfo) { - question.headline[selectedLanguageCode] = question.headline[selectedLanguageCode].replace( - recallInfo, - "" - ); + for (const [languageCode, headline] of Object.entries(question.headline)) { + if (headline.includes(`recall:${questionId}`)) { + const recallInfo = extractRecallInfo(headline); + if (recallInfo) { + question.headline[languageCode] = headline.replace(recallInfo, ""); + } } } }); @@ -434,6 +433,13 @@ export const QuestionsView = ({ activeQuestionId={activeQuestionId} /> + {/* */} + void; + activeQuestionId: string | null; + setActiveQuestionId: (id: string | null) => void; +} + +const variablesCardId = `fb-variables-${Date.now()}`; + +export const SurveyVariablesCard = ({ + localSurvey, + setLocalSurvey, + activeQuestionId, + setActiveQuestionId, +}: SurveyVariablesCardProps) => { + const open = activeQuestionId === variablesCardId; + + const setOpenState = (state: boolean) => { + if (state) { + setActiveQuestionId(variablesCardId); + } else { + setActiveQuestionId(null); + } + }; + + return ( +

+
+

🪣

+
+ + +
+
+
+

Variables

+
+
+
+
+ +
+ {localSurvey.variables.length > 0 ? ( + localSurvey.variables.map((variable) => ( + + )) + ) : ( +

No variables yet. Add the first one below.

+ )} +
+ + +
+
+
+ ); +}; diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyVariablesCardItem.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyVariablesCardItem.tsx new file mode 100644 index 0000000000..63820f46fa --- /dev/null +++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyVariablesCardItem.tsx @@ -0,0 +1,224 @@ +"use client"; + +import { createId } from "@paralleldrive/cuid2"; +import { TrashIcon } from "lucide-react"; +import React, { useCallback, useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { extractRecallInfo } from "@formbricks/lib/utils/recall"; +import { TSurvey, TSurveyVariable } from "@formbricks/types/surveys/types"; +import { Button } from "@formbricks/ui/Button"; +import { FormControl, FormField, FormItem, FormProvider } from "@formbricks/ui/Form"; +import { Input } from "@formbricks/ui/Input"; +import { Label } from "@formbricks/ui/Label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui/Select"; + +interface SurveyVariablesCardItemProps { + variable?: TSurveyVariable; + localSurvey: TSurvey; + setLocalSurvey: React.Dispatch>; + mode: "create" | "edit"; +} + +export const SurveyVariablesCardItem = ({ + variable, + localSurvey, + setLocalSurvey, + mode, +}: SurveyVariablesCardItemProps) => { + const form = useForm({ + defaultValues: variable ?? { + id: createId(), + name: "", + type: "number", + value: 0, + }, + mode: "onChange", + }); + + const { errors } = form.formState; + const isNameError = !!errors.name?.message; + const variableType = form.watch("type"); + + const editSurveyVariable = useCallback( + (data: TSurveyVariable) => { + setLocalSurvey((prevSurvey) => { + const updatedVariables = prevSurvey.variables.map((v) => (v.id === data.id ? data : v)); + return { ...prevSurvey, variables: updatedVariables }; + }); + }, + [setLocalSurvey] + ); + + const createSurveyVariable = (data: TSurveyVariable) => { + setLocalSurvey({ + ...localSurvey, + variables: [...localSurvey.variables, data], + }); + + form.reset({ + id: createId(), + name: "", + type: "number", + value: 0, + }); + }; + + useEffect(() => { + if (mode === "create") { + return; + } + + const subscription = form.watch(() => form.handleSubmit(editSurveyVariable)()); + return () => subscription.unsubscribe(); + }, [form, mode, editSurveyVariable]); + + const onVaribleDelete = (variable: TSurveyVariable) => { + const questions = [...localSurvey.questions]; + + // find if this variable is used in any question's recall and remove it for every language + + questions.forEach((question) => { + for (const [languageCode, headline] of Object.entries(question.headline)) { + if (headline.includes(`recall:${variable.id}`)) { + const recallInfo = extractRecallInfo(headline); + if (recallInfo) { + question.headline[languageCode] = headline.replace(recallInfo, ""); + } + } + } + }); + + setLocalSurvey((prevSurvey) => { + const updatedVariables = prevSurvey.variables.filter((v) => v.id !== variable.id); + return { ...prevSurvey, variables: updatedVariables, questions }; + }); + }; + + if (mode === "edit" && !variable) { + return null; + } + + return ( +
+ +
{ + if (mode === "create") { + createSurveyVariable(data); + } else { + editSurveyVariable(data); + } + })}> + {mode === "create" && } + +
+ { + // if the variable name is already taken + if ( + mode === "create" && + localSurvey.variables.find((variable) => variable.name === value) + ) { + return "Variable name is already taken, please choose another."; + } + + if (mode === "edit" && variable && variable.name !== value) { + if (localSurvey.variables.find((variable) => variable.name === value)) { + return "Variable name is already taken, please choose another."; + } + } + + // if it does not start with a letter + if (!/^[a-z]/.test(value)) { + return "Variable name must start with a letter."; + } + }, + }} + render={({ field }) => ( + + + + + + )} + /> + + ( + + )} + /> + +

=

+ + ( + + + { + field.onChange(variableType === "number" ? Number(e.target.value) : e.target.value); + }} + placeholder="Initial value" + type={variableType === "number" ? "number" : "text"} + /> + + + )} + /> + + {mode === "create" && ( + + )} + + {mode === "edit" && variable && ( + + )} +
+ + {isNameError &&

{errors.name?.message}

} +
+
+
+ ); +}; diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/lib/minimalSurvey.ts b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/lib/minimalSurvey.ts index 428eab5de9..4e4920f56e 100644 --- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/lib/minimalSurvey.ts +++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/lib/minimalSurvey.ts @@ -37,4 +37,5 @@ export const minimalSurvey: TSurvey = { languages: [], showLanguageSwitch: false, isVerifyEmailEnabled: false, + variables: [], }; diff --git a/apps/web/app/api/cron/weekly-summary/lib/notificationResponse.ts b/apps/web/app/api/cron/weekly-summary/lib/notificationResponse.ts index 80c6f67fd1..9841b81955 100644 --- a/apps/web/app/api/cron/weekly-summary/lib/notificationResponse.ts +++ b/apps/web/app/api/cron/weekly-summary/lib/notificationResponse.ts @@ -1,7 +1,9 @@ import { getLocalizedValue } from "@formbricks/lib/i18n/utils"; import { convertResponseValue } from "@formbricks/lib/responses"; import { replaceHeadlineRecall } from "@formbricks/lib/utils/recall"; +import { TSurvey } from "@formbricks/types/surveys/types"; import { + TWeeklyEmailResponseData, TWeeklySummaryEnvironmentData, TWeeklySummaryNotificationDataSurvey, TWeeklySummaryNotificationResponse, @@ -23,7 +25,11 @@ export const getNotificationResponse = ( const surveys: TWeeklySummaryNotificationDataSurvey[] = []; // iterate through the surveys and calculate the overall insights for (const survey of environment.surveys) { - const parsedSurvey = replaceHeadlineRecall(survey, "default", environment.attributeClasses); + const parsedSurvey = replaceHeadlineRecall( + survey as unknown as TSurvey, + "default", + environment.attributeClasses + ) as TSurvey & { responses: TWeeklyEmailResponseData[] }; const surveyData: TWeeklySummaryNotificationDataSurvey = { id: parsedSurvey.id, name: parsedSurvey.name, diff --git a/packages/database/json-types.ts b/packages/database/json-types.ts index 16e95507f2..cd5cf51b04 100644 --- a/packages/database/json-types.ts +++ b/packages/database/json-types.ts @@ -17,6 +17,7 @@ import { type TSurveyQuestions, type TSurveySingleUse, type TSurveyStyling, + type TSurveyVariables, type TSurveyWelcomeCard, } from "@formbricks/types/surveys/types"; import { type TUserNotificationSettings } from "@formbricks/types/user"; @@ -34,6 +35,7 @@ declare global { export type SurveyQuestions = TSurveyQuestions; export type SurveyEnding = TSurveyEnding; export type SurveyHiddenFields = TSurveyHiddenFields; + export type SurveyVariables = TSurveyVariables; export type SurveyProductOverwrites = TSurveyProductOverwrites; export type SurveyStyling = TSurveyStyling; export type SurveyClosedMessage = TSurveyClosedMessage; diff --git a/packages/database/migrations/20240813094711_added_variables_to_survey_model/migration.sql b/packages/database/migrations/20240813094711_added_variables_to_survey_model/migration.sql new file mode 100644 index 0000000000..3a0b4bd5e5 --- /dev/null +++ b/packages/database/migrations/20240813094711_added_variables_to_survey_model/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Survey" ADD COLUMN "variables" JSONB NOT NULL DEFAULT '[]'; diff --git a/packages/database/schema.prisma b/packages/database/schema.prisma index 32d544a3cd..2920c6a53c 100644 --- a/packages/database/schema.prisma +++ b/packages/database/schema.prisma @@ -278,6 +278,9 @@ model Survey { /// @zod.custom(imports.ZSurveyHiddenFields) /// [SurveyHiddenFields] hiddenFields Json @default("{\"enabled\": false}") + /// @zod.custom(imports.ZSurveyVariables) + /// [SurveyVariables] + variables Json @default("[]") responses Response[] displayOption displayOptions @default(displayOnce) recontactDays Int? diff --git a/packages/database/zod-utils.ts b/packages/database/zod-utils.ts index 21a869b445..376c8d3e74 100644 --- a/packages/database/zod-utils.ts +++ b/packages/database/zod-utils.ts @@ -15,6 +15,7 @@ export { ZSurveyWelcomeCard, ZSurveyQuestions, ZSurveyHiddenFields, + ZSurveyVariables, ZSurveyClosedMessage, ZSurveyProductOverwrites, ZSurveyStyling, diff --git a/packages/lib/styling/constants.ts b/packages/lib/styling/constants.ts index 74ead8b6d2..61c3767ff2 100644 --- a/packages/lib/styling/constants.ts +++ b/packages/lib/styling/constants.ts @@ -104,6 +104,7 @@ export const PREVIEW_SURVEY = { enabled: true, fieldIds: [], }, + variables: [], displayOption: "displayOnce", recontactDays: null, displayLimit: null, diff --git a/packages/lib/survey/service.ts b/packages/lib/survey/service.ts index 77cbc3dba6..8bd8a7185a 100644 --- a/packages/lib/survey/service.ts +++ b/packages/lib/survey/service.ts @@ -66,6 +66,7 @@ export const selectSurvey = { questions: true, endings: true, hiddenFields: true, + variables: true, displayOption: true, recontactDays: true, displayLimit: true, diff --git a/packages/lib/survey/tests/__mock__/survey.mock.ts b/packages/lib/survey/tests/__mock__/survey.mock.ts index f8852e9026..6e4ec19e17 100644 --- a/packages/lib/survey/tests/__mock__/survey.mock.ts +++ b/packages/lib/survey/tests/__mock__/survey.mock.ts @@ -277,6 +277,7 @@ export const updateSurveyInput: TSurvey = { segment: null, languages: [], showLanguageSwitch: null, + variables: [], ...commonMockProperties, ...baseSurveyProperties, }; diff --git a/packages/lib/utils/recall.ts b/packages/lib/utils/recall.ts index daecc7840f..65423e68d6 100644 --- a/packages/lib/utils/recall.ts +++ b/packages/lib/utils/recall.ts @@ -5,8 +5,8 @@ import { TI18nString, TSurvey, TSurveyQuestion, - TSurveyQuestionsObject, TSurveyRecallItem, + TSurveyVariables, } from "@formbricks/types/surveys/types"; import { getLocalizedValue } from "../i18n/utils"; import { structuredClone } from "../pollyfills/structuredClone"; @@ -60,7 +60,7 @@ export const findRecallInfoById = (text: string, id: string): string | null => { return match ? match[0] : null; }; -const getRecallItemLabel = ( +const getRecallItemLabel = ( recallItemId: string, survey: T, languageCode: string, @@ -75,11 +75,14 @@ const getRecallItemLabel = ( const attributeClass = attributeClasses.find( (attributeClass) => attributeClass.name.replaceAll(" ", "nbsp") === recallItemId ); - return attributeClass?.name; + if (attributeClass) return attributeClass?.name; + + const variable = survey.variables?.find((variable) => variable.id === recallItemId); + if (variable) return variable.name; }; // Converts recall information in a headline to a corresponding recall question headline, with or without a slash. -export const recallToHeadline = ( +export const recallToHeadline = ( headline: TI18nString, survey: T, withSlash: boolean, @@ -149,7 +152,7 @@ export const checkForEmptyFallBackValue = (survey: TSurvey, language: string): T }; // Processes each question in a survey to ensure headlines are formatted correctly for recall and return the modified survey. -export const replaceHeadlineRecall = ( +export const replaceHeadlineRecall = ( survey: T, language: string, attributeClasses: TAttributeClass[] @@ -181,15 +184,24 @@ export const getRecallItems = ( ids.forEach((recallItemId) => { const isHiddenField = survey.hiddenFields.fieldIds?.includes(recallItemId); const isSurveyQuestion = survey.questions.find((question) => question.id === recallItemId); + const isVariable = survey.variables.find((variable) => variable.id === recallItemId); const recallItemLabel = getRecallItemLabel(recallItemId, survey, languageCode, attributeClasses); + + const getRecallItemType = () => { + if (isHiddenField) return "hiddenField"; + if (isSurveyQuestion) return "question"; + if (isVariable) return "variable"; + return "attributeClass"; + }; + if (recallItemLabel) { let recallItemLabelTemp = recallItemLabel; recallItemLabelTemp = replaceRecallInfoWithUnderline(recallItemLabelTemp); recallItems.push({ id: recallItemId, label: recallItemLabelTemp, - type: isHiddenField ? "hiddenField" : isSurveyQuestion ? "question" : "attributeClass", + type: getRecallItemType(), }); } }); @@ -228,6 +240,7 @@ export const parseRecallInfo = ( text: string, attributes?: TAttributes, responseData?: TResponseData, + variables?: TSurveyVariables, withSlash: boolean = false ) => { let modifiedText = text; @@ -253,6 +266,29 @@ export const parseRecallInfo = ( } }); } + + if (variables && variables.length > 0) { + variables.forEach((variable) => { + const recallPattern = `#recall:`; + while (modifiedText.includes(recallPattern)) { + const recallInfo = extractRecallInfo(modifiedText, variable.id); + if (!recallInfo) break; // Exit the loop if no recall info is found + + const recallItemId = extractId(recallInfo); + if (!recallItemId) continue; // Skip to the next iteration if no ID could be extracted + + const fallback = extractFallbackValue(recallInfo).replaceAll("nbsp", " "); + + let value = variable.value?.toString() || fallback; + if (withSlash) { + modifiedText = modifiedText.replace(recallInfo, "#/" + value + "\\#"); + } else { + modifiedText = modifiedText.replace(recallInfo, value); + } + } + }); + } + if (responseData && questionIds.length > 0) { while (modifiedText.includes("recall:")) { const recallInfo = extractRecallInfo(modifiedText); diff --git a/packages/surveys/src/components/general/EndingCard.tsx b/packages/surveys/src/components/general/EndingCard.tsx index a52f775102..086a08594f 100644 --- a/packages/surveys/src/components/general/EndingCard.tsx +++ b/packages/surveys/src/components/general/EndingCard.tsx @@ -96,7 +96,11 @@ export const EndingCard = ({ alignTextCenter={true} headline={ endingCard.type === "endScreen" - ? replaceRecallInfo(getLocalizedValue(endingCard.headline, languageCode), responseData) + ? replaceRecallInfo( + getLocalizedValue(endingCard.headline, languageCode), + responseData, + survey.variables + ) : "Respondants will not see this card" } questionId="EndingCard" @@ -104,7 +108,11 @@ export const EndingCard = ({ q.id === questionId); } }, [questionId, survey, history]); + const contentRef = useRef(null); const showProgressBar = !styling.hideProgressBar; const getShowSurveyCloseButton = (offset: number) => { @@ -297,7 +298,7 @@ export const Survey = ({ string; + replaceRecallInfo: (text: string, responseData: TResponseData, variables: TSurveyVariables) => string; isCurrent: boolean; responseData: TResponseData; } @@ -142,11 +142,19 @@ export const WelcomeCard = ({ )} diff --git a/packages/surveys/src/lib/recall.ts b/packages/surveys/src/lib/recall.ts index 5782b367d6..621be79f12 100644 --- a/packages/surveys/src/lib/recall.ts +++ b/packages/surveys/src/lib/recall.ts @@ -3,9 +3,13 @@ import { structuredClone } from "@formbricks/lib/pollyfills/structuredClone"; import { formatDateWithOrdinal, isValidDateString } from "@formbricks/lib/utils/datetime"; import { extractFallbackValue, extractId, extractRecallInfo } from "@formbricks/lib/utils/recall"; import { TResponseData } from "@formbricks/types/responses"; -import { TSurveyQuestion } from "@formbricks/types/surveys/types"; +import { TSurveyQuestion, TSurveyVariables } from "@formbricks/types/surveys/types"; -export const replaceRecallInfo = (text: string, responseData: TResponseData): string => { +export const replaceRecallInfo = ( + text: string, + responseData: TResponseData, + variables: TSurveyVariables +): string => { let modifiedText = text; while (modifiedText.includes("recall:")) { @@ -16,7 +20,13 @@ export const replaceRecallInfo = (text: string, responseData: TResponseData): st if (!recallItemId) return modifiedText; // Return the text if no ID could be extracted const fallback = extractFallbackValue(recallInfo).replaceAll("nbsp", " "); - let value = null; + let value: string | null = null; + + // Fetching value from variables based on recallItemId + if (variables.length) { + const variable = variables.find((variable) => variable.id === recallItemId); + value = variable?.value?.toString() ?? fallback; + } // Fetching value from responseData or attributes based on recallItemId if (responseData[recallItemId]) { @@ -42,13 +52,15 @@ export const replaceRecallInfo = (text: string, responseData: TResponseData): st export const parseRecallInformation = ( question: TSurveyQuestion, languageCode: string, - responseData: TResponseData + responseData: TResponseData, + variables: TSurveyVariables ) => { const modifiedQuestion = structuredClone(question); if (question.headline && question.headline[languageCode]?.includes("recall:")) { modifiedQuestion.headline[languageCode] = replaceRecallInfo( getLocalizedValue(modifiedQuestion.headline, languageCode), - responseData + responseData, + variables ); } if ( @@ -58,7 +70,8 @@ export const parseRecallInformation = ( ) { modifiedQuestion.subheader[languageCode] = replaceRecallInfo( getLocalizedValue(modifiedQuestion.subheader, languageCode), - responseData + responseData, + variables ); } return modifiedQuestion; diff --git a/packages/types/surveys/types.ts b/packages/types/surveys/types.ts index 48671fea35..f2b7686c06 100644 --- a/packages/types/surveys/types.ts +++ b/packages/types/surveys/types.ts @@ -142,6 +142,36 @@ export const ZSurveyHiddenFields = z.object({ export type TSurveyHiddenFields = z.infer; +export const ZSurveyVariable = z + .discriminatedUnion("type", [ + z.object({ + id: z.string().cuid2(), + name: z.string(), + type: z.literal("number"), + value: z.number().default(0), + }), + z.object({ + id: z.string().cuid2(), + name: z.string(), + type: z.literal("text"), + value: z.string().default(""), + }), + ]) + .superRefine((data, ctx) => { + // variable name can only contain lowercase letters, numbers, and underscores + if (!/^[a-z0-9_]+$/.test(data.name)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Variable name can only contain lowercase letters, numbers, and underscores", + path: ["variables"], + }); + } + }); +export const ZSurveyVariables = z.array(ZSurveyVariable); + +export type TSurveyVariable = z.infer; +export type TSurveyVariables = z.infer; + export const ZSurveyProductOverwrites = z.object({ brandColor: ZColor.nullish(), highlightBorderColor: ZColor.nullish(), @@ -603,6 +633,29 @@ export const ZSurvey = z } }), hiddenFields: ZSurveyHiddenFields, + variables: ZSurveyVariables.superRefine((variables, ctx) => { + // variable ids must be unique + const variableIds = variables.map((v) => v.id); + const uniqueVariableIds = new Set(variableIds); + if (uniqueVariableIds.size !== variableIds.length) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Variable IDs must be unique", + path: ["variables"], + }); + } + + // variable names must be unique + const variableNames = variables.map((v) => v.name); + const uniqueVariableNames = new Set(variableNames); + if (uniqueVariableNames.size !== variableNames.length) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Variable names must be unique", + path: ["variables"], + }); + } + }), delay: z.number(), autoComplete: z.number().min(1, { message: "Response limit must be greater than 0" }).nullable(), runOnDate: z.date().nullable(), @@ -1389,7 +1442,7 @@ export type TSurveySummary = z.infer; export const ZSurveyRecallItem = z.object({ id: z.string(), label: z.string(), - type: z.enum(["question", "hiddenField", "attributeClass"]), + type: z.enum(["question", "hiddenField", "attributeClass", "variable"]), }); export type TSurveyRecallItem = z.infer; diff --git a/packages/ui/Input/index.tsx b/packages/ui/Input/index.tsx index 6c2abcb975..cf391afc5e 100644 --- a/packages/ui/Input/index.tsx +++ b/packages/ui/Input/index.tsx @@ -16,7 +16,7 @@ const Input = React.forwardRef(({ className, isInv className={cn( "focus:border-brand-dark flex h-10 w-full rounded-md border border-slate-300 bg-transparent px-3 py-2 text-sm text-slate-800 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-500 dark:text-slate-300", className, - isInvalid && "border-error focus:border-error border" + isInvalid && "border border-red-500 focus:border-red-500" )} ref={ref} {...props} diff --git a/packages/ui/PreviewSurvey/index.tsx b/packages/ui/PreviewSurvey/index.tsx index 8ef4872039..c563d7814d 100644 --- a/packages/ui/PreviewSurvey/index.tsx +++ b/packages/ui/PreviewSurvey/index.tsx @@ -145,7 +145,13 @@ export const PreviewSurvey = ({ const updateQuestionId = useCallback( (newQuestionId: string) => { - if (!newQuestionId || newQuestionId === "hidden" || newQuestionId === "multiLanguage") return; + if ( + !newQuestionId || + newQuestionId === "hidden" || + newQuestionId === "multiLanguage" || + newQuestionId.includes("fb-variables-") + ) + return; if (newQuestionId === "start" && !survey.welcomeCard.enabled) return; setQuestionId(newQuestionId); }, diff --git a/packages/ui/QuestionFormInput/components/RecallItemSelect.tsx b/packages/ui/QuestionFormInput/components/RecallItemSelect.tsx index 1903d549e8..685ded56dd 100644 --- a/packages/ui/QuestionFormInput/components/RecallItemSelect.tsx +++ b/packages/ui/QuestionFormInput/components/RecallItemSelect.tsx @@ -2,6 +2,8 @@ import { DropdownMenuItem } from "@radix-ui/react-dropdown-menu"; import { CalendarDaysIcon, EyeOffIcon, + FileDigitIcon, + FileTextIcon, HomeIcon, ListIcon, MessageSquareTextIcon, @@ -98,6 +100,22 @@ export const RecallItemSelect = ({ }); }, [attributeClasses]); + const variableRecallItems = useMemo(() => { + if (localSurvey.variables.length) { + return localSurvey.variables + .filter((variable) => !recallItemIds.includes(variable.id)) + .map((variable) => { + return { + id: variable.id, + label: variable.name, + type: "variable" as const, + }; + }); + } + + return []; + }, [localSurvey.variables, recallItemIds]); + const surveyQuestionRecallItems = useMemo(() => { const isEndingCard = !localSurvey.questions.map((question) => question.id).includes(questionId); const idx = isEndingCard @@ -118,22 +136,31 @@ export const RecallItemSelect = ({ }, [localSurvey.questions, questionId, recallItemIds]); const filteredRecallItems: TSurveyRecallItem[] = useMemo(() => { - return [...surveyQuestionRecallItems, ...hiddenFieldRecallItems, ...attributeClassRecallItems].filter( - (recallItems) => { - if (searchValue.trim() === "") return true; - else { - return recallItems.label.toLowerCase().startsWith(searchValue.toLowerCase()); - } + return [ + ...surveyQuestionRecallItems, + ...hiddenFieldRecallItems, + ...attributeClassRecallItems, + ...variableRecallItems, + ].filter((recallItems) => { + if (searchValue.trim() === "") return true; + else { + return recallItems.label.toLowerCase().startsWith(searchValue.toLowerCase()); } - ); - }, [surveyQuestionRecallItems, hiddenFieldRecallItems, attributeClassRecallItems, searchValue]); + }); + }, [ + surveyQuestionRecallItems, + hiddenFieldRecallItems, + attributeClassRecallItems, + variableRecallItems, + searchValue, + ]); // function to modify headline (recallInfo to corresponding headline) const getRecallLabel = (label: string): string => { return replaceRecallInfoWithUnderline(label); }; - const getQuestionIcon = (recallItem: TSurveyRecallItem) => { + const getRecallItemIcon = (recallItem: TSurveyRecallItem) => { switch (recallItem.type) { case "question": const question = localSurvey.questions.find((question) => question.id === recallItem.id); @@ -144,6 +171,9 @@ export const RecallItemSelect = ({ return EyeOffIcon; case "attributeClass": return TagIcon; + case "variable": + const variable = localSurvey.variables.find((variable) => variable.id === recallItem.id); + return variable?.type === "number" ? FileDigitIcon : FileTextIcon; } }; @@ -170,7 +200,7 @@ export const RecallItemSelect = ({ />
{filteredRecallItems.map((recallItem, index) => { - const IconComponent = getQuestionIcon(recallItem); + const IconComponent = getRecallItemIcon(recallItem); return ( {survey.welcomeCard.enabled && ( @@ -161,6 +162,7 @@ export const SingleResponseCardBody = ({ getLocalizedValue(question.headline, "default"), {}, response.data, + survey.variables, true ) )} From 5689c36b12903b7a61508c6c614228099b10e4e3 Mon Sep 17 00:00:00 2001 From: Piyush Gupta <56182734+gupta-piyush19@users.noreply.github.com> Date: Thu, 22 Aug 2024 15:25:13 +0530 Subject: [PATCH 04/19] feat: adds management endpoint to generate multiple suId (#3022) Co-authored-by: Matti Nannt --- .../docs/app/developer-docs/rest-api/page.mdx | 2 +- .../surveys/[surveyId]/singleUseIds/route.ts | 47 +++++++++++++++++++ packages/lib/utils/singleUseSurveys.ts | 10 ++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 apps/web/app/api/v1/management/surveys/[surveyId]/singleUseIds/route.ts diff --git a/apps/docs/app/developer-docs/rest-api/page.mdx b/apps/docs/app/developer-docs/rest-api/page.mdx index 7bb30057da..7783a07063 100644 --- a/apps/docs/app/developer-docs/rest-api/page.mdx +++ b/apps/docs/app/developer-docs/rest-api/page.mdx @@ -39,7 +39,7 @@ We currently have the following Management API methods exposed and below is thei - [Me API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#79e08365-641d-4b2d-aea2-9a855e0438ec) - Retrieve Account Information - [People API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#cffc27a6-dafb-428f-8ea7-5165bedb911e) - List and Delete People - [Response API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#e544ec0d-8b30-4e33-8d35-2441cb40d676) - List, List by Survey, Update, and Delete Responses -- [Survey API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#953189b2-37b5-4429-a7bd-f4d01ceae242) - List, Create, Update, and Delete Surveys +- [Survey API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#953189b2-37b5-4429-a7bd-f4d01ceae242) - List, Create, Update, generate multiple suId and Delete Surveys - [Webhook API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#62e6ec65-021b-42a4-ac93-d1434b393c6c) - List, Create, and Delete Webhooks ## How to Generate an API key diff --git a/apps/web/app/api/v1/management/surveys/[surveyId]/singleUseIds/route.ts b/apps/web/app/api/v1/management/surveys/[surveyId]/singleUseIds/route.ts new file mode 100644 index 0000000000..9fa9ef30b2 --- /dev/null +++ b/apps/web/app/api/v1/management/surveys/[surveyId]/singleUseIds/route.ts @@ -0,0 +1,47 @@ +import { authenticateRequest, handleErrorResponse } from "@/app/api/v1/auth"; +import { responses } from "@/app/lib/api/response"; +import { NextRequest } from "next/server"; +import { getSurvey } from "@formbricks/lib/survey/service"; +import { generateSurveySingleUseIds } from "@formbricks/lib/utils/singleUseSurveys"; + +export const GET = async ( + request: NextRequest, + { params }: { params: { surveyId: string } } +): Promise => { + try { + const authentication = await authenticateRequest(request); + if (!authentication) return responses.notAuthenticatedResponse(); + const survey = await getSurvey(params.surveyId); + if (!survey) { + return responses.notFoundResponse("Survey", params.surveyId); + } + if (survey.environmentId !== authentication.environmentId) { + throw new Error("Unauthorized"); + } + + if (!survey.singleUse || !survey.singleUse.enabled) { + return responses.badRequestResponse("Single use links are not enabled for this survey"); + } + const searchParams = request.nextUrl.searchParams; + const limit = searchParams.get("limit") ? Number(searchParams.get("limit")) : 10; + + if (limit < 1) { + return responses.badRequestResponse("Limit cannot be less than 1"); + } + + if (limit > 5000) { + return responses.badRequestResponse("Limit cannot be more than 5000"); + } + + const singleUseIds = generateSurveySingleUseIds(limit, survey.singleUse.isEncrypted); + + // map single use ids to survey links + const surveyLinks = singleUseIds.map( + (singleUseId) => `${process.env.WEBAPP_URL}/s/${survey.id}?suId=${singleUseId}` + ); + + return responses.successResponse(surveyLinks); + } catch (error) { + return handleErrorResponse(error); + } +}; diff --git a/packages/lib/utils/singleUseSurveys.ts b/packages/lib/utils/singleUseSurveys.ts index f6d75d0387..378f88db72 100644 --- a/packages/lib/utils/singleUseSurveys.ts +++ b/packages/lib/utils/singleUseSurveys.ts @@ -13,6 +13,16 @@ export const generateSurveySingleUseId = (isEncrypted: boolean): string => { return encryptedCuid; }; +export const generateSurveySingleUseIds = (count: number, isEncrypted: boolean): string[] => { + const singleUseIds: string[] = []; + + for (let i = 0; i < count; i++) { + singleUseIds.push(generateSurveySingleUseId(isEncrypted)); + } + + return singleUseIds; +}; + // validate the survey single use id export const validateSurveySingleUseId = (surveySingleUseId: string): string | undefined => { try { From 2bbeb040c2265a1b1e96f1961d4e11af52c8ad47 Mon Sep 17 00:00:00 2001 From: Matti Nannt Date: Thu, 22 Aug 2024 13:14:08 +0200 Subject: [PATCH 05/19] fix: use createdAt instead of updatedAt in responseCard (#3040) --- packages/lib/response/service.ts | 2 +- .../components/SingleResponseCardHeader.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/lib/response/service.ts b/packages/lib/response/service.ts index bed519ebd6..509d3193c4 100644 --- a/packages/lib/response/service.ts +++ b/packages/lib/response/service.ts @@ -114,7 +114,7 @@ export const getResponsesByPersonId = reactCache( take: page ? ITEMS_PER_PAGE : undefined, skip: page ? ITEMS_PER_PAGE * (page - 1) : undefined, orderBy: { - updatedAt: "asc", + createdAt: "desc", }, }); diff --git a/packages/ui/SingleResponseCard/components/SingleResponseCardHeader.tsx b/packages/ui/SingleResponseCard/components/SingleResponseCardHeader.tsx index 877de0683b..f705cf3c57 100644 --- a/packages/ui/SingleResponseCard/components/SingleResponseCardHeader.tsx +++ b/packages/ui/SingleResponseCard/components/SingleResponseCardHeader.tsx @@ -168,8 +168,8 @@ export const SingleResponseCardHeader = ({
-
); From 89ffe99dccfc5d3cebfc1b27625b142104ba0011 Mon Sep 17 00:00:00 2001 From: ty kerr Date: Fri, 23 Aug 2024 03:43:49 -0700 Subject: [PATCH 08/19] feat: react native sdk (#2565) Co-authored-by: tykerr Co-authored-by: Dhruwang Co-authored-by: Dhruwang Jariwala <67850763+Dhruwang@users.noreply.github.com> Co-authored-by: pandeymangg Co-authored-by: Matthias Nannt --- apps/demo-react-native/.env.example | 2 + apps/demo-react-native/.eslintrc.js | 7 + apps/demo-react-native/.gitignore | 35 + apps/demo-react-native/.npmrc | 0 apps/demo-react-native/app.json | 34 + .../assets/adaptive-icon.png | Bin 0 -> 17547 bytes apps/demo-react-native/assets/favicon.png | Bin 0 -> 1466 bytes apps/demo-react-native/assets/icon.png | Bin 0 -> 22380 bytes apps/demo-react-native/assets/splash.png | Bin 0 -> 47346 bytes apps/demo-react-native/babel.config.js | 6 + apps/demo-react-native/index.js | 7 + apps/demo-react-native/metro.config.js | 21 + apps/demo-react-native/package.json | 28 + apps/demo-react-native/src/app.tsx | 52 + apps/demo-react-native/tsconfig.json | 6 + .../framework-guides/components/Libraries.tsx | 8 +- .../app/app-surveys/framework-guides/page.mdx | 60 + apps/docs/app/app-surveys/quickstart/page.mdx | 8 +- .../developer-docs/app-survey-rn-sdk/page.mdx | 127 + .../contributing/get-started/page.mdx | 10 +- apps/docs/lib/navigation.ts | 5 +- .../product/look/components/EditLogo.tsx | 3 +- .../components/ThemeStylingPreviewSurvey.tsx | 6 +- .../app/sync/[userId]/route.ts | 2 - .../people/[userId]/attributes/route.ts | 1 + .../[environmentId]/storage/local/route.ts | 8 +- .../s/[surveyId]/components/LinkSurvey.tsx | 3 +- packages/api/src/api/client/storage.ts | 42 +- packages/config-typescript/react-library.json | 2 +- .../react-native-library.json | 13 + packages/js-core/src/app/lib/actions.ts | 17 +- packages/js-core/src/app/lib/attributes.ts | 17 +- packages/js-core/src/app/lib/config.ts | 72 +- .../js-core/src/app/lib/eventListeners.ts | 4 +- packages/js-core/src/app/lib/initialize.ts | 36 +- packages/js-core/src/app/lib/sync.ts | 32 +- packages/js-core/src/app/lib/widget.ts | 63 +- packages/js-core/src/shared/commandQueue.ts | 2 +- packages/js-core/src/shared/constants.ts | 3 + packages/js-core/src/shared/errors.ts | 10 +- packages/js-core/src/shared/utils.ts | 74 +- packages/js-core/src/website/lib/config.ts | 14 +- .../js-core/src/website/lib/initialize.ts | 7 +- packages/js-core/src/website/lib/widget.ts | 14 +- packages/js/index.html | 2 +- packages/react-native/.eslintrc.cjs | 11 + packages/react-native/.gitignore | 28 + packages/react-native/LICENSE | 9 + packages/react-native/README.md | 47 + packages/react-native/package.json | 59 + packages/react-native/src/formbricks.tsx | 44 + packages/react-native/src/index.ts | 6 + packages/react-native/src/lib/actions.ts | 70 + .../react-native/src/lib/command-queue.ts | 77 + packages/react-native/src/lib/config.ts | 11 + packages/react-native/src/lib/index.ts | 21 + packages/react-native/src/lib/initialize.ts | 154 + packages/react-native/src/lib/person.ts | 28 + packages/react-native/src/lib/survey-store.ts | 46 + packages/react-native/src/survey-web-view.tsx | 389 ++ packages/react-native/tsconfig.json | 8 + packages/react-native/vite.config.ts | 28 + .../src/components/general/FileInput.tsx | 100 +- .../general/QuestionConditional.tsx | 3 +- .../questions/FileUploadQuestion.tsx | 3 +- packages/types/formbricks-surveys.ts | 9 +- packages/types/js.ts | 22 +- packages/types/surveys/types.ts | 2 +- packages/ui/PreviewSurvey/index.tsx | 3 +- packages/ui/Survey/index.tsx | 2 +- pnpm-lock.yaml | 5313 ++++++++++++++--- turbo.json | 15 +- 72 files changed, 6300 insertions(+), 1071 deletions(-) create mode 100644 apps/demo-react-native/.env.example create mode 100644 apps/demo-react-native/.eslintrc.js create mode 100644 apps/demo-react-native/.gitignore create mode 100644 apps/demo-react-native/.npmrc create mode 100644 apps/demo-react-native/app.json create mode 100644 apps/demo-react-native/assets/adaptive-icon.png create mode 100644 apps/demo-react-native/assets/favicon.png create mode 100644 apps/demo-react-native/assets/icon.png create mode 100644 apps/demo-react-native/assets/splash.png create mode 100644 apps/demo-react-native/babel.config.js create mode 100644 apps/demo-react-native/index.js create mode 100644 apps/demo-react-native/metro.config.js create mode 100644 apps/demo-react-native/package.json create mode 100644 apps/demo-react-native/src/app.tsx create mode 100644 apps/demo-react-native/tsconfig.json create mode 100644 apps/docs/app/developer-docs/app-survey-rn-sdk/page.mdx create mode 100644 packages/config-typescript/react-native-library.json create mode 100644 packages/js-core/src/shared/constants.ts create mode 100644 packages/react-native/.eslintrc.cjs create mode 100644 packages/react-native/.gitignore create mode 100644 packages/react-native/LICENSE create mode 100644 packages/react-native/README.md create mode 100644 packages/react-native/package.json create mode 100644 packages/react-native/src/formbricks.tsx create mode 100644 packages/react-native/src/index.ts create mode 100644 packages/react-native/src/lib/actions.ts create mode 100644 packages/react-native/src/lib/command-queue.ts create mode 100644 packages/react-native/src/lib/config.ts create mode 100644 packages/react-native/src/lib/index.ts create mode 100644 packages/react-native/src/lib/initialize.ts create mode 100644 packages/react-native/src/lib/person.ts create mode 100644 packages/react-native/src/lib/survey-store.ts create mode 100644 packages/react-native/src/survey-web-view.tsx create mode 100644 packages/react-native/tsconfig.json create mode 100644 packages/react-native/vite.config.ts diff --git a/apps/demo-react-native/.env.example b/apps/demo-react-native/.env.example new file mode 100644 index 0000000000..3a2d97bdc4 --- /dev/null +++ b/apps/demo-react-native/.env.example @@ -0,0 +1,2 @@ +EXPO_PUBLIC_API_HOST=http://192.168.178.20:3000 +EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=clzr04nkd000bcdl110j0ijyq diff --git a/apps/demo-react-native/.eslintrc.js b/apps/demo-react-native/.eslintrc.js new file mode 100644 index 0000000000..4d8dbbccec --- /dev/null +++ b/apps/demo-react-native/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ["@formbricks/eslint-config/react.js"], + parserOptions: { + project: "tsconfig.json", + tsconfigRootDir: __dirname, + }, +}; diff --git a/apps/demo-react-native/.gitignore b/apps/demo-react-native/.gitignore new file mode 100644 index 0000000000..05647d55c7 --- /dev/null +++ b/apps/demo-react-native/.gitignore @@ -0,0 +1,35 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ + +# Native +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo diff --git a/apps/demo-react-native/.npmrc b/apps/demo-react-native/.npmrc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/demo-react-native/app.json b/apps/demo-react-native/app.json new file mode 100644 index 0000000000..b7701d49b3 --- /dev/null +++ b/apps/demo-react-native/app.json @@ -0,0 +1,34 @@ +{ + "expo": { + "name": "react-native-demo", + "slug": "react-native-demo", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/icon.png", + "userInterfaceStyle": "light", + "splash": { + "image": "./assets/splash.png", + "resizeMode": "contain", + "backgroundColor": "#ffffff" + }, + "jsEngine": "hermes", + "assetBundlePatterns": ["**/*"], + "ios": { + "supportsTablet": true, + "infoPlist": { + "NSCameraUsageDescription": "Take pictures for certain activities.", + "NSPhotoLibraryUsageDescription": "Select pictures for certain activities.", + "NSMicrophoneUsageDescription": "Need microphone access for recording videos." + } + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/adaptive-icon.png", + "backgroundColor": "#ffffff" + } + }, + "web": { + "favicon": "./assets/favicon.png" + } + } +} diff --git a/apps/demo-react-native/assets/adaptive-icon.png b/apps/demo-react-native/assets/adaptive-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..03d6f6b6c6727954aec1d8206222769afd178d8d GIT binary patch literal 17547 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 literal 0 HcmV?d00001 diff --git a/apps/demo-react-native/assets/icon.png b/apps/demo-react-native/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a0b1526fc7b78680fd8d733dbc6113e1af695487 GIT binary patch literal 22380 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 literal 0 HcmV?d00001 diff --git a/apps/demo-react-native/assets/splash.png b/apps/demo-react-native/assets/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..0e89705a9436743e42954d3744a0e7ff0d3d4701 GIT binary patch literal 47346 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 literal 0 HcmV?d00001 diff --git a/apps/demo-react-native/babel.config.js b/apps/demo-react-native/babel.config.js new file mode 100644 index 0000000000..29433509d7 --- /dev/null +++ b/apps/demo-react-native/babel.config.js @@ -0,0 +1,6 @@ +module.exports = function babel(api) { + api.cache(true); + return { + presets: ["babel-preset-expo"], + }; +}; diff --git a/apps/demo-react-native/index.js b/apps/demo-react-native/index.js new file mode 100644 index 0000000000..c2ccbfc1d6 --- /dev/null +++ b/apps/demo-react-native/index.js @@ -0,0 +1,7 @@ +import { registerRootComponent } from "expo"; +import { LogBox } from "react-native"; +import App from "./src/app"; + +registerRootComponent(App); + +LogBox.ignoreAllLogs(); diff --git a/apps/demo-react-native/metro.config.js b/apps/demo-react-native/metro.config.js new file mode 100644 index 0000000000..6bd167c023 --- /dev/null +++ b/apps/demo-react-native/metro.config.js @@ -0,0 +1,21 @@ +// Learn more https://docs.expo.io/guides/customizing-metro +const path = require("node:path"); +const { getDefaultConfig } = require("expo/metro-config"); + +// Find the workspace root, this can be replaced with `find-yarn-workspace-root` +const workspaceRoot = path.resolve(__dirname, "../.."); +const projectRoot = __dirname; + +const config = getDefaultConfig(projectRoot); + +// 1. Watch all files within the monorepo +config.watchFolders = [workspaceRoot]; +// 2. Let Metro know where to resolve packages, and in what order +config.resolver.nodeModulesPaths = [ + path.resolve(projectRoot, "node_modules"), + path.resolve(workspaceRoot, "node_modules"), +]; +// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths` +config.resolver.disableHierarchicalLookup = true; + +module.exports = config; diff --git a/apps/demo-react-native/package.json b/apps/demo-react-native/package.json new file mode 100644 index 0000000000..186d52e822 --- /dev/null +++ b/apps/demo-react-native/package.json @@ -0,0 +1,28 @@ +{ + "name": "@formbricks/demo-react-native", + "version": "1.0.0", + "main": "./index.js", + "scripts": { + "dev": "expo start", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web", + "eject": "expo eject", + "clean": "rimraf .turbo node_modules .expo" + }, + "dependencies": { + "@formbricks/js": "workspace:*", + "@formbricks/react-native": "workspace:*", + "expo": "^51.0.26", + "expo-status-bar": "~1.12.1", + "react": "^18.2.0", + "react-native": "^0.74.4", + "react-native-webview": "13.8.6" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@types/react": "~18.2.79", + "typescript": "^5.3.3" + }, + "private": true +} diff --git a/apps/demo-react-native/src/app.tsx b/apps/demo-react-native/src/app.tsx new file mode 100644 index 0000000000..829fe1637c --- /dev/null +++ b/apps/demo-react-native/src/app.tsx @@ -0,0 +1,52 @@ +import { StatusBar } from "expo-status-bar"; +import { Button, LogBox, StyleSheet, Text, View } from "react-native"; +import Formbricks, { track } from "@formbricks/react-native"; + +LogBox.ignoreAllLogs(); + +export default function App(): JSX.Element { + if (!process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID) { + throw new Error("EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID is required"); + } + + if (!process.env.EXPO_PUBLIC_API_HOST) { + throw new Error("EXPO_PUBLIC_API_HOST is required"); + } + + const config = { + environmentId: process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID, + apiHost: process.env.EXPO_PUBLIC_API_HOST, + userId: "random user id", + attributes: { + language: "en", + testAttr: "attr-test", + }, + }; + + return ( + + Formbricks React Native SDK Demo + + diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx index 4b7258e488..30e6d3ea6d 100644 --- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx +++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/EditWelcomeCard.tsx @@ -1,6 +1,7 @@ "use client"; import * as Collapsible from "@radix-ui/react-collapsible"; +import { Hand } from "lucide-react"; import { usePathname } from "next/navigation"; import { useState } from "react"; import { LocalizedEditor } from "@formbricks/ee/multi-language/components/localized-editor"; @@ -66,7 +67,7 @@ export const EditWelcomeCard = ({ "flex w-10 items-center justify-center rounded-l-lg border-b border-l border-t group-aria-expanded:rounded-bl-none", isInvalid ? "bg-red-400" : "bg-white group-hover:bg-slate-50" )}> -

+ -

🥷

+ ) : ( -
- 🕵️ - -
{isFetching ? "Fetching surveys..." : "No surveys found"}
+
+ +
+ {isFetching ? "Fetching surveys..." : "No surveys found"} +
)}
From cf783ea4800a6d2bcca690f92d9b004488b227f0 Mon Sep 17 00:00:00 2001 From: Anshuman Pandey <54475686+pandeymangg@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:13:25 +0530 Subject: [PATCH 12/19] fix: s3 file upload (#3050) Co-authored-by: Matti Nannt --- packages/api/package.json | 7 +- packages/api/src/api/client/storage.ts | 17 +- packages/api/vite.config.js | 11 +- packages/lib/storage/service.ts | 1 + pnpm-lock.yaml | 733 ++++++++++++++++++++++--- 5 files changed, 675 insertions(+), 94 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index b04a62aecb..64a4fcc9d2 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -35,10 +35,13 @@ }, "devDependencies": { "@formbricks/config-typescript": "workspace:*", - "@formbricks/types": "workspace:*", "@formbricks/eslint-config": "workspace:*", + "@formbricks/types": "workspace:*", + "@rollup/plugin-inject": "^5.0.5", + "buffer": "^6.0.3", "terser": "^5.31.6", "vite": "^5.4.1", - "vite-plugin-dts": "^3.9.1" + "vite-plugin-dts": "^3.9.1", + "vite-plugin-node-polyfills": "^0.22.0" } } diff --git a/packages/api/src/api/client/storage.ts b/packages/api/src/api/client/storage.ts index 8574c8e87a..2bc8f7f39c 100644 --- a/packages/api/src/api/client/storage.ts +++ b/packages/api/src/api/client/storage.ts @@ -1,4 +1,5 @@ /* eslint-disable no-console -- used for error logging */ +import { Buffer } from "node:buffer"; import type { TUploadFileConfig, TUploadFileResponse } from "@formbricks/types/storage"; export class StorageAPI { @@ -63,11 +64,23 @@ export class StorageAPI { } const formData: Record = {}; + const formDataForS3 = new FormData(); if (presignedFields) { Object.keys(presignedFields).forEach((key) => { - formData[key] = presignedFields[key]; + formDataForS3.append(key, presignedFields[key]); }); + + try { + const buffer = Buffer.from(file.base64.split(",")[1], "base64"); + const blob = new Blob([buffer], { type: file.type }); + + formDataForS3.append("file", blob); + } catch (buffErr) { + console.error({ buffErr }); + + throw new Error("Error uploading file"); + } } formData.fileBase64String = file.base64; @@ -86,7 +99,7 @@ export class StorageAPI { }, } : {}), - body: JSON.stringify(formData), + body: presignedFields ? formDataForS3 : JSON.stringify(formData), }); } catch (err) { console.error("Error uploading file", err); diff --git a/packages/api/vite.config.js b/packages/api/vite.config.js index 6ca4f9a1bb..cb325b77ef 100644 --- a/packages/api/vite.config.js +++ b/packages/api/vite.config.js @@ -1,6 +1,7 @@ import { resolve } from "path"; import { defineConfig } from "vite"; import dts from "vite-plugin-dts"; +import { nodePolyfills } from "vite-plugin-node-polyfills"; export default defineConfig({ build: { @@ -16,5 +17,13 @@ export default defineConfig({ fileName: "index", }, }, - plugins: [dts({ rollupTypes: true })], + plugins: [ + dts({ rollupTypes: true }), + nodePolyfills({ + include: ["buffer"], + globals: { + Buffer: true, + }, + }), + ], }); diff --git a/packages/lib/storage/service.ts b/packages/lib/storage/service.ts index 510584065f..cdcbd62671 100644 --- a/packages/lib/storage/service.ts +++ b/packages/lib/storage/service.ts @@ -232,6 +232,7 @@ export const getS3UploadSignedUrl = async ( Key: `${environmentId}/${accessType}/${fileName}`, Fields: { "Content-Type": contentType, + "Content-Encoding": "base64", }, Conditions: postConditions, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91a982b6e6..2751ec7b54 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,19 +130,19 @@ importers: version: 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@headlessui/tailwindcss': specifier: ^0.2.1 - version: 0.2.1(tailwindcss@3.4.7(ts-node@10.9.2(typescript@5.5.4))) + version: 0.2.1(tailwindcss@3.4.7(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4))) '@mapbox/rehype-prism': specifier: ^0.9.0 version: 0.9.0 '@mdx-js/loader': specifier: ^3.0.1 - version: 3.0.1(webpack@5.93.0) + version: 3.0.1(webpack@5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11))) '@mdx-js/react': specifier: ^3.0.1 version: 3.0.1(@types/react@18.3.3)(react@18.3.1) '@next/mdx': specifier: 14.2.5 - version: 14.2.5(@mdx-js/loader@3.0.1(webpack@5.93.0))(@mdx-js/react@3.0.1(@types/react@18.3.3)(react@18.3.1)) + version: 14.2.5(@mdx-js/loader@3.0.1(webpack@5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11))))(@mdx-js/react@3.0.1(@types/react@18.3.3)(react@18.3.1)) '@paralleldrive/cuid2': specifier: ^2.2.2 version: 2.2.2 @@ -151,7 +151,7 @@ importers: version: 2.2.1 '@tailwindcss/typography': specifier: ^0.5.13 - version: 0.5.13(tailwindcss@3.4.7(ts-node@10.9.2(typescript@5.5.4))) + version: 0.5.13(tailwindcss@3.4.7(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4))) acorn: specifier: ^8.12.1 version: 8.12.1 @@ -247,7 +247,7 @@ importers: version: 1.2.1 tailwindcss: specifier: ^3.4.7 - version: 3.4.7(ts-node@10.9.2(typescript@5.5.4)) + version: 3.4.7(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) unist-util-filter: specifier: ^5.0.1 version: 5.0.1 @@ -300,7 +300,7 @@ importers: version: 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2))) '@storybook/addon-interactions': specifier: ^8.2.9 - version: 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(vitest@2.0.5(@types/node@22.3.0)(terser@5.31.6)) + version: 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6)) '@storybook/addon-links': specifier: ^8.2.9 version: 8.2.9(react@18.3.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2))) @@ -315,10 +315,10 @@ importers: version: 8.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(typescript@5.5.4) '@storybook/react-vite': specifier: ^8.2.9 - version: 8.2.9(@preact/preset-vite@2.9.0(@babel/core@7.25.2)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.19.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)) + version: 8.2.9(@preact/preset-vite@2.9.0(@babel/core@7.25.2)(preact@10.23.2)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.19.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)) '@storybook/test': specifier: ^8.2.9 - version: 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(vitest@2.0.5(@types/node@22.3.0)(terser@5.31.6)) + version: 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6)) '@typescript-eslint/eslint-plugin': specifier: ^8.0.0 version: 8.0.0(@typescript-eslint/parser@8.0.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) @@ -342,7 +342,7 @@ importers: version: 8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)) tsup: specifier: ^8.2.4 - version: 8.2.4(@microsoft/api-extractor@7.43.0(@types/node@22.3.0))(@swc/core@1.3.101)(jiti@1.21.6)(postcss@8.4.41)(tsx@4.16.5)(typescript@5.5.4)(yaml@2.4.5) + version: 8.2.4(@microsoft/api-extractor@7.43.0(@types/node@22.3.0))(@swc/core@1.3.101(@swc/helpers@0.5.11))(jiti@1.21.6)(postcss@8.4.41)(tsx@4.16.5)(typescript@5.5.4)(yaml@2.4.5) vite: specifier: ^5.4.1 version: 5.4.1(@types/node@22.3.0)(terser@5.31.6) @@ -533,6 +533,12 @@ importers: '@formbricks/types': specifier: workspace:* version: link:../types + '@rollup/plugin-inject': + specifier: ^5.0.5 + version: 5.0.5(rollup@4.19.1) + buffer: + specifier: ^6.0.3 + version: 6.0.3 terser: specifier: ^5.31.6 version: 5.31.6 @@ -542,6 +548,9 @@ importers: vite-plugin-dts: specifier: ^3.9.1 version: 3.9.1(@types/node@22.3.0)(rollup@4.19.1)(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)) + vite-plugin-node-polyfills: + specifier: ^0.22.0 + version: 0.22.0(rollup@4.19.1)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)) packages/config-eslint: devDependencies: @@ -556,7 +565,7 @@ importers: version: 8.0.0(eslint@8.57.0)(typescript@5.5.4) '@vercel/style-guide': specifier: ^6.0.0 - version: 6.0.0(@next/eslint-plugin-next@14.2.5)(eslint@8.57.0)(prettier@3.3.3)(typescript@5.5.4)(vitest@2.0.5) + version: 6.0.0(@next/eslint-plugin-next@14.2.5)(eslint@8.57.0)(prettier@3.3.3)(typescript@5.5.4)(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6)) eslint-config-next: specifier: ^14.2.5 version: 14.2.5(eslint@8.57.0)(typescript@5.5.4) @@ -592,10 +601,10 @@ importers: devDependencies: '@tailwindcss/forms': specifier: ^0.5.7 - version: 0.5.7(tailwindcss@3.4.10(ts-node@10.9.2)) + version: 0.5.7(tailwindcss@3.4.10(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4))) '@tailwindcss/typography': specifier: ^0.5.14 - version: 0.5.14(tailwindcss@3.4.10(ts-node@10.9.2)) + version: 0.5.14(tailwindcss@3.4.10(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4))) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.41) @@ -604,7 +613,7 @@ importers: version: 8.4.41 tailwindcss: specifier: ^3.4.10 - version: 3.4.10(ts-node@10.9.2(@types/node@22.3.0)(typescript@5.5.4)) + version: 3.4.10(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) packages/config-typescript: devDependencies: @@ -656,7 +665,7 @@ importers: version: 3.0.4(prisma@5.18.0)(typescript@5.5.4) ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.3.101)(@types/node@22.3.0)(typescript@5.5.4) + version: 10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4) zod: specifier: ^3.23.8 version: 3.23.8 @@ -756,7 +765,7 @@ importers: version: 6.9.14 react-email: specifier: ^2.1.6 - version: 2.1.6(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.11)(eslint@8.57.0)(ts-node@10.9.2) + version: 2.1.6(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.11)(eslint@8.57.0)(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) devDependencies: '@types/nodemailer': specifier: ^6.4.15 @@ -906,7 +915,7 @@ importers: version: 16.4.5 ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.3.101)(@types/node@22.3.0)(typescript@5.5.4) + version: 10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4) vitest: specifier: ^2.0.5 version: 2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6) @@ -997,7 +1006,7 @@ importers: version: 14.2.3 tailwindcss: specifier: ^3.4.10 - version: 3.4.10(ts-node@10.9.2(@types/node@22.3.0)(typescript@5.5.4)) + version: 3.4.10(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) terser: specifier: ^5.31.6 version: 5.31.6 @@ -4723,6 +4732,15 @@ packages: rollup: optional: true + '@rollup/plugin-inject@5.0.5': + resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/pluginutils@4.2.1': resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} engines: {node: '>= 8.0.0'} @@ -6437,6 +6455,12 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + asn1.js@4.10.1: + resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + + assert@2.1.0: + resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} @@ -6603,6 +6627,12 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + + bn.js@5.2.1: + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + body-parser@1.20.2: resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -6644,9 +6674,34 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + browser-assert@1.2.1: resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==} + browser-resolve@2.0.0: + resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} + + browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + + browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + + browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + + browserify-rsa@4.1.0: + resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} + + browserify-sign@4.2.3: + resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==} + engines: {node: '>= 0.12'} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + browserslist@4.23.1: resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -6678,6 +6733,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -6688,6 +6746,9 @@ packages: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + builtins@1.0.3: resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} @@ -6886,6 +6947,9 @@ packages: resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} engines: {node: '>=8'} + cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} @@ -7110,6 +7174,12 @@ packages: resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} engines: {node: ^14.18.0 || >=16.10.0} + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + content-disposition@0.5.2: resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==} engines: {node: '>= 0.6'} @@ -7170,6 +7240,15 @@ packages: resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} engines: {node: '>= 10'} + create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + + create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -7190,6 +7269,9 @@ packages: crypt@0.0.2: resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + crypto-browserify@3.12.0: + resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} @@ -7394,6 +7476,9 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -7439,6 +7524,9 @@ packages: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} + diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + dijkstrajs@1.0.3: resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} @@ -7466,6 +7554,10 @@ packages: dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + domain-browser@4.23.0: + resolution: {integrity: sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==} + engines: {node: '>=10'} + domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} @@ -7525,6 +7617,9 @@ packages: electron-to-chromium@1.5.11: resolution: {integrity: sha512-R1CccCDYqndR25CaXFd6hp/u9RaaMcftMkphmvuepXr5b1vfLkRml6aWVeBhXJ7rbevHkKEMJtz8XqPf7ffmew==} + elliptic@6.5.7: + resolution: {integrity: sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==} + emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} @@ -7942,6 +8037,9 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + exec-async@2.2.0: resolution: {integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==} @@ -8522,6 +8620,17 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + hash-base@3.0.4: + resolution: {integrity: sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==} + engines: {node: '>=4'} + + hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + hasha@5.2.2: resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} engines: {node: '>=8'} @@ -8587,6 +8696,9 @@ packages: highlight-words-core@1.2.2: resolution: {integrity: sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg==} + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -8627,6 +8739,9 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} + https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -8889,6 +9004,10 @@ packages: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} + is-nan@1.3.2: + resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} + engines: {node: '>= 0.4'} + is-negative-zero@2.0.3: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} @@ -9030,6 +9149,10 @@ packages: resolution: {integrity: sha512-7xyjuzBf3P/HBt0PbOpmv5LuV38TmfvidBFvgyuSWVMLwCGDITBPHWsBZ/L1a8DpcGz5PEintBeGdlrKzUqt5A==} engines: {node: '>=18'} + isomorphic-timers-promises@1.0.1: + resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} + engines: {node: '>=10'} + isomorphic-ws@4.0.1: resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} peerDependencies: @@ -9610,6 +9733,9 @@ packages: engines: {node: '>=0.10'} hasBin: true + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + md5@2.2.1: resolution: {integrity: sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==} @@ -9881,6 +10007,10 @@ packages: resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} + miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + mime-db@1.33.0: resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==} engines: {node: '>= 0.6'} @@ -9936,6 +10066,12 @@ packages: resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} hasBin: true + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + minimatch@10.0.1: resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} engines: {node: 20 || >=22} @@ -10254,6 +10390,10 @@ packages: node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + node-stdlib-browser@1.2.0: + resolution: {integrity: sha512-VSjFxUhRhkyed8AtLwSCkMrJRfQ3e2lGtG3sP6FEgaLKBBbxM/dLfjRe1+iLhjvyLFW3tBQ8+c0pcOtXGbAZJg==} + engines: {node: '>=10'} + node-stream-zip@1.15.0: resolution: {integrity: sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==} engines: {node: '>=0.12.0'} @@ -10345,6 +10485,10 @@ packages: object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -10439,6 +10583,9 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} + os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + os-homedir@1.0.2: resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} engines: {node: '>=0.10.0'} @@ -10511,6 +10658,9 @@ packages: pako@0.2.9: resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + papaparse@5.4.1: resolution: {integrity: sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==} @@ -10521,6 +10671,10 @@ packages: parenthesis@3.1.8: resolution: {integrity: sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==} + parse-asn1@5.1.7: + resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} + engines: {node: '>= 0.10'} + parse-css-color@0.2.1: resolution: {integrity: sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==} @@ -10620,6 +10774,10 @@ packages: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} + pbkdf2@3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} @@ -10673,6 +10831,10 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + pkg-dir@5.0.0: + resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} + engines: {node: '>=10'} + playwright-core@1.45.3: resolution: {integrity: sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==} engines: {node: '>=18'} @@ -10997,6 +11159,9 @@ packages: psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} @@ -11028,6 +11193,14 @@ packages: resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==} engines: {node: '>=0.6'} + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + querystring@0.2.1: resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==} engines: {node: '>=0.4.x'} @@ -11045,6 +11218,9 @@ packages: randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + range-parser@1.2.0: resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==} engines: {node: '>= 0.6'} @@ -11527,6 +11703,9 @@ packages: engines: {node: 20 || >=22} hasBin: true + ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + rollup@3.29.4: resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -11680,6 +11859,10 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} @@ -11913,10 +12096,16 @@ packages: resolution: {integrity: sha512-S7Q/Yt4A+nu1O23rg39lQvBqL2Vg+PKXbserDWUR4LFJtfmoZ2xGO8oFIhJmvvhjUBvolw1q7QDeswPq2i0sGw==} hasBin: true + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + stream-buffers@2.2.0: resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} engines: {node: '>= 0.10.0'} + stream-http@3.2.0: + resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} @@ -12240,6 +12429,10 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} @@ -12419,6 +12612,9 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tty-browserify@0.0.1: + resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + turbo-darwin-64@2.0.11: resolution: {integrity: sha512-YlHEEhcm+jI1BSZoLugGHUWDfRXaNaQIv7tGQBfadYjo9kixBnqoTOU6s1ubOrQMID+lizZZQs79GXwqM6vohg==} cpu: [x64] @@ -12710,6 +12906,10 @@ packages: url-template@2.0.8: resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==} + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + use-callback-ref@1.3.2: resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==} engines: {node: '>=10'} @@ -12807,6 +13007,11 @@ packages: vite: optional: true + vite-plugin-node-polyfills@0.22.0: + resolution: {integrity: sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA==} + peerDependencies: + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + vite-tsconfig-paths@5.0.1: resolution: {integrity: sha512-yqwv+LstU7NwPeNqajZzLEBVpUFU6Dugtb2P84FXuvaoYA+/70l9MHE+GYfYAycVyPSDYZ7mjOFuYBRqlEpTig==} peerDependencies: @@ -12880,6 +13085,9 @@ packages: vlq@1.0.1: resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==} + vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} @@ -15877,9 +16085,9 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@headlessui/tailwindcss@0.2.1(tailwindcss@3.4.7(ts-node@10.9.2(typescript@5.5.4)))': + '@headlessui/tailwindcss@0.2.1(tailwindcss@3.4.7(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)))': dependencies: - tailwindcss: 3.4.7(ts-node@10.9.2(typescript@5.5.4)) + tailwindcss: 3.4.7(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) '@hookform/resolvers@3.9.0(react-hook-form@7.52.2(react@18.3.1))': dependencies: @@ -16253,11 +16461,11 @@ snapshots: refractor: 3.6.0 unist-util-visit: 2.0.3 - '@mdx-js/loader@3.0.1(webpack@5.93.0)': + '@mdx-js/loader@3.0.1(webpack@5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11)))': dependencies: '@mdx-js/mdx': 3.0.1 source-map: 0.7.4 - webpack: 5.93.0 + webpack: 5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11)) transitivePeerDependencies: - supports-color @@ -16349,11 +16557,11 @@ snapshots: dependencies: glob: 10.3.10 - '@next/mdx@14.2.5(@mdx-js/loader@3.0.1(webpack@5.93.0))(@mdx-js/react@3.0.1(@types/react@18.3.3)(react@18.3.1))': + '@next/mdx@14.2.5(@mdx-js/loader@3.0.1(webpack@5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11))))(@mdx-js/react@3.0.1(@types/react@18.3.3)(react@18.3.1))': dependencies: source-map: 0.7.4 optionalDependencies: - '@mdx-js/loader': 3.0.1(webpack@5.93.0) + '@mdx-js/loader': 3.0.1(webpack@5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11))) '@mdx-js/react': 3.0.1(@types/react@18.3.3)(react@18.3.1) '@next/swc-darwin-arm64@14.1.4': @@ -18466,6 +18674,14 @@ snapshots: optionalDependencies: rollup: 3.29.4 + '@rollup/plugin-inject@5.0.5(rollup@4.19.1)': + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.19.1) + estree-walker: 2.0.2 + magic-string: 0.30.10 + optionalDependencies: + rollup: 4.19.1 + '@rollup/pluginutils@4.2.1': dependencies: estree-walker: 2.0.2 @@ -19219,11 +19435,11 @@ snapshots: '@storybook/global': 5.0.0 storybook: 8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)) - '@storybook/addon-interactions@8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(vitest@2.0.5(@types/node@22.3.0)(terser@5.31.6))': + '@storybook/addon-interactions@8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6))': dependencies: '@storybook/global': 5.0.0 '@storybook/instrumenter': 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2))) - '@storybook/test': 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(vitest@2.0.5(@types/node@22.3.0)(terser@5.31.6)) + '@storybook/test': 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6)) polished: 4.3.1 storybook: 8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)) ts-dedent: 2.2.0 @@ -19292,7 +19508,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@storybook/builder-vite@8.2.9(@preact/preset-vite@2.9.0(@babel/core@7.25.2)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)))(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6))': + '@storybook/builder-vite@8.2.9(@preact/preset-vite@2.9.0(@babel/core@7.25.2)(preact@10.23.2)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)))(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6))': dependencies: '@storybook/csf-plugin': 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2))) '@types/find-cache-dir': 3.2.1 @@ -19394,11 +19610,11 @@ snapshots: react-dom: 18.3.1(react@18.3.1) storybook: 8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)) - '@storybook/react-vite@8.2.9(@preact/preset-vite@2.9.0(@babel/core@7.25.2)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.19.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6))': + '@storybook/react-vite@8.2.9(@preact/preset-vite@2.9.0(@babel/core@7.25.2)(preact@10.23.2)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.19.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.1(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)) '@rollup/pluginutils': 5.1.0(rollup@4.19.1) - '@storybook/builder-vite': 8.2.9(@preact/preset-vite@2.9.0(@babel/core@7.25.2)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)))(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)) + '@storybook/builder-vite': 8.2.9(@preact/preset-vite@2.9.0(@babel/core@7.25.2)(preact@10.23.2)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)))(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)) '@storybook/react': 8.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(typescript@5.5.4) find-up: 5.0.0 magic-string: 0.30.10 @@ -19445,12 +19661,12 @@ snapshots: optionalDependencies: typescript: 5.5.4 - '@storybook/test@8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(vitest@2.0.5(@types/node@22.3.0)(terser@5.31.6))': + '@storybook/test@8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2)))(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6))': dependencies: '@storybook/csf': 0.1.11 '@storybook/instrumenter': 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.25.2))) '@testing-library/dom': 10.1.0 - '@testing-library/jest-dom': 6.4.5(vitest@2.0.5(@types/node@22.3.0)(terser@5.31.6)) + '@testing-library/jest-dom': 6.4.5(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6)) '@testing-library/user-event': 14.5.2(@testing-library/dom@10.1.0) '@vitest/expect': 1.6.0 '@vitest/spy': 1.6.0 @@ -19548,26 +19764,26 @@ snapshots: optionalDependencies: typescript: 5.5.4 - '@tailwindcss/forms@0.5.7(tailwindcss@3.4.10(ts-node@10.9.2))': + '@tailwindcss/forms@0.5.7(tailwindcss@3.4.10(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)))': dependencies: mini-svg-data-uri: 1.4.4 - tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@22.3.0)(typescript@5.5.4)) + tailwindcss: 3.4.10(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) - '@tailwindcss/typography@0.5.13(tailwindcss@3.4.7(ts-node@10.9.2(typescript@5.5.4)))': + '@tailwindcss/typography@0.5.13(tailwindcss@3.4.7(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)))': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.7(ts-node@10.9.2(typescript@5.5.4)) + tailwindcss: 3.4.7(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) - '@tailwindcss/typography@0.5.14(tailwindcss@3.4.10(ts-node@10.9.2))': + '@tailwindcss/typography@0.5.14(tailwindcss@3.4.10(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)))': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@22.3.0)(typescript@5.5.4)) + tailwindcss: 3.4.10(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) '@tanstack/react-virtual@3.8.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -19588,7 +19804,7 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.4.5(vitest@2.0.5(@types/node@22.3.0)(terser@5.31.6))': + '@testing-library/jest-dom@6.4.5(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6))': dependencies: '@adobe/css-tools': 4.4.0 '@babel/runtime': 7.24.7 @@ -20210,7 +20426,7 @@ snapshots: next: 14.2.5(@opentelemetry/api@1.9.0)(@playwright/test@1.45.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - '@vercel/style-guide@6.0.0(@next/eslint-plugin-next@14.2.5)(eslint@8.57.0)(prettier@3.3.3)(typescript@5.5.4)(vitest@2.0.5)': + '@vercel/style-guide@6.0.0(@next/eslint-plugin-next@14.2.5)(eslint@8.57.0)(prettier@3.3.3)(typescript@5.5.4)(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6))': dependencies: '@babel/core': 7.24.7 '@babel/eslint-parser': 7.24.7(@babel/core@7.24.7)(eslint@8.57.0) @@ -20230,7 +20446,7 @@ snapshots: eslint-plugin-testing-library: 6.2.2(eslint@8.57.0)(typescript@5.5.4) eslint-plugin-tsdoc: 0.2.17 eslint-plugin-unicorn: 51.0.1(eslint@8.57.0) - eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)(vitest@2.0.5) + eslint-plugin-vitest: 0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6)) prettier-plugin-packagejson: 2.5.0(prettier@3.3.3) optionalDependencies: '@next/eslint-plugin-next': 14.2.5 @@ -20718,6 +20934,20 @@ snapshots: asap@2.0.6: {} + asn1.js@4.10.1: + dependencies: + bn.js: 4.12.0 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + assert@2.1.0: + dependencies: + call-bind: 1.0.7 + is-nan: 1.3.2 + object-is: 1.1.6 + object.assign: 4.1.5 + util: 0.12.5 + assertion-error@1.1.0: {} assertion-error@2.0.1: {} @@ -20910,6 +21140,10 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + bn.js@4.12.0: {} + + bn.js@5.2.1: {} + body-parser@1.20.2: dependencies: bytes: 3.1.2 @@ -20973,8 +21207,58 @@ snapshots: dependencies: fill-range: 7.1.1 + brorand@1.1.0: {} + browser-assert@1.2.1: {} + browser-resolve@2.0.0: + dependencies: + resolve: 1.22.8 + + browserify-aes@1.2.0: + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.4 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-cipher@1.0.1: + dependencies: + browserify-aes: 1.2.0 + browserify-des: 1.0.2 + evp_bytestokey: 1.0.3 + + browserify-des@1.0.2: + dependencies: + cipher-base: 1.0.4 + des.js: 1.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + + browserify-rsa@4.1.0: + dependencies: + bn.js: 5.2.1 + randombytes: 2.1.0 + + browserify-sign@4.2.3: + dependencies: + bn.js: 5.2.1 + browserify-rsa: 4.1.0 + create-hash: 1.2.0 + create-hmac: 1.1.7 + elliptic: 6.5.7 + hash-base: 3.0.4 + inherits: 2.0.4 + parse-asn1: 5.1.7 + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + + browserify-zlib@0.2.0: + dependencies: + pako: 1.0.11 + browserslist@4.23.1: dependencies: caniuse-lite: 1.0.30001636 @@ -21008,6 +21292,8 @@ snapshots: buffer-from@1.1.2: {} + buffer-xor@1.0.3: {} + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -21020,6 +21306,8 @@ snapshots: builtin-modules@3.3.0: {} + builtin-status-codes@3.0.0: {} + builtins@1.0.3: {} bundle-require@5.0.0(esbuild@0.23.0): @@ -21218,6 +21506,11 @@ snapshots: ci-info@4.0.0: {} + cipher-base@1.0.4: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + citty@0.1.6: dependencies: consola: 3.2.3 @@ -21439,6 +21732,10 @@ snapshots: consola@3.2.3: {} + console-browserify@1.2.0: {} + + constants-browserify@1.0.0: {} + content-disposition@0.5.2: {} content-disposition@0.5.4: @@ -21490,6 +21787,28 @@ snapshots: crc-32: 1.2.2 readable-stream: 3.6.2 + create-ecdh@4.0.4: + dependencies: + bn.js: 4.12.0 + elliptic: 6.5.7 + + create-hash@1.2.0: + dependencies: + cipher-base: 1.0.4 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + + create-hmac@1.1.7: + dependencies: + cipher-base: 1.0.4 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + create-require@1.1.1: {} cross-fetch@3.1.8(encoding@0.1.13): @@ -21520,6 +21839,20 @@ snapshots: crypt@0.0.2: {} + crypto-browserify@3.12.0: + dependencies: + browserify-cipher: 1.0.1 + browserify-sign: 4.2.3 + create-ecdh: 4.0.4 + create-hash: 1.2.0 + create-hmac: 1.1.7 + diffie-hellman: 5.0.3 + inherits: 2.0.4 + pbkdf2: 3.1.2 + public-encrypt: 4.0.3 + randombytes: 2.1.0 + randomfill: 1.0.4 + crypto-js@4.2.0: {} crypto-random-string@1.0.0: {} @@ -21692,6 +22025,11 @@ snapshots: dequal@2.0.3: {} + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + destroy@1.2.0: {} detect-element-overflow@1.4.2: {} @@ -21718,6 +22056,12 @@ snapshots: diff@4.0.2: {} + diffie-hellman@5.0.3: + dependencies: + bn.js: 4.12.0 + miller-rabin: 4.0.1 + randombytes: 2.1.0 + dijkstrajs@1.0.3: {} dir-glob@3.0.1: @@ -21744,6 +22088,8 @@ snapshots: domhandler: 5.0.3 entities: 4.5.0 + domain-browser@4.23.0: {} + domelementtype@2.3.0: {} domhandler@5.0.3: @@ -21808,6 +22154,16 @@ snapshots: electron-to-chromium@1.5.11: {} + elliptic@6.5.7: + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + emoji-regex@10.3.0: {} emoji-regex@8.0.0: {} @@ -22143,7 +22499,7 @@ snapshots: debug: 4.3.5 enhanced-resolve: 5.17.0 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 @@ -22183,6 +22539,16 @@ snapshots: transitivePeerDependencies: - supports-color + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.5.4) + eslint: 8.57.0 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + transitivePeerDependencies: + - supports-color + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 @@ -22387,13 +22753,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)(vitest@2.0.5): + eslint-plugin-vitest@0.3.26(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4)(vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6)): dependencies: '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 optionalDependencies: '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) - vitest: 2.0.5 + vitest: 2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6) transitivePeerDependencies: - supports-color - typescript @@ -22520,6 +22886,11 @@ snapshots: events@3.3.0: {} + evp_bytestokey@1.0.3: + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + exec-async@2.2.0: {} execa@1.0.0: @@ -23251,6 +23622,22 @@ snapshots: dependencies: has-symbols: 1.0.3 + hash-base@3.0.4: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + + hash-base@3.1.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + safe-buffer: 5.2.1 + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + hasha@5.2.2: dependencies: is-stream: 2.0.1 @@ -23356,6 +23743,12 @@ snapshots: highlight-words-core@1.2.2: {} + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 @@ -23411,6 +23804,8 @@ snapshots: transitivePeerDependencies: - supports-color + https-browserify@1.0.0: {} + https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 @@ -23648,6 +24043,11 @@ snapshots: is-map@2.0.3: {} + is-nan@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + is-negative-zero@2.0.3: {} is-number-object@1.0.7: @@ -23761,6 +24161,8 @@ snapshots: - supports-color - utf-8-validate + isomorphic-timers-promises@1.0.1: {} + isomorphic-ws@4.0.1(ws@8.17.1): dependencies: ws: 8.17.1 @@ -24412,6 +24814,12 @@ snapshots: dependencies: buffer-alloc: 1.2.0 + md5.js@1.3.5: + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + md5@2.2.1: dependencies: charenc: 0.0.2 @@ -25079,6 +25487,11 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + miller-rabin@4.0.1: + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + mime-db@1.33.0: {} mime-db@1.52.0: {} @@ -25109,6 +25522,10 @@ snapshots: mini-svg-data-uri@1.4.4: {} + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + minimatch@10.0.1: dependencies: brace-expansion: 2.0.1 @@ -25356,7 +25773,7 @@ snapshots: postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(@babel/core@7.24.5)(react@18.3.1) + styled-jsx: 5.1.1(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 14.2.5 '@next/swc-darwin-x64': 14.2.5 @@ -25480,6 +25897,36 @@ snapshots: node-releases@2.0.18: {} + node-stdlib-browser@1.2.0: + dependencies: + assert: 2.1.0 + browser-resolve: 2.0.0 + browserify-zlib: 0.2.0 + buffer: 5.7.1 + console-browserify: 1.2.0 + constants-browserify: 1.0.0 + create-require: 1.1.1 + crypto-browserify: 3.12.0 + domain-browser: 4.23.0 + events: 3.3.0 + https-browserify: 1.0.0 + isomorphic-timers-promises: 1.0.1 + os-browserify: 0.3.0 + path-browserify: 1.0.1 + pkg-dir: 5.0.0 + process: 0.11.10 + punycode: 1.4.1 + querystring-es3: 0.2.1 + readable-stream: 3.6.2 + stream-browserify: 3.0.0 + stream-http: 3.2.0 + string_decoder: 1.3.0 + timers-browserify: 2.0.12 + tty-browserify: 0.0.1 + url: 0.11.4 + util: 0.12.5 + vm-browserify: 1.1.2 + node-stream-zip@1.15.0: {} nodemailer@6.9.14: {} @@ -25568,6 +26015,11 @@ snapshots: object-inspect@1.13.1: {} + object-is@1.1.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + object-keys@1.1.1: {} object.assign@4.1.5: @@ -25695,6 +26147,8 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + os-browserify@0.3.0: {} + os-homedir@1.0.2: {} os-tmpdir@1.0.2: {} @@ -25757,6 +26211,8 @@ snapshots: pako@0.2.9: {} + pako@1.0.11: {} + papaparse@5.4.1: {} parent-module@1.0.1: @@ -25765,6 +26221,15 @@ snapshots: parenthesis@3.1.8: {} + parse-asn1@5.1.7: + dependencies: + asn1.js: 4.10.1 + browserify-aes: 1.2.0 + evp_bytestokey: 1.0.3 + hash-base: 3.0.4 + pbkdf2: 3.1.2 + safe-buffer: 5.2.1 + parse-css-color@0.2.1: dependencies: color-name: 1.1.4 @@ -25864,6 +26329,14 @@ snapshots: pathval@2.0.0: {} + pbkdf2@3.1.2: + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + peberminta@0.9.0: {} periscopic@3.1.0: @@ -25906,6 +26379,10 @@ snapshots: dependencies: find-up: 4.1.0 + pkg-dir@5.0.0: + dependencies: + find-up: 5.0.0 + playwright-core@1.45.3: {} playwright@1.45.3: @@ -25956,21 +26433,21 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.4.41 - postcss-load-config@4.0.2(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4)): + postcss-load-config@4.0.2(postcss@8.4.40)(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)): dependencies: lilconfig: 3.1.2 yaml: 2.4.5 optionalDependencies: postcss: 8.4.40 - ts-node: 10.9.2(@swc/core@1.3.101)(@types/node@22.3.0)(typescript@5.5.4) + ts-node: 10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4) - postcss-load-config@4.0.2(postcss@8.4.41)(ts-node@10.9.2(@types/node@22.3.0)(typescript@5.5.4)): + postcss-load-config@4.0.2(postcss@8.4.41)(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)): dependencies: lilconfig: 3.1.2 yaml: 2.4.5 optionalDependencies: postcss: 8.4.41 - ts-node: 10.9.2(@swc/core@1.3.101)(@types/node@22.3.0)(typescript@5.5.4) + ts-node: 10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4) postcss-load-config@6.0.1(jiti@1.21.6)(postcss@8.4.41)(tsx@4.16.5)(yaml@2.4.5): dependencies: @@ -26187,6 +26664,15 @@ snapshots: psl@1.9.0: {} + public-encrypt@4.0.3: + dependencies: + bn.js: 4.12.0 + browserify-rsa: 4.1.0 + create-hash: 1.2.0 + parse-asn1: 5.1.7 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + pump@3.0.0: dependencies: end-of-stream: 1.4.4 @@ -26214,6 +26700,12 @@ snapshots: dependencies: side-channel: 1.0.6 + qs@6.13.0: + dependencies: + side-channel: 1.0.6 + + querystring-es3@0.2.1: {} + querystring@0.2.1: {} querystringify@2.2.0: {} @@ -26228,6 +26720,11 @@ snapshots: dependencies: safe-buffer: 5.2.1 + randomfill@1.0.4: + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + range-parser@1.2.0: {} range-parser@1.2.1: {} @@ -26344,7 +26841,7 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-is: 18.1.0 - react-email@2.1.6(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.11)(eslint@8.57.0)(ts-node@10.9.2): + react-email@2.1.6(@opentelemetry/api@1.9.0)(@swc/helpers@0.5.11)(eslint@8.57.0)(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)): dependencies: '@babel/core': 7.24.5 '@babel/parser': 7.24.5 @@ -26384,7 +26881,7 @@ snapshots: source-map-js: 1.0.2 stacktrace-parser: 0.1.10 tailwind-merge: 2.2.0 - tailwindcss: 3.4.0(ts-node@10.9.2) + tailwindcss: 3.4.0(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) typescript: 5.1.6 transitivePeerDependencies: - '@opentelemetry/api' @@ -27035,6 +27532,11 @@ snapshots: glob: 11.0.0 package-json-from-dist: 1.0.0 + ripemd160@2.0.2: + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + rollup@3.29.4: optionalDependencies: fsevents: 2.3.3 @@ -27246,6 +27748,11 @@ snapshots: setprototypeof@1.2.0: {} + sha.js@2.4.11: + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + shallow-clone@3.0.1: dependencies: kind-of: 6.0.3 @@ -27552,8 +28059,20 @@ snapshots: - supports-color - utf-8-validate + stream-browserify@3.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-buffers@2.2.0: {} + stream-http@3.2.0: + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + xtend: 4.0.2 + stream-shift@1.0.3: {} streamsearch@1.1.0: {} @@ -27688,6 +28207,11 @@ snapshots: optionalDependencies: '@babel/core': 7.24.5 + styled-jsx@5.1.1(react@18.3.1): + dependencies: + client-only: 0.0.1 + react: 18.3.1 + styled-jsx@5.1.1(react@19.0.0-rc-935180c7e0-20240524): dependencies: client-only: 0.0.1 @@ -27764,7 +28288,7 @@ snapshots: tailwind-merge@2.5.2: {} - tailwindcss@3.4.0(ts-node@10.9.2): + tailwindcss@3.4.0(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -27783,7 +28307,7 @@ snapshots: postcss: 8.4.41 postcss-import: 15.1.0(postcss@8.4.41) postcss-js: 4.0.1(postcss@8.4.41) - postcss-load-config: 4.0.2(postcss@8.4.41)(ts-node@10.9.2(@types/node@22.3.0)(typescript@5.5.4)) + postcss-load-config: 4.0.2(postcss@8.4.41)(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) postcss-nested: 6.0.1(postcss@8.4.41) postcss-selector-parser: 6.1.0 resolve: 1.22.8 @@ -27791,7 +28315,7 @@ snapshots: transitivePeerDependencies: - ts-node - tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.3.0)(typescript@5.5.4)): + tailwindcss@3.4.10(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -27810,7 +28334,7 @@ snapshots: postcss: 8.4.41 postcss-import: 15.1.0(postcss@8.4.41) postcss-js: 4.0.1(postcss@8.4.41) - postcss-load-config: 4.0.2(postcss@8.4.41)(ts-node@10.9.2(@types/node@22.3.0)(typescript@5.5.4)) + postcss-load-config: 4.0.2(postcss@8.4.41)(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) postcss-nested: 6.0.1(postcss@8.4.41) postcss-selector-parser: 6.1.0 resolve: 1.22.8 @@ -27818,7 +28342,7 @@ snapshots: transitivePeerDependencies: - ts-node - tailwindcss@3.4.7(ts-node@10.9.2(typescript@5.5.4)): + tailwindcss@3.4.7(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -27837,7 +28361,7 @@ snapshots: postcss: 8.4.40 postcss-import: 15.1.0(postcss@8.4.40) postcss-js: 4.0.1(postcss@8.4.40) - postcss-load-config: 4.0.2(postcss@8.4.40)(ts-node@10.9.2(typescript@5.5.4)) + postcss-load-config: 4.0.2(postcss@8.4.40)(ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4)) postcss-nested: 6.0.1(postcss@8.4.40) postcss-selector-parser: 6.1.0 resolve: 1.22.8 @@ -27934,6 +28458,17 @@ snapshots: '@swc/core': 1.3.101(@swc/helpers@0.5.11) esbuild: 0.19.11 + terser-webpack-plugin@5.3.10(@swc/core@1.3.101(@swc/helpers@0.5.11))(webpack@5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11))): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.31.6 + webpack: 5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11)) + optionalDependencies: + '@swc/core': 1.3.101(@swc/helpers@0.5.11) + terser-webpack-plugin@5.3.10(webpack@5.93.0): dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -27973,6 +28508,10 @@ snapshots: through@2.3.8: {} + timers-browserify@2.0.12: + dependencies: + setimmediate: 1.0.5 + tiny-inflate@1.0.3: {} tiny-invariant@1.3.3: {} @@ -28055,7 +28594,7 @@ snapshots: '@ts-morph/common': 0.12.3 code-block-writer: 11.0.3 - ts-node@10.9.2(@swc/core@1.3.101)(@types/node@22.3.0)(typescript@5.5.4): + ts-node@10.9.2(@swc/core@1.3.101(@swc/helpers@0.5.11))(@types/node@22.3.0)(typescript@5.5.4): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -28100,7 +28639,7 @@ snapshots: tslib@2.6.3: {} - tsup@8.2.4(@microsoft/api-extractor@7.43.0(@types/node@22.3.0))(@swc/core@1.3.101)(jiti@1.21.6)(postcss@8.4.41)(tsx@4.16.5)(typescript@5.5.4)(yaml@2.4.5): + tsup@8.2.4(@microsoft/api-extractor@7.43.0(@types/node@22.3.0))(@swc/core@1.3.101(@swc/helpers@0.5.11))(jiti@1.21.6)(postcss@8.4.41)(tsx@4.16.5)(typescript@5.5.4)(yaml@2.4.5): dependencies: bundle-require: 5.0.0(esbuild@0.23.0) cac: 6.7.14 @@ -28141,6 +28680,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tty-browserify@0.0.1: {} + turbo-darwin-64@2.0.11: optional: true @@ -28437,6 +28978,11 @@ snapshots: url-template@2.0.8: {} + url@0.11.4: + dependencies: + punycode: 1.4.1 + qs: 6.13.0 + use-callback-ref@1.3.2(@types/react@18.2.47)(react@18.3.1): dependencies: react: 18.3.1 @@ -28556,6 +29102,14 @@ snapshots: - rollup - supports-color + vite-plugin-node-polyfills@0.22.0(rollup@4.19.1)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)): + dependencies: + '@rollup/plugin-inject': 5.0.5(rollup@4.19.1) + node-stdlib-browser: 1.2.0 + vite: 5.4.1(@types/node@22.3.0)(terser@5.31.6) + transitivePeerDependencies: + - rollup + vite-tsconfig-paths@5.0.1(typescript@5.5.4)(vite@5.4.1(@types/node@22.3.0)(terser@5.31.6)): dependencies: debug: 4.3.5 @@ -28583,38 +29137,6 @@ snapshots: typescript: 5.5.4 vitest: 2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6) - vitest@2.0.5: - dependencies: - '@ampproject/remapping': 2.3.0 - '@vitest/expect': 2.0.5 - '@vitest/pretty-format': 2.0.5 - '@vitest/runner': 2.0.5 - '@vitest/snapshot': 2.0.5 - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 - chai: 5.1.1 - debug: 4.3.5 - execa: 8.0.1 - magic-string: 0.30.10 - pathe: 1.1.2 - std-env: 3.7.0 - tinybench: 2.8.0 - tinypool: 1.0.0 - tinyrainbow: 1.2.0 - vite: 5.4.1(@types/node@22.3.0)(terser@5.31.6) - vite-node: 2.0.5(@types/node@22.3.0)(terser@5.31.6) - why-is-node-running: 2.3.0 - transitivePeerDependencies: - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - optional: true - vitest@2.0.5(@types/node@22.3.0)(jsdom@24.1.1)(terser@5.31.6): dependencies: '@ampproject/remapping': 2.3.0 @@ -28651,6 +29173,8 @@ snapshots: vlq@1.0.1: {} + vm-browserify@1.1.2: {} + vscode-oniguruma@1.7.0: {} vscode-textmate@8.0.0: {} @@ -28739,6 +29263,37 @@ snapshots: - esbuild - uglify-js + webpack@5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11)): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) + browserslist: 4.23.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.0 + es-module-lexer: 1.5.3 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.10(@swc/core@1.3.101(@swc/helpers@0.5.11))(webpack@5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11))) + watchpack: 2.4.1 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + webpack@5.93.0(@swc/core@1.3.101(@swc/helpers@0.5.11))(esbuild@0.19.11): dependencies: '@types/eslint-scope': 3.7.7 From 6314eeda0a13f63411a77f3b131c08084132893e Mon Sep 17 00:00:00 2001 From: Matti Nannt Date: Tue, 27 Aug 2024 10:01:21 +0200 Subject: [PATCH 13/19] chore: release js 2.2.0 (#3055) --- packages/js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/package.json b/packages/js/package.json index 38c22e5d2a..7483320b43 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -1,7 +1,7 @@ { "name": "@formbricks/js", "license": "MIT", - "version": "2.1.1", + "version": "2.2.0", "description": "Formbricks-js allows you to connect your app to Formbricks, display surveys and trigger events.", "homepage": "https://formbricks.com", "repository": { From 711dc83f5ceeb67f5420861e001bb8eff3526c13 Mon Sep 17 00:00:00 2001 From: Matti Nannt Date: Tue, 27 Aug 2024 10:35:01 +0200 Subject: [PATCH 14/19] fix: build errors on docs page (#3056) --- .../app/developer-docs/contributing/get-started/page.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/docs/app/developer-docs/contributing/get-started/page.mdx b/apps/docs/app/developer-docs/contributing/get-started/page.mdx index 682bb7170d..87e3f5deac 100644 --- a/apps/docs/app/developer-docs/contributing/get-started/page.mdx +++ b/apps/docs/app/developer-docs/contributing/get-started/page.mdx @@ -94,8 +94,8 @@ cp .env.example .env 4. Generate & set some secret values mandatory for the `ENCRYPTION_KEY`, `NEXTAUTH_SECRET` and `CRON_SECRET` in the .env file. You can use the following command to generate the random string of required length: - For Linux - - + + ```bash sed -i '/^ENCRYPTION_KEY=/c\ENCRYPTION_KEY='$(openssl rand -hex 32) .env @@ -107,8 +107,8 @@ sed -i '/^CRON_SECRET=/c\CRON_SECRET='$(openssl rand -hex 32) .env - For Mac - - + + ```bash sed -i '' '/^ENCRYPTION_KEY=/s|.*|ENCRYPTION_KEY='$(openssl rand -hex 32)'|' .env From 212e0753c8ced5cbd6dac5459c00c360c28f7b47 Mon Sep 17 00:00:00 2001 From: mdm317 Date: Tue, 27 Aug 2024 21:24:47 +0900 Subject: [PATCH 15/19] fix: closing survey on mobile falsely positions it (#3004) Co-authored-by: Piyush Gupta Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com> Co-authored-by: Johannes Co-authored-by: Dhruwang --- .../edit/components/ColorSurveyBg.tsx | 4 ++-- .../[environmentId]/product/tags/page.tsx | 2 +- apps/web/app/lib/questions.tsx | 2 +- packages/lib/constants.ts | 1 + packages/ui/MediaBackground/index.tsx | 4 ++-- .../ui/PreviewSurvey/components/Modal.tsx | 24 +++++++++---------- packages/ui/PreviewSurvey/index.tsx | 2 +- 7 files changed, 19 insertions(+), 20 deletions(-) diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ColorSurveyBg.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ColorSurveyBg.tsx index ed3d435408..e9f3d4e943 100644 --- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ColorSurveyBg.tsx +++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/ColorSurveyBg.tsx @@ -8,7 +8,7 @@ interface ColorSurveyBgProps { } export const ColorSurveyBg = ({ handleBgChange, colors, background }: ColorSurveyBgProps) => { - const [color, setColor] = useState(background || "#ffff"); + const [color, setColor] = useState(background || "#FFFFFF"); const handleBg = (x: string) => { setColor(x); @@ -23,7 +23,7 @@ export const ColorSurveyBg = ({ handleBgChange, colors, background }: ColorSurve {colors.map((x) => { return (
{ productChannel={currentProductChannel} /> - + { // Colors for Survey Bg export const SURVEY_BG_COLORS = [ + "#FFFFFF", "#FFF2D8", "#EAD7BB", "#BCA37F", diff --git a/packages/ui/MediaBackground/index.tsx b/packages/ui/MediaBackground/index.tsx index 4e3487e3ae..828362c655 100644 --- a/packages/ui/MediaBackground/index.tsx +++ b/packages/ui/MediaBackground/index.tsx @@ -168,7 +168,7 @@ export const MediaBackground: React.FC = ({
); default: - return
; + return
; } }; @@ -185,7 +185,7 @@ export const MediaBackground: React.FC = ({ className={`relative h-[90%] max-h-[40rem] w-[22rem] overflow-hidden rounded-[3rem] border-[6px] border-slate-400 ${getFilterStyle()}`}> {/* below element is use to create notch for the mobile device mockup */}
- {renderBackground()} + {survey.type === "link" && renderBackground()} {renderContent()}
); diff --git a/packages/ui/PreviewSurvey/components/Modal.tsx b/packages/ui/PreviewSurvey/components/Modal.tsx index 76a4401b34..a33269071d 100644 --- a/packages/ui/PreviewSurvey/components/Modal.tsx +++ b/packages/ui/PreviewSurvey/components/Modal.tsx @@ -29,7 +29,7 @@ export const Modal = ({ const [show, setShow] = useState(true); const modalRef = useRef(null); const [windowWidth, setWindowWidth] = useState(null); - const [overlayVisible, setOverlayVisible] = useState(true); + const [overlayVisible, setOverlayVisible] = useState(placement === "center"); useEffect(() => { if (typeof window !== "undefined") { @@ -42,6 +42,10 @@ export const Modal = ({ } }, []); + useEffect(() => { + setOverlayVisible(placement === "center"); + }, [placement]); + const calculateScaling = () => { if (windowWidth === null) return {}; @@ -78,29 +82,23 @@ export const Modal = ({ }; const scalingClasses = calculateScaling(); - const overlayStyle = overlayVisible && darkOverlay ? "bg-gray-700/80" : "bg-white/50"; useEffect(() => { - if (!clickOutsideClose) { - setOverlayVisible(true); - setShow(true); - } - + if (!clickOutsideClose || placement !== "center") return; const handleClickOutside = (e: MouseEvent) => { const previewBase = document.getElementById("preview-survey-base"); if ( - scalingClasses.transformOrigin === "" && clickOutsideClose && modalRef.current && previewBase && previewBase.contains(e.target as Node) && !modalRef.current.contains(e.target as Node) ) { + setShow(false); setTimeout(() => { - setOverlayVisible(false); - setShow(false); - }, 500); + setShow(true); + }, 1000); } }; @@ -108,7 +106,7 @@ export const Modal = ({ return () => { document.removeEventListener("mousedown", handleClickOutside); }; - }, [clickOutsideClose, scalingClasses.transformOrigin]); + }, [clickOutsideClose, placement]); useEffect(() => { setShow(isOpen); @@ -137,7 +135,7 @@ export const Modal = ({ aria-live="assertive" className={cn( "relative h-full w-full overflow-hidden rounded-b-md", - overlayStyle, + overlayVisible ? (darkOverlay ? "bg-gray-700/80" : "bg-white/50") : "", "transition-all duration-500 ease-in-out" )}>
{ setIsModalOpen(false); setTimeout(() => { - setQuestionId(survey.welcomeCard.enabled ? "start" : survey?.questions[0]?.id); setIsModalOpen(true); + resetQuestionProgress(); }, 1000); }; From ad028947e0ec14bce745f08a2394e1a9e60b3c35 Mon Sep 17 00:00:00 2001 From: Piyush Gupta <56182734+gupta-piyush19@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:01:39 +0530 Subject: [PATCH 16/19] fix: new survey button visible to viewers (#3065) --- .../surveys/templates/page.tsx | 12 +++++++++ .../components/EnvironmentLayout.tsx | 1 + .../components/TopControlBar.tsx | 10 +++++++- .../components/TopControlButtons.tsx | 25 +++++++++++-------- .../[environmentId]/surveys/page.tsx | 9 +++++++ 5 files changed, 46 insertions(+), 11 deletions(-) diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/page.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/page.tsx index 1de26ec9ec..9349a84cbe 100644 --- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/page.tsx +++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/page.tsx @@ -1,6 +1,9 @@ import { getServerSession } from "next-auth"; +import { redirect } from "next/navigation"; import { authOptions } from "@formbricks/lib/authOptions"; import { getEnvironment } from "@formbricks/lib/environment/service"; +import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service"; +import { getAccessFlags } from "@formbricks/lib/membership/utils"; import { getProductByEnvironmentId } from "@formbricks/lib/product/service"; import { getUser } from "@formbricks/lib/user/service"; import { TProductConfigChannel, TProductConfigIndustry } from "@formbricks/types/product"; @@ -43,6 +46,15 @@ const Page = async ({ params, searchParams }: SurveyTemplateProps) => { if (!environment) { throw new Error("Environment not found"); } + const currentUserMembership = await getMembershipByUserIdOrganizationId( + session?.user.id, + product.organizationId + ); + const { isViewer } = getAccessFlags(currentUserMembership?.role); + + if (isViewer) { + return redirect(`/environments/${environment.id}/surveys`); + } const prefilledFilters = [product.config.channel, product.config.industry, searchParams.role ?? null]; diff --git a/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx b/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx index ec311d0236..d3238597cf 100644 --- a/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/components/EnvironmentLayout.tsx @@ -102,6 +102,7 @@ export const EnvironmentLayout = async ({ environmentId, session, children }: En environment={environment} environments={environments} currentProductChannel={currentProductChannel} + membershipRole={currentUserMembership?.role} />
{children}
diff --git a/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx b/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx index 02055cb50a..bcd7c99fd2 100644 --- a/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/components/TopControlBar.tsx @@ -2,15 +2,22 @@ import { TopControlButtons } from "@/app/(app)/environments/[environmentId]/comp import { WidgetStatusIndicator } from "@/app/(app)/environments/[environmentId]/components/WidgetStatusIndicator"; import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants"; import { TEnvironment } from "@formbricks/types/environment"; +import { TMembershipRole } from "@formbricks/types/memberships"; import { TProductConfigChannel } from "@formbricks/types/product"; interface SideBarProps { environment: TEnvironment; environments: TEnvironment[]; currentProductChannel: TProductConfigChannel; + membershipRole?: TMembershipRole; } -export const TopControlBar = ({ environment, environments, currentProductChannel }: SideBarProps) => { +export const TopControlBar = ({ + environment, + environments, + currentProductChannel, + membershipRole, +}: SideBarProps) => { return (
@@ -28,6 +35,7 @@ export const TopControlBar = ({ environment, environments, currentProductChannel environment={environment} environments={environments} isFormbricksCloud={IS_FORMBRICKS_CLOUD} + membershipRole={membershipRole} />
diff --git a/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx b/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx index b52e7d722f..c8d10049a4 100644 --- a/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/components/TopControlButtons.tsx @@ -5,18 +5,21 @@ import { CircleUserIcon, MessageCircleQuestionIcon, PlusIcon } from "lucide-reac import { useRouter } from "next/navigation"; import formbricks from "@formbricks/js/app"; import { TEnvironment } from "@formbricks/types/environment"; +import { TMembershipRole } from "@formbricks/types/memberships"; import { Button } from "@formbricks/ui/Button"; interface TopControlButtonsProps { environment: TEnvironment; environments: TEnvironment[]; isFormbricksCloud: boolean; + membershipRole?: TMembershipRole; } export const TopControlButtons = ({ environment, environments, isFormbricksCloud, + membershipRole, }: TopControlButtonsProps) => { const router = useRouter(); return ( @@ -44,16 +47,18 @@ export const TopControlButtons = ({ }}> - + {membershipRole && membershipRole !== "viewer" ? ( + + ) : null}
); }; diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/page.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/page.tsx index d49d2778cc..3e7ca70495 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/page.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/page.tsx @@ -90,6 +90,15 @@ const Page = async ({ params, searchParams }: SurveyTemplateProps) => { currentProductChannel={currentProductChannel} /> + ) : isViewer ? ( + <> +

No surveys created yet.

+ +

+ As a Viewer you are not allowed to create surveys. Please ask an Editor to create a survey or an + Admin to upgrade your role. +

+ ) : ( <>

From 55c305c56966c05e66dc57accd7916a33dc87d0b Mon Sep 17 00:00:00 2001 From: Dhruwang Jariwala <67850763+Dhruwang@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:56:25 +0530 Subject: [PATCH 17/19] fix: rounded-corner-gap=issue (#3064) Co-authored-by: Johannes --- .../QuestionsStylingSettingsTabs.tsx | 10 +++++----- .../edit/components/QuestionsView.tsx | 2 +- .../edit/components/SurveyEditor.tsx | 2 +- .../edit/components/SurveyMenuBar.tsx | 18 +++++++++++++----- .../components/SurveyStatusDropdown.tsx | 2 +- .../wrappers/StackedCardsContainer.tsx | 2 +- 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsStylingSettingsTabs.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsStylingSettingsTabs.tsx index db4add1b33..c2e710a9aa 100644 --- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsStylingSettingsTabs.tsx +++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/QuestionsStylingSettingsTabs.tsx @@ -18,7 +18,7 @@ const tabs: Tab[] = [ { id: "styling", label: "Styling", - icon: , + icon: , }, { id: "settings", @@ -46,7 +46,7 @@ export const QuestionsAudienceTabs = ({ }, [isStylingTabVisible]); return ( -
+
{responseCount > 0 && ( -
+
@@ -318,7 +320,9 @@ export const SurveyMenuBar = ({ -

{cautionText}

+

+ {cautionText} +

)}
@@ -332,6 +336,7 @@ export const SurveyMenuBar = ({ )} {!isViewer && (