Skip to content

Commit 6cbfec1

Browse files
authored
Merge pull request #47 from formbricks/feat/workspace-support
feat: adds workspace support
2 parents ca8bd8c + 9b01a3c commit 6cbfec1

5 files changed

Lines changed: 123 additions & 25 deletions

File tree

packages/js/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ import formbricks from "@formbricks/js";
2525

2626
if (typeof window !== "undefined") {
2727
formbricks.setup({
28-
environmentId: "your-environment-id",
28+
workspaceId: "your-workspace-id",
2929
appUrl: "https://app.formbricks.com",
3030
});
3131
}
3232
```
3333

34-
Replace your-environment-id with your actual environment ID. You can find your environment ID in the **Setup Checklist** in the Formbricks settings. If you want to use the user identification feature, please check out [our docs for details](https://formbricks.com/docs/app-surveys/user-identification).
34+
Replace your-workspace-id with your actual workspace ID. You can find your workspace ID in the **Setup Checklist** in the Formbricks settings. If you want to use the user identification feature, please check out [our docs for details](https://formbricks.com/docs/app-surveys/user-identification).
35+
36+
> `environmentId` is still accepted for backward compatibility but is deprecated and will be removed in a future version. Prefer `workspaceId`.
3537
3638
For more detailed guides for different frameworks, check out our [Framework Guides](https://formbricks.com/docs/getting-started/framework-guides).

packages/js/src/index.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,18 @@ describe("formbricks", () => {
2424
expect(typeof formbricks).toBe("object");
2525
});
2626

27-
test("should delegate setup to setup()", async () => {
27+
test("should delegate setup to setup() (workspaceId)", async () => {
28+
const setupArgs = {
29+
workspaceId: "ws123",
30+
appUrl: "https://app.formbricks.com",
31+
};
32+
33+
await formbricks.setup(setupArgs);
34+
35+
expect(mockSetup).toHaveBeenCalledWith(setupArgs);
36+
});
37+
38+
test("should delegate setup to setup() (environmentId backward compat)", async () => {
2839
const setupArgs = {
2940
environmentId: "env123",
3041
appUrl: "https://app.formbricks.com",
@@ -212,6 +223,7 @@ describe("type safety", () => {
212223

213224
test("should maintain type safety for known methods", () => {
214225
const testTypeSafety = () => {
226+
void formbricks.setup({ workspaceId: "ws", appUrl: "url" });
215227
void formbricks.setup({ environmentId: "env", appUrl: "url" });
216228
void formbricks.track("event");
217229
void formbricks.setEmail("email");

packages/js/src/lib/load-formbricks.test.ts

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
2+
import type { TSetupConfig } from "../types/formbricks";
23

34
// We need to import the module after each reset
4-
let setup: (config: { appUrl: string; environmentId: string }) => Promise<void>;
5+
let setup: (config: TSetupConfig) => Promise<void>;
56
let callMethod: (method: string, ...args: unknown[]) => Promise<void>;
67

78
// Mock the globalThis formbricks object
@@ -191,7 +192,7 @@ describe("load-formbricks", () => {
191192
);
192193
});
193194

194-
test("should log error when environmentId is missing", async () => {
195+
test("should log error when both workspaceId and environmentId are missing", async () => {
195196
const consoleSpy = createConsoleErrorSpy();
196197

197198
await setup({
@@ -200,10 +201,83 @@ describe("load-formbricks", () => {
200201
});
201202

202203
expect(consoleSpy).toHaveBeenCalledWith(
203-
"🧱 Formbricks - Error: environmentId is required",
204+
"🧱 Formbricks - Error: workspaceId or environmentId is required",
204205
);
205206
});
206207

208+
test("should handle setup call with workspaceId", async () => {
209+
const setupArgs = {
210+
appUrl: "https://app.formbricks.com",
211+
workspaceId: "ws123",
212+
};
213+
214+
const mockAppendChild = vi
215+
.spyOn(document.head, "appendChild")
216+
.mockImplementation(createSuccessfulScriptMock());
217+
218+
await setup(setupArgs);
219+
220+
expect(mockAppendChild).toHaveBeenCalledWith(
221+
expect.objectContaining({
222+
src: `${setupArgs.appUrl}/js/formbricks.umd.cjs`,
223+
type: "text/javascript",
224+
async: true,
225+
}),
226+
);
227+
expect(mockFormbricks.setup).toHaveBeenCalledWith(setupArgs);
228+
});
229+
230+
test("should warn about deprecation when only environmentId is provided", async () => {
231+
const consoleWarnSpy = createConsoleWarnSpy();
232+
233+
vi.spyOn(document.head, "appendChild").mockImplementation(
234+
createSuccessfulScriptMock(),
235+
);
236+
237+
await setup({
238+
appUrl: "https://app.formbricks.com",
239+
environmentId: "env123",
240+
});
241+
242+
expect(consoleWarnSpy).toHaveBeenCalledWith(
243+
"🧱 Formbricks - Warning: environmentId is deprecated and will be removed in a future version. Please use workspaceId instead.",
244+
);
245+
});
246+
247+
test("should not warn about deprecation when workspaceId is provided", async () => {
248+
const consoleWarnSpy = createConsoleWarnSpy();
249+
250+
vi.spyOn(document.head, "appendChild").mockImplementation(
251+
createSuccessfulScriptMock(),
252+
);
253+
254+
await setup({
255+
appUrl: "https://app.formbricks.com",
256+
workspaceId: "ws123",
257+
environmentId: "env123",
258+
});
259+
260+
expect(consoleWarnSpy).not.toHaveBeenCalledWith(
261+
expect.stringContaining("environmentId is deprecated"),
262+
);
263+
});
264+
265+
test("should pass both workspaceId and environmentId through when both provided", async () => {
266+
const setupArgs = {
267+
appUrl: "https://app.formbricks.com",
268+
workspaceId: "ws123",
269+
environmentId: "env123",
270+
};
271+
272+
vi.spyOn(document.head, "appendChild").mockImplementation(
273+
createSuccessfulScriptMock(),
274+
);
275+
276+
await setup(setupArgs);
277+
278+
expect(mockFormbricks.setup).toHaveBeenCalledWith(setupArgs);
279+
});
280+
207281
test("should not load script again if formbricks is already available", async () => {
208282
const setupArgs = {
209283
appUrl: "https://app.formbricks.com",

packages/js/src/lib/load-formbricks.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { TFormbricks } from "../types/formbricks";
1+
import type { TFormbricks, TSetupConfig } from "../types/formbricks";
22

33
type Result<T, E = Error> = { ok: true; data: T } | { ok: false; error: E };
44

@@ -76,30 +76,37 @@ const loadFormbricksSDK = async (appUrl: string): Promise<Result<void>> => {
7676
return loadPromise;
7777
};
7878

79-
const validateSetupArgs = (
80-
config: unknown,
81-
): { appUrl: string; environmentId: string } | null => {
82-
const { appUrl, environmentId } = config as {
83-
appUrl: string;
84-
environmentId: string;
85-
};
79+
const validateSetupArgs = (config: TSetupConfig): TSetupConfig | null => {
80+
const { appUrl, environmentId, workspaceId } = config;
8681

8782
if (!appUrl) {
8883
console.error("🧱 Formbricks - Error: appUrl is required");
8984
return null;
9085
}
9186

92-
if (!environmentId) {
93-
console.error("🧱 Formbricks - Error: environmentId is required");
87+
if (!workspaceId && !environmentId) {
88+
console.error(
89+
"🧱 Formbricks - Error: workspaceId or environmentId is required",
90+
);
9491
return null;
9592
}
9693

94+
if (environmentId && !workspaceId) {
95+
console.warn(
96+
"🧱 Formbricks - Warning: environmentId is deprecated and will be removed in a future version. Please use workspaceId instead.",
97+
);
98+
}
99+
97100
// Removing trailing slash
98101
const appUrlWithoutTrailingSlash = appUrl.endsWith("/")
99102
? appUrl.slice(0, -1)
100103
: appUrl;
101104

102-
return { appUrl: appUrlWithoutTrailingSlash, environmentId };
105+
return {
106+
appUrl: appUrlWithoutTrailingSlash,
107+
...(workspaceId ? { workspaceId } : {}),
108+
...(environmentId ? { environmentId } : {}),
109+
};
103110
};
104111

105112
const processQueue = (): void => {
@@ -126,10 +133,7 @@ const processQueue = (): void => {
126133
}
127134
};
128135

129-
export const setup = async (config: {
130-
appUrl: string;
131-
environmentId: string;
132-
}): Promise<void> => {
136+
export const setup = async (config: TSetupConfig): Promise<void> => {
133137
if (isInitializing) {
134138
console.warn(
135139
"🧱 Formbricks - Warning: Formbricks is already initializing.",

packages/js/src/types/formbricks.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ export interface TFormbricks {
33
* @description Initializes the Formbricks SDK.
44
* @param setupConfig - The configuration for the Formbricks SDK.
55
*/
6-
setup: (setupConfig: {
7-
environmentId: string;
8-
appUrl: string;
9-
}) => Promise<void>;
6+
setup: (setupConfig: TSetupConfig) => Promise<void>;
107

118
/**
129
* @description Sets the email of the user.
@@ -67,3 +64,12 @@ export interface TFormbricks {
6764
*/
6865
registerRouteChange: () => Promise<void>;
6966
}
67+
68+
export type TSetupConfig = {
69+
workspaceId?: string;
70+
/**
71+
* @deprecated use workspaceId instead, environmentId will be removed in a future version
72+
*/
73+
environmentId?: string;
74+
appUrl: string;
75+
};

0 commit comments

Comments
 (0)