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) {}
};
}