import React from "react"; import ReactDOM from "react-dom/client"; import "./index.css"; import { Provider } from "./providers/provider.tsx"; import ErrorBoundary from "./components/ErrorBoundary"; import { detectInstallationSource, InstallationSource, } from "./utils/installation-source"; import { WidgetType } from "./utils/session-storage"; import App from "./App.tsx"; import { EmbeddedWidget } from "./components/embedded-widget/EmbeddedWidget"; import { captureStructuredError } from "./utils/sentry"; /** * Wait for document.body to be available with timeout. * This handles the case when script runs in before exists. * Timeout prevents infinite hang if body never appears (e.g., broken HTML). */ function waitForBody(timeoutMs: number = 10000): Promise { return new Promise((resolve, reject) => { if (document.body) { resolve(document.body); return; } let resolved = false; const timeoutId = setTimeout(() => { if (!resolved) { resolved = true; const error = new Error( `[Mori Widget] Timeout waiting for document.body after ${timeoutMs}ms` ); console.error(error.message); reject(error); } }, timeoutMs); const handleBodyReady = () => { if (!resolved && document.body) { resolved = true; clearTimeout(timeoutId); resolve(document.body); } }; // If body doesn't exist yet, wait for DOMContentLoaded if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", handleBodyReady, { once: true, }); } else { // readyState is 'interactive' or 'complete' but body is somehow null // This is rare but can happen - poll briefly const checkBody = () => { if (resolved) return; if (document.body) { handleBodyReady(); } else { requestAnimationFrame(checkBody); } }; checkBody(); } }); } // Auto-initialize in dev mode // @ts-ignore if (__VITE_IS_DEV_MODE__) { // Wait for DOM to be ready if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => { initializeDoriWidget(); }); } else { // DOM is already ready setTimeout(() => initializeDoriWidget(), 0); } } export async function initializeDoriWidget() { const roots: ReactDOM.Root[] = []; let installationSource: InstallationSource; try { installationSource = detectInstallationSource(); } catch (error) { // Fallback to default installationSource = InstallationSource.DIRECT_SCRIPT; } // Wait for body to be available before proceeding try { await waitForBody(); } catch (bodyError) { const error = bodyError instanceof Error ? bodyError : new Error(String(bodyError)); captureStructuredError(error, { operation: "initializeDoriWidget", additionalData: { context: "waitForBody", readyState: document.readyState, installationSource, }, }); return () => {}; // Return empty cleanup function } // Handle embedded widget initialization try { const embeddedContainer = document.getElementById( "mori-pdp-embedded-widget-container" ); if ( embeddedContainer && installationSource === InstallationSource.WORDPRESS_PLUGIN ) { let embeddedWidgetContainer = document.getElementById( "mori-react-embedded-widget-host" ); if (!embeddedWidgetContainer) { try { embeddedWidgetContainer = document.createElement("div"); embeddedWidgetContainer.id = "mori-react-embedded-widget-host"; embeddedContainer.appendChild(embeddedWidgetContainer); } catch (domError) { const error = domError instanceof Error ? domError : new Error(String(domError)); console.error("[Mori Widget] Failed to create embedded container:", error); captureStructuredError(error, { operation: "initializeDoriWidget", additionalData: { context: "createEmbeddedContainer", containerId: "mori-react-embedded-widget-host", installationSource, }, }); return () => {}; // Return empty cleanup function } } try { const embeddedRoot = ReactDOM.createRoot(embeddedWidgetContainer); embeddedRoot.render( // @ts-ignore __VITE_IS_DEV_MODE__ ? ( ) : ( ) ); roots.push(embeddedRoot); } catch (rootError) { const error = rootError instanceof Error ? rootError : new Error(String(rootError)); console.error("[Mori Widget] Failed to create embedded root:", error); captureStructuredError(error, { operation: "initializeDoriWidget", additionalData: { context: "createEmbeddedRoot", containerId: "mori-react-embedded-widget-host", installationSource, }, }); } } } catch (embeddedError) { const error = embeddedError instanceof Error ? embeddedError : new Error(String(embeddedError)); console.error("[Mori Widget] Embedded widget initialization failed:", error); captureStructuredError(error, { operation: "initializeDoriWidget", additionalData: { context: "embeddedWidgetInitialization", installationSource, }, }); } // Handle floating widget initialization try { let floatingWidgetContainer = document.getElementById( "mori-react-widget-host" ); if (!floatingWidgetContainer) { try { floatingWidgetContainer = document.createElement("div"); floatingWidgetContainer.id = "mori-react-widget-host"; document.body.appendChild(floatingWidgetContainer); } catch (domError) { const error = domError instanceof Error ? domError : new Error(String(domError)); console.error("[Mori Widget] Failed to create floating container:", error); captureStructuredError(error, { operation: "initializeDoriWidget", additionalData: { context: "createFloatingContainer", containerId: "mori-react-widget-host", installationSource, }, }); return () => {}; // Return empty cleanup function } } try { const floatingRoot = ReactDOM.createRoot(floatingWidgetContainer); floatingRoot.render( // @ts-ignore __VITE_IS_DEV_MODE__ ? ( ) : ( ) ); roots.push(floatingRoot); } catch (rootError) { const error = rootError instanceof Error ? rootError : new Error(String(rootError)); console.error("[Mori Widget] Failed to create floating root:", error); captureStructuredError(error, { operation: "initializeDoriWidget", additionalData: { context: "createFloatingRoot", containerId: "mori-react-widget-host", installationSource, }, }); } } catch (floatingError) { const error = floatingError instanceof Error ? floatingError : new Error(String(floatingError)); console.error("[Mori Widget] Floating widget initialization failed:", error); captureStructuredError(error, { operation: "initializeDoriWidget", additionalData: { context: "floatingWidgetInitialization", installationSource, }, }); } return () => { try { roots.forEach((root) => { try { root.unmount(); } catch (unmountError) {} }); // Remove containers for clean remount document.getElementById("mori-react-widget-host")?.remove(); document.getElementById("mori-react-embedded-widget-host")?.remove(); } catch (cleanupError) {} }; }