mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-07 00:40:10 -06:00
fix: global error handling js package (#2553)
This commit is contained in:
@@ -1,37 +1,57 @@
|
||||
import { TFormbricksApp } from "@formbricks/js-core/app";
|
||||
import { TFormbricksWebsite } from "@formbricks/js-core/website";
|
||||
|
||||
import { Result, wrapThrowsAsync } from "../../types/errorHandlers";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
formbricks: TFormbricksApp | TFormbricksWebsite;
|
||||
}
|
||||
}
|
||||
|
||||
let sdkLoadingPromise: Promise<void> | null = null;
|
||||
let isErrorLoadingSdk = false;
|
||||
|
||||
async function loadSDK(apiHost: string) {
|
||||
// load the sdk, return the result
|
||||
async function loadFormbricksAppSDK(apiHost: string): Promise<Result<void>> {
|
||||
if (!window.formbricks) {
|
||||
const res = await fetch(`${apiHost}/api/packages/app`);
|
||||
if (!res.ok) throw new Error("Failed to load Formbricks App SDK");
|
||||
|
||||
// failed to fetch the app package
|
||||
if (!res.ok) {
|
||||
return { ok: false, error: new Error("Failed to load Formbricks App SDK") };
|
||||
}
|
||||
|
||||
const sdkScript = await res.text();
|
||||
const scriptTag = document.createElement("script");
|
||||
scriptTag.innerHTML = sdkScript;
|
||||
document.head.appendChild(scriptTag);
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const checkInterval = setInterval(() => {
|
||||
if (window.formbricks) {
|
||||
const getFormbricks = async () =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const checkInterval = setInterval(() => {
|
||||
if (window.formbricks) {
|
||||
clearInterval(checkInterval);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
setTimeout(() => {
|
||||
clearInterval(checkInterval);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
setTimeout(() => {
|
||||
clearInterval(checkInterval);
|
||||
reject(new Error("Formbricks SDK loading timed out"));
|
||||
}, 10000);
|
||||
});
|
||||
reject(new Error("Formbricks App SDK loading timed out"));
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
try {
|
||||
await getFormbricks();
|
||||
return { ok: true, data: undefined };
|
||||
} catch (error: any) {
|
||||
// formbricks loading failed, return the error
|
||||
return {
|
||||
ok: false,
|
||||
error: new Error(error.message ?? "Failed to load Formbricks App SDK"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: true, data: undefined };
|
||||
}
|
||||
|
||||
type FormbricksAppMethods = {
|
||||
@@ -41,31 +61,34 @@ type FormbricksAppMethods = {
|
||||
const formbricksProxyHandler: ProxyHandler<TFormbricksApp> = {
|
||||
get(_target, prop, _receiver) {
|
||||
return async (...args: any[]) => {
|
||||
if (!window.formbricks && !sdkLoadingPromise && !isErrorLoadingSdk) {
|
||||
const { apiHost } = args[0];
|
||||
sdkLoadingPromise = loadSDK(apiHost).catch((error) => {
|
||||
console.error(`🧱 Formbricks - Error loading SDK: ${error}`);
|
||||
sdkLoadingPromise = null;
|
||||
isErrorLoadingSdk = true;
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
if (isErrorLoadingSdk) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sdkLoadingPromise) {
|
||||
await sdkLoadingPromise;
|
||||
}
|
||||
|
||||
if (!window.formbricks) {
|
||||
throw new Error("Formbricks App SDK is not available");
|
||||
if (prop !== "init") {
|
||||
console.error(
|
||||
"🧱 Formbricks - Global error: You need to call formbricks.init before calling any other method"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// still need to check if the apiHost is passed
|
||||
if (!args[0]) {
|
||||
console.error("🧱 Formbricks - Global error: You need to pass the apiHost as the first argument");
|
||||
return;
|
||||
}
|
||||
|
||||
const { apiHost } = args[0];
|
||||
const loadSDKResult = await wrapThrowsAsync(loadFormbricksAppSDK)(apiHost);
|
||||
|
||||
if (!loadSDKResult.ok) {
|
||||
console.error(`🧱 Formbricks - Global error: ${loadSDKResult.error.message}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
if (typeof window.formbricks[prop as FormbricksAppMethods] !== "function") {
|
||||
console.error(`🧱 Formbricks App SDK does not support method ${String(prop)}`);
|
||||
if (window.formbricks && typeof window.formbricks[prop as FormbricksAppMethods] !== "function") {
|
||||
console.error(
|
||||
`🧱 Formbricks - Global error: Formbricks App SDK does not support method ${String(prop)}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,8 +96,8 @@ const formbricksProxyHandler: ProxyHandler<TFormbricksApp> = {
|
||||
// @ts-expect-error
|
||||
return (window.formbricks[prop as FormbricksAppMethods] as Function)(...args);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
console.error(`Something went wrong: ${error}`);
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1,30 +1,57 @@
|
||||
import { TFormbricksApp } from "@formbricks/js-core/app";
|
||||
import { TFormbricksWebsite } from "@formbricks/js-core/website";
|
||||
|
||||
let sdkLoadingPromise: Promise<void> | null = null;
|
||||
let isErrorLoadingSdk = false;
|
||||
import { Result, wrapThrowsAsync } from "../../types/errorHandlers";
|
||||
|
||||
async function loadSDK(apiHost: string) {
|
||||
declare global {
|
||||
interface Window {
|
||||
formbricks: TFormbricksApp | TFormbricksWebsite;
|
||||
}
|
||||
}
|
||||
|
||||
// load the sdk, return the result
|
||||
async function loadFormbricksWebsiteSDK(apiHost: string): Promise<Result<void>> {
|
||||
if (!window.formbricks) {
|
||||
const res = await fetch(`${apiHost}/api/packages/website`);
|
||||
if (!res.ok) throw new Error("Failed to load Formbricks Website SDK");
|
||||
|
||||
// failed to fetch the app package
|
||||
if (!res.ok) {
|
||||
return { ok: false, error: new Error("Failed to load Formbricks Website SDK") };
|
||||
}
|
||||
|
||||
const sdkScript = await res.text();
|
||||
const scriptTag = document.createElement("script");
|
||||
scriptTag.innerHTML = sdkScript;
|
||||
document.head.appendChild(scriptTag);
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const checkInterval = setInterval(() => {
|
||||
if (window.formbricks) {
|
||||
const getFormbricks = async () =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const checkInterval = setInterval(() => {
|
||||
if (window.formbricks) {
|
||||
clearInterval(checkInterval);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
setTimeout(() => {
|
||||
clearInterval(checkInterval);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
setTimeout(() => {
|
||||
clearInterval(checkInterval);
|
||||
reject(new Error("Formbricks SDK loading timed out"));
|
||||
}, 10000);
|
||||
});
|
||||
reject(new Error("Formbricks Website SDK loading timed out"));
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
try {
|
||||
await getFormbricks();
|
||||
return { ok: true, data: undefined };
|
||||
} catch (error: any) {
|
||||
// formbricks loading failed, return the error
|
||||
return {
|
||||
ok: false,
|
||||
error: new Error(error.message ?? "Failed to load Formbricks Website SDK"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { ok: true, data: undefined };
|
||||
}
|
||||
|
||||
type FormbricksWebsiteMethods = {
|
||||
@@ -34,42 +61,45 @@ type FormbricksWebsiteMethods = {
|
||||
const formbricksProxyHandler: ProxyHandler<TFormbricksWebsite> = {
|
||||
get(_target, prop, _receiver) {
|
||||
return async (...args: any[]) => {
|
||||
if (!window.formbricks && !sdkLoadingPromise && !isErrorLoadingSdk) {
|
||||
const { apiHost } = args[0];
|
||||
sdkLoadingPromise = loadSDK(apiHost).catch((error) => {
|
||||
console.error(`🧱 Formbricks - Error loading SDK: ${error}`);
|
||||
sdkLoadingPromise = null;
|
||||
isErrorLoadingSdk = true;
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
if (isErrorLoadingSdk) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sdkLoadingPromise) {
|
||||
await sdkLoadingPromise;
|
||||
}
|
||||
|
||||
if (!window.formbricks) {
|
||||
throw new Error("Formbricks Website SDK is not available");
|
||||
if (prop !== "init") {
|
||||
console.error(
|
||||
"🧱 Formbricks - Global error: You need to call formbricks.init before calling any other method"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// still need to check if the apiHost is passed
|
||||
if (!args[0]) {
|
||||
console.error("🧱 Formbricks - Global error: You need to pass the apiHost as the first argument");
|
||||
return;
|
||||
}
|
||||
|
||||
const { apiHost } = args[0];
|
||||
const loadSDKResult = await wrapThrowsAsync(loadFormbricksWebsiteSDK)(apiHost);
|
||||
|
||||
if (!loadSDKResult.ok) {
|
||||
console.error(`🧱 Formbricks - Global error: ${loadSDKResult.error.message}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window.formbricks[prop as FormbricksWebsiteMethods] !== "function") {
|
||||
console.error(`🧱 Formbricks Website SDK does not support method ${String(prop)}`);
|
||||
if (window.formbricks && typeof window.formbricks[prop as FormbricksWebsiteMethods] !== "function") {
|
||||
console.error(
|
||||
`🧱 Formbricks - Global error: Formbricks Website SDK does not support method ${String(prop)}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
return (window.formbricks[prop as FormbricksWebsiteMethods] as Function)(...args);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error;
|
||||
console.error(`🧱 Formbricks - Global error: Something went wrong: ${error}`);
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const formbricksWebsite: TFormbricksWebsite = new Proxy({} as TFormbricksWebsite, formbricksProxyHandler);
|
||||
export default formbricksWebsite;
|
||||
const formbricksApp: TFormbricksWebsite = new Proxy({} as TFormbricksWebsite, formbricksProxyHandler);
|
||||
export default formbricksApp;
|
||||
|
||||
Reference in New Issue
Block a user