LCOV - code coverage report
Current view: top level - dashql-app/src/app/loading - initial_memory_setup.ts Coverage Total Hit
Test: DashQL 915828b Lines: 96.9 % 64 62
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : 
       3              : import type { Logger } from '../../platform/logger/logger.js';
       4              : import { stringifyError } from '../../platform/logger/logger.js';
       5              : import type { DynamicConnectionDispatch } from '../../connections/connection_registry.js';
       6              : import { ConnectionHealth, type ConnectionState } from '../../connections/connection_state.js';
       7              : import type { HyperSetupApi } from '../../connections/hyper/hyper_connection_setup.js';
       8              : import { cancelConnectionSetup, registerConnectionSetup } from '../../connections/connection_setup_cancellation.js';
       9              : import { isHyperLinkParams, useHyperLinkTabState } from '../../connections/hyper/hyper_link.js';
      10              : 
      11            1 : const startupAttempts = new Map<string, AbortController>();
      12              : 
      13              : // Settings can cancel the loader-owned attempt without creating a second setup controller.
      14            1 : export function cancelInitialMemorySetup(connectionId: string): boolean {
      15            1 :     const abort = startupAttempts.get(connectionId);
      16            1 :     if (abort == null) return false;
      17            1 :     cancelConnectionSetup(connectionId);
      18            1 :     return true;
      19            1 : }
      20              : 
      21              : /// Set up the embedded database and the Hyper of a link once for the session the app opens with.
      22              : /// Every connection of the session is considered, other connections are connected by the user.
      23            1 : export function useInitialMemorySetup(
      24            1 :     sessionId: string | null,
      25            1 :     connections: readonly ConnectionState[] | null,
      26            1 :     setup: HyperSetupApi | null,
      27            1 :     dispatch: DynamicConnectionDispatch,
      28            1 :     logger: Logger,
      29            1 : ): void {
      30            1 :     const hyperLinks = useHyperLinkTabState();
      31            1 :     const considered = React.useRef(new Set<string>());
      32            1 :     const attempts = React.useRef(new Map<string, AbortController>());
      33            1 :     const considerConnection = (database: ConnectionState, setup: HyperSetupApi) => {
      34            1 :         const params = database.params;
      35              :         // The connection to the Hyper of a link connects once per link, without the token of a link it waits for one
      36            1 :         const linked = isHyperLinkParams(params);
      37            1 :         const token = hyperLinks.tokens[database.connectionId];
      38            1 :         if (linked && token == null) return;
      39            1 :         const key = linked && 'hyper' in params ? `${database.connectionId} ${params.hyper.endpoint} ${token}` : database.connectionId;
      40            1 :         if (considered.current.has(key)) return;
      41              :         // Consume the initial selection even when online, cancelled, or carrying a draft.
      42              :         // Later resets, draft discards, and session switches must not opt back into setup.
      43            1 :         considered.current.add(key);
      44            1 :         if (database.paramsDraft != null
      45            1 :             || !('hyper' in params)
      46            1 :             || (params.hyper.protocol !== 'WASM' && !linked)) return;
      47            1 :         if (database.connectionHealth !== ConnectionHealth.NOT_STARTED) {
      48            1 :             if (!linked) return;
      49              :             // The connection speaks to the Hyper of an earlier link
      50            1 :             cancelConnectionSetup(database.connectionId);
      51            1 :             void setup.reset(action => dispatch(database.connectionId, action));
      52            1 :         }
      53            1 :         const abort = new AbortController();
      54            1 :         attempts.current.set(database.connectionId, abort);
      55            1 :         startupAttempts.set(database.connectionId, abort);
      56            1 :         const unregister = registerConnectionSetup(database.connectionId, abort);
      57            1 :         void setup.setup(
      58            1 :             action => dispatch(database.connectionId, action),
      59            1 :             structuredClone(params.hyper) as Parameters<HyperSetupApi['setup']>[1],
      60            1 :             abort.signal,
      61            1 :             database.connectionId,
      62            1 :         ).catch(error => {
      63            1 :             if (!abort.signal.aborted) logger.warn('Memory database setup failed', {
      64            0 :                 sessionId, error: stringifyError(error),
      65            0 :             }, 'app_loader');
      66            1 :         }).finally(() => {
      67            1 :             unregister();
      68            1 :             attempts.current.delete(database.connectionId);
      69            1 :             if (startupAttempts.get(database.connectionId) === abort) startupAttempts.delete(database.connectionId);
      70            1 :         });
      71            1 :     };
      72            1 :     React.useEffect(() => {
      73            1 :         if (sessionId == null || connections == null || setup == null) return;
      74            1 :         for (const database of connections) {
      75            1 :             considerConnection(database, setup);
      76              :         }
      77            1 :     }, [connections, dispatch, hyperLinks, logger, sessionId, setup]);
      78            1 :     React.useEffect(() => () => {
      79            1 :         for (const [connectionId, abort] of attempts.current) {
      80            1 :             abort.abort(new DOMException('App loader unmounted', 'AbortError'));
      81            1 :             if (startupAttempts.get(connectionId) === abort) startupAttempts.delete(connectionId);
      82            1 :         }
      83            1 :     }, []);
      84            1 : }
        

Generated by: LCOV version 2.0-1