UNPKG

@formbricks/js

Version:

Formbricks-js allows you to connect your index to Formbricks, display surveys and trigger events.

1 lines 8.98 kB
{"version":3,"file":"index.cjs","names":[],"sources":["../src/lib/load-formbricks.ts","../src/index.ts"],"sourcesContent":["import type { TFormbricks, TSetupConfig } from \"../types/formbricks\";\n\ntype Result<T, E = Error> = { ok: true; data: T } | { ok: false; error: E };\n\nlet coreInstance: TFormbricks | null = null;\nlet isInitializing = false;\nconst queue: { method: string; args: unknown[] }[] = [];\n\nconst loadFormbricksSDK = async (appUrl: string): Promise<Result<void>> => {\n if ((globalThis as unknown as Record<string, unknown>).formbricks) {\n return { ok: true, data: undefined };\n }\n\n const scriptSrc = `${appUrl}/js/formbricks.umd.cjs`;\n\n // Remove any previously appended script to prevent duplicates on retry\n const existingScript = document.querySelector(`script[src=\"${scriptSrc}\"]`);\n if (existingScript) {\n existingScript.remove();\n }\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n script.src = scriptSrc;\n script.async = true;\n\n const loadPromise = new Promise<Result<void>>((resolve) => {\n const timeoutId = setTimeout(() => {\n resolve({\n ok: false,\n error: new Error(\"Formbricks SDK loading timed out\"),\n });\n }, 10000);\n\n script.onload = () => {\n clearTimeout(timeoutId);\n\n // UMD should set globalThis.formbricks synchronously on execution.\n // Poll briefly as a fallback in case UMD environment detection was\n // fooled (e.g. a leaked `exports` global) and the assignment was\n // routed to module.exports instead of globalThis.\n if ((globalThis as unknown as Record<string, unknown>).formbricks) {\n resolve({ ok: true, data: undefined });\n return;\n }\n\n let attempts = 0;\n const poll = setInterval(() => {\n if ((globalThis as unknown as Record<string, unknown>).formbricks) {\n clearInterval(poll);\n resolve({ ok: true, data: undefined });\n } else if (++attempts >= 50) {\n clearInterval(poll);\n resolve({\n ok: false,\n error: new Error(\n \"Formbricks SDK loaded but not available on globalThis\",\n ),\n });\n }\n }, 10);\n };\n\n script.onerror = () => {\n clearTimeout(timeoutId);\n resolve({\n ok: false,\n error: new Error(\"Failed to load Formbricks SDK\"),\n });\n };\n });\n\n // Register handlers above BEFORE appending to DOM so that a cached\n // script whose onload fires on the next microtask is always caught.\n document.head.appendChild(script);\n return loadPromise;\n};\n\nconst validateSetupArgs = (config: TSetupConfig): TSetupConfig | null => {\n const { appUrl, environmentId, workspaceId } = config;\n\n if (!appUrl) {\n console.error(\"🧱 Formbricks - Error: appUrl is required\");\n return null;\n }\n\n if (!workspaceId && !environmentId) {\n console.error(\n \"🧱 Formbricks - Error: workspaceId or environmentId is required\",\n );\n return null;\n }\n\n if (environmentId && !workspaceId) {\n console.warn(\n \"🧱 Formbricks - Warning: environmentId is deprecated and will be removed in a future version. Please use workspaceId instead.\",\n );\n }\n\n // Removing trailing slash\n const appUrlWithoutTrailingSlash = appUrl.endsWith(\"/\")\n ? appUrl.slice(0, -1)\n : appUrl;\n\n if (workspaceId) {\n return {\n appUrl: appUrlWithoutTrailingSlash,\n workspaceId,\n ...(environmentId ? { environmentId } : {}),\n };\n }\n\n return {\n appUrl: appUrlWithoutTrailingSlash,\n environmentId: environmentId as string,\n };\n};\n\nconst processQueue = (): void => {\n while (queue.length > 0) {\n const entry = queue.shift();\n // Should never happen as we check for length above\n if (!entry) break;\n if (!coreInstance) break;\n\n if (\n typeof coreInstance[entry.method as keyof typeof coreInstance] !==\n \"function\"\n ) {\n console.error(\n `🧱 Formbricks - Error: Method ${entry.method} does not exist on formbricks`,\n );\n continue;\n }\n\n // @ts-expect-error -- Required for dynamic function calls\n (coreInstance[entry.method as keyof typeof coreInstance] as unknown)(\n ...entry.args,\n );\n }\n};\n\nexport const setup = async (config: TSetupConfig): Promise<void> => {\n if (isInitializing) {\n console.warn(\n \"🧱 Formbricks - Warning: Formbricks is already initializing.\",\n );\n return;\n }\n\n const validatedArgs = validateSetupArgs(config);\n if (!validatedArgs) return;\n\n isInitializing = true;\n try {\n const loadResult = await loadFormbricksSDK(validatedArgs.appUrl);\n const instance = (globalThis as unknown as Record<string, unknown>)\n .formbricks as TFormbricks | undefined;\n\n if (!loadResult.ok || !instance) {\n console.error(\"🧱 Formbricks - Error: Failed to load Formbricks SDK\");\n return;\n }\n\n await instance.setup({ ...validatedArgs });\n coreInstance = instance;\n processQueue();\n } catch (err) {\n coreInstance = null;\n console.error(\"🧱 Formbricks - Error: setup failed\", err);\n } finally {\n isInitializing = false;\n }\n};\n\nexport const callMethod = async (\n method: string,\n ...args: unknown[]\n): Promise<void> => {\n if (coreInstance) {\n // @ts-expect-error -- Required for dynamic function calls\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n await coreInstance[method](...args);\n } else {\n console.warn(\n \"🧱 Formbricks - Warning: Formbricks not initialized. This method will be queued and executed after initialization.\",\n );\n queue.push({ method, args });\n }\n};\n","import { callMethod, setup } from \"./lib/load-formbricks\";\nimport type { TFormbricks } from \"./types/formbricks\";\n\ndeclare global {\n interface Window {\n formbricks: TFormbricks | undefined;\n }\n}\n\nconst formbricks: TFormbricks = {\n setup: (setupConfig) => setup(setupConfig),\n setEmail: (email) => callMethod(\"setEmail\", email),\n setAttribute: (key, value) => callMethod(\"setAttribute\", key, value),\n setAttributes: (attributes) => callMethod(\"setAttributes\", attributes),\n setLanguage: (language) => callMethod(\"setLanguage\", language),\n setUserId: (userId) => callMethod(\"setUserId\", userId),\n setNonce: (nonce) => callMethod(\"setNonce\", nonce),\n track: (code, properties) => callMethod(\"track\", code, properties),\n logout: () => callMethod(\"logout\"),\n registerRouteChange: () => callMethod(\"registerRouteChange\"),\n};\n\nexport default formbricks;\n"],"mappings":"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"}