export type CV = typeof import('@techstark/opencv-js'); // Everything returns { cv }: the cv module is a thenable, so resolving with it directly never settles. let loading: Promise<{ cv: CV }> | null = null; export function loadOpenCV(): Promise<{ cv: CV }> { loading ??= (async () => { const { cv } = typeof document === 'undefined' ? await importModule() : await loadScript(); if (!cv.Mat) { await new Promise((resolve) => { (cv as unknown as { onRuntimeInitialized: () => void }).onRuntimeInitialized = () => resolve(); }); } return { cv }; })(); return loading; } /** Node (tests). The variable specifier keeps the bundler from emitting a second OpenCV chunk. */ async function importModule(): Promise<{ cv: CV }> { const specifier = '@techstark/opencv-js'; const mod = (await import(/* @vite-ignore */ specifier)) as { default: CV }; return { cv: mod.default }; } // A script tag, not import(): bundler CJS interop copies `then` and the import never settles. async function loadScript(): Promise<{ cv: CV }> { const { default: url } = await import('@techstark/opencv-js/dist/opencv.js?url'); await new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = url; script.onload = () => resolve(); script.onerror = () => reject(new Error('Failed to load OpenCV')); document.head.append(script); }); return { cv: (window as unknown as { cv: CV }).cv }; }