LCOV - code coverage report
Current view: top level - dashql-app/src/platform/database - embedded_database_provider.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 91.2 % 57 52
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : 
       3              : import type { EmbeddedComputeDatabase } from './embedded_database.js';
       4              : import type { Logger } from '../logger/logger.js';
       5              : import { useLogger } from '../logger/logger_provider.js';
       6              : import { useAppHandOver } from '../crosstab/app_hand_over.js';
       7              : 
       8            1 : const SETUP_CTX = React.createContext<EmbeddedDatabaseSetupFn | null>(null);
       9              : 
      10              : export interface SetupProgress {
      11              :     readonly bytesLoaded: number;
      12              :     readonly bytesTotal: number;
      13              : }
      14              : 
      15              : /// The startup options of the embedded database, they apply when it is instantiated
      16              : export interface EmbeddedDatabaseOptions {
      17              :     /// Run the queries on the experimental wasm backend of HyperDB, the default backend if the engine has none
      18              :     experimentalWasmBackend?: boolean;
      19              : }
      20              : 
      21            1 : const DEFAULT_OPTIONS: EmbeddedDatabaseOptions = {};
      22              : 
      23              : interface Props {
      24              :     children: React.ReactElement;
      25              :     setupDatabase?: EmbeddedDatabaseFactory;
      26              :     /// The startup options, null while they are not known yet, e.g. before the app settings are loaded.
      27              :     /// The instantiation waits for them.
      28              :     options?: EmbeddedDatabaseOptions | null;
      29              : }
      30              : 
      31              : type EmbeddedDatabaseFactory = (
      32              :     context: string,
      33              :     logger: Logger,
      34              :     onSetupProgress?: (progress: SetupProgress) => void,
      35              :     options?: EmbeddedDatabaseOptions,
      36              : ) => Promise<EmbeddedComputeDatabase>;
      37              : 
      38            1 : const setupDefaultDatabase: EmbeddedDatabaseFactory = async (context, logger, onSetupProgress, options) => {
      39            1 :     const { setupWebHyperDB } = await import('../hyperdb/hyperdb_provider_web.js');
      40            1 :     return await setupWebHyperDB(context, logger, onSetupProgress, options);
      41            1 : };
      42              : 
      43            1 : export const EmbeddedDatabaseProvider: React.FC<Props> = (props: Props) => {
      44            1 :     const logger = useLogger();
      45            1 :     const instantiation = React.useRef<Promise<EmbeddedComputeDatabase> | null>(null);
      46              : 
      47              :     // The options of the first instantiation, the instantiation waits until they are known
      48            1 :     const [options, resolveOptions] = React.useMemo(() => {
      49            1 :         let resolve: (options: EmbeddedDatabaseOptions) => void;
      50            1 :         const promise = new Promise<EmbeddedDatabaseOptions>(r => { resolve = r; });
      51            1 :         return [promise, resolve!] as const;
      52            1 :     }, []);
      53            1 :     const knownOptions = props.options === undefined ? DEFAULT_OPTIONS : props.options;
      54            1 :     React.useEffect(() => {
      55            1 :         if (knownOptions != null) resolveOptions(knownOptions);
      56            1 :     }, [knownOptions, resolveOptions]);
      57              : 
      58            1 :     const setup = React.useCallback(async (
      59            1 :         context: string,
      60            1 :         onSetupProgress?: (progress: SetupProgress) => void,
      61            1 :     ): Promise<EmbeddedComputeDatabase> => {
      62            1 :         if (instantiation.current != null) {
      63            1 :             return await instantiation.current;
      64              :         }
      65              : 
      66            1 :         const instantiate = async (): Promise<EmbeddedComputeDatabase> =>
      67            1 :             (props.setupDatabase ?? setupDefaultDatabase)(context, logger, onSetupProgress, await options);
      68              : 
      69            1 :         instantiation.current = instantiate().catch(error => {
      70            1 :             instantiation.current = null;
      71            1 :             throw error;
      72            1 :         });
      73            1 :         return await instantiation.current;
      74            1 :     }, [logger, props.setupDatabase, options]);
      75              : 
      76              :     // Another tab takes over the app. The database closes the files it shares with the other tab, e.g.
      77              :     // the log extracts of the connections, before that tab opens them.
      78            1 :     useAppHandOver(async () => {
      79            0 :         const pending = instantiation.current;
      80            0 :         instantiation.current = null;
      81            0 :         if (pending) {
      82            0 :             const database = await pending.catch(() => null);
      83            0 :             await database?.terminate();
      84              :         }
      85            1 :     });
      86              : 
      87            1 :     React.useEffect(() => {
      88            1 :         return () => {
      89            1 :             const pending = instantiation.current;
      90            1 :             instantiation.current = null;
      91            1 :             if (pending) {
      92            1 :                 pending
      93            1 :                     .then(database => database.terminate())
      94            1 :                     .catch(() => { /* instantiation failed - nothing to terminate */ });
      95              :             }
      96            1 :         };
      97            1 :     }, []);
      98              : 
      99            1 :     return (
     100            1 :         <SETUP_CTX.Provider value={setup}>
     101            1 :             {props.children}
     102            1 :         </SETUP_CTX.Provider>
     103              :     );
     104            1 : };
     105              : 
     106              : export type EmbeddedDatabaseSetupFn = (
     107              :     context: string,
     108              :     onSetupProgress?: (progress: SetupProgress) => void,
     109              : ) => Promise<EmbeddedComputeDatabase>;
     110            1 : export function useEmbeddedDatabaseSetup(): EmbeddedDatabaseSetupFn {
     111            1 :     return React.useContext(SETUP_CTX)!;
     112            1 : }
        

Generated by: LCOV version 2.0-1