|
3 | 3 | * Required for logging errors |
4 | 4 | */ |
5 | 5 |
|
| 6 | +import type { TFormbricks } from "../types/formbricks"; |
| 7 | + |
| 8 | +declare global { |
| 9 | + var formbricks: TFormbricks & { |
| 10 | + [key: string]: (...args: any[]) => any; |
| 11 | + }; |
| 12 | +} |
| 13 | + |
6 | 14 | type Result<T, E = Error> = { ok: true; data: T } | { ok: false; error: E }; |
7 | 15 |
|
8 | 16 | let isInitializing = false; |
9 | 17 | let isInitialized = false; |
10 | 18 | // Load the SDK, return the result |
11 | 19 | const loadFormbricksSDK = async (apiHostParam: string): Promise<Result<void>> => { |
12 | | - if (!window.formbricks) { |
| 20 | + if (!globalThis.formbricks) { |
13 | 21 | const scriptTag = document.createElement("script"); |
14 | 22 | scriptTag.type = "text/javascript"; |
15 | 23 | scriptTag.src = `${apiHostParam}/js/formbricks.umd.cjs`; |
@@ -45,64 +53,78 @@ const loadFormbricksSDK = async (apiHostParam: string): Promise<Result<void>> => |
45 | 53 |
|
46 | 54 | const functionsToProcess: { prop: string; args: unknown[] }[] = []; |
47 | 55 |
|
48 | | -export const loadFormbricksToProxy = async (prop: string, ...args: unknown[]): Promise<void> => { |
49 | | - // all of this should happen when not initialized: |
50 | | - if (!isInitialized) { |
51 | | - // We need to still support init for backwards compatibility |
52 | | - // but we should log a warning that the init method is deprecated |
53 | | - if (prop === "setup") { |
54 | | - if (isInitializing) { |
55 | | - console.warn("🧱 Formbricks - Warning: Formbricks is already initializing."); |
56 | | - return; |
57 | | - } |
58 | | - // reset the initialization state |
59 | | - isInitializing = true; |
60 | | - const argsTyped = args[0] as { appUrl: string; environmentId: string }; |
61 | | - const { appUrl, environmentId } = argsTyped; |
| 56 | +const validateSetupArgs = (args: unknown[]): { appUrl: string; environmentId: string } | null => { |
| 57 | + const argsTyped = args[0] as { appUrl: string; environmentId: string }; |
| 58 | + const { appUrl, environmentId } = argsTyped; |
62 | 59 |
|
63 | | - if (!appUrl) { |
64 | | - console.error("🧱 Formbricks - Error: appUrl is required"); |
65 | | - return; |
66 | | - } |
| 60 | + if (!appUrl) { |
| 61 | + console.error("🧱 Formbricks - Error: appUrl is required"); |
| 62 | + return null; |
| 63 | + } |
67 | 64 |
|
68 | | - if (!environmentId) { |
69 | | - console.error("🧱 Formbricks - Error: environmentId is required"); |
70 | | - return; |
71 | | - } |
| 65 | + if (!environmentId) { |
| 66 | + console.error("🧱 Formbricks - Error: environmentId is required"); |
| 67 | + return null; |
| 68 | + } |
72 | 69 |
|
73 | | - const loadSDKResult = await loadFormbricksSDK(appUrl); |
74 | | - if (loadSDKResult.ok) { |
75 | | - if (window.formbricks) { |
76 | | - const formbricksInstance = window.formbricks; |
77 | | - // @ts-expect-error -- Required for dynamic function calls |
78 | | - void formbricksInstance.setup(...args); |
79 | | - isInitializing = false; |
80 | | - isInitialized = true; |
81 | | - // process the queued functions |
82 | | - for (const { prop: functionProp, args: functionArgs } of functionsToProcess) { |
83 | | - if (typeof formbricksInstance[functionProp as keyof typeof formbricksInstance] !== "function") { |
84 | | - console.error(`🧱 Formbricks - Error: Method ${functionProp} does not exist on formbricks`); |
85 | | - continue; |
86 | | - } |
87 | | - // @ts-expect-error -- Required for dynamic function calls |
88 | | - (formbricksInstance[functionProp] as unknown)(...functionArgs); |
89 | | - } |
90 | | - } |
91 | | - } |
92 | | - } else { |
93 | | - console.warn( |
94 | | - "🧱 Formbricks - Warning: Formbricks not initialized. This method will be queued and executed after initialization." |
95 | | - ); |
| 70 | + return { appUrl, environmentId }; |
| 71 | +}; |
| 72 | + |
| 73 | +const processQueuedFunctions = (formbricksInstance: any): void => { |
| 74 | + for (const { prop: functionProp, args: functionArgs } of functionsToProcess) { |
| 75 | + if (typeof formbricksInstance[functionProp as keyof typeof formbricksInstance] !== "function") { |
| 76 | + console.error(`🧱 Formbricks - Error: Method ${functionProp} does not exist on formbricks`); |
| 77 | + continue; |
| 78 | + } |
| 79 | + // @ts-expect-error -- Required for dynamic function calls |
| 80 | + (formbricksInstance[functionProp] as unknown)(...functionArgs); |
| 81 | + } |
| 82 | +}; |
96 | 83 |
|
97 | | - functionsToProcess.push({ prop, args }); |
| 84 | +const handleSetupCall = async (args: unknown[]): Promise<void> => { |
| 85 | + if (isInitializing) { |
| 86 | + console.warn("🧱 Formbricks - Warning: Formbricks is already initializing."); |
| 87 | + return; |
| 88 | + } |
| 89 | + const validatedArgs = validateSetupArgs(args); |
| 90 | + if (!validatedArgs) return; |
| 91 | + isInitializing = true; |
| 92 | + try { |
| 93 | + const loadSDKResult = await loadFormbricksSDK(validatedArgs.appUrl); |
| 94 | + if (!loadSDKResult.ok || !globalThis.formbricks) { |
| 95 | + console.error("🧱 Formbricks - Error: Failed to load Formbricks SDK"); |
| 96 | + return; |
98 | 97 | } |
99 | | - } else if (window.formbricks) { |
100 | | - // Access the default export for initialized state too |
101 | | - const formbricksInstance = window.formbricks; |
102 | | - type Formbricks = typeof formbricksInstance; |
103 | | - type FunctionProp = keyof Formbricks; |
104 | | - const functionPropTyped = prop as FunctionProp; |
| 98 | + const formbricksInstance = globalThis.formbricks; |
105 | 99 | // @ts-expect-error -- Required for dynamic function calls |
106 | | - await formbricksInstance[functionPropTyped](...args); |
| 100 | + await formbricksInstance.setup(...args); |
| 101 | + isInitialized = true; |
| 102 | + processQueuedFunctions(formbricksInstance); |
| 103 | + } catch (err) { |
| 104 | + console.error("🧱 Formbricks - Error: setup failed", err); |
| 105 | + } finally { |
| 106 | + isInitializing = false; |
| 107 | + } |
| 108 | +}; |
| 109 | +const executeFormbricksMethod = async (prop: string, args: unknown[]): Promise<void> => { |
| 110 | + if (!globalThis.formbricks) return; |
| 111 | + |
| 112 | + const formbricksInstance = globalThis.formbricks; |
| 113 | + type Formbricks = typeof formbricksInstance; |
| 114 | + type FunctionProp = keyof Formbricks; |
| 115 | + const functionPropTyped = prop as FunctionProp; |
| 116 | + await formbricksInstance[functionPropTyped](...args); |
| 117 | +}; |
| 118 | + |
| 119 | +export const loadFormbricksToProxy = async (prop: string, ...args: unknown[]): Promise<void> => { |
| 120 | + if (isInitialized) { |
| 121 | + await executeFormbricksMethod(prop, args) |
| 122 | + } else if (prop === "setup") { |
| 123 | + await handleSetupCall(args); |
| 124 | + } else { |
| 125 | + console.warn( |
| 126 | + "🧱 Formbricks - Warning: Formbricks not initialized. This method will be queued and executed after initialization." |
| 127 | + ); |
| 128 | + functionsToProcess.push({ prop, args }); |
107 | 129 | } |
108 | 130 | }; |
0 commit comments