import React, { ReactElement } from 'react'; import * as duckdb from '@duckdb/duckdb-wasm'; import { useDuckDBLogger, useDuckDBBundleResolver } from './platform_provider'; import { Resolvable, Resolver } from './resolvable'; const setupCtx = React.createContext | null>(null); const resolverCtx = React.createContext | null>(null); export const useDuckDB = (): Resolvable => React.useContext(setupCtx)!; export const useDuckDBResolver = (): Resolver => React.useContext(resolverCtx)!; type DuckDBProps = { children: React.ReactElement | ReactElement[]; config?: duckdb.DuckDBConfig; value?: duckdb.AsyncDuckDB; }; export const DuckDBProvider: React.FC = (props: DuckDBProps) => { const logger = useDuckDBLogger(); const resolveBundle = useDuckDBBundleResolver(); const [setup, updateSetup] = React.useState>( new Resolvable(), ); const worker = React.useRef(null); React.useEffect( () => () => { if (worker.current != null) { worker.current.terminate(); worker.current = null; } }, [], ); const inFlight = React.useRef | null>(null); const resolver = React.useCallback(async () => { // Run only once if (inFlight.current) return await inFlight.current; inFlight.current = (async () => { // Resolve bundle const bundle = await resolveBundle(); if (bundle == null) { updateSetup(s => s.failWith('invalid bundle')); return null; } // Create worker and next database let worker: Worker; let next: duckdb.AsyncDuckDB; try { worker = new Worker(bundle.mainWorker!); next = new duckdb.AsyncDuckDB(logger, worker); } catch (e: any) { updateSetup(s => s.failWith(e)); return null; } // Instantiate the database asynchronously try { await next.instantiate(bundle.mainModule, bundle.pthreadWorker, (p: duckdb.InstantiationProgress) => { try { updateSetup(s => s.updateRunning(p)); } catch (e: any) { console.warn(`progress handler failed with error: ${e.toString()}`); } }); if (props.config !== undefined) { await next.open(props.config!); } } catch (e: any) { updateSetup(s => s.failWith(e)); return null; } updateSetup(s => s.completeWith(next)); return next; })(); return await inFlight.current; }, [logger]); return ( {props.children} ); };