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

            Line data    Source code
       1              : import * as dashql from '@ankoh/dashql-core';
       2              : 
       3              : import { TracedLogger } from '../../platform/logger/logger.js';
       4              : import type { AppStateRestorationProgress, RestoredAppState } from '../../session/persistence/app_state_loader.js';
       5              : import { AppLoadingProgress, AppLoadingProgressConsumer } from './app_loading_progress.js';
       6              : import { SetConnectionRegistryAction } from '../../connections/connection_registry.js';
       7              : import type { ConnectionState } from '../../connections/connection_state.js';
       8              : import { Dispatch } from '../../utils/variant.js';
       9              : import { SetSessionScriptsRegistryAction } from '../../session/scripts/session_scripts_registry.js';
      10              : import { ProgressCounter } from '../../utils/progress.js';
      11              : import { InvalidSession } from '../../storage/session_validation.js';
      12              : import { AppLoadingStatus } from './app_loading_status.js';
      13              : import type { RouteContext } from '../router/router.js';
      14              : import { isEphemeralSession, type SessionScripts } from '../../session/scripts/session_scripts.js';
      15              : 
      16            1 : export function canShowSession(route: RouteContext, pauseAfterSetup: boolean): boolean {
      17            1 :     return route.appLoadingStatus === AppLoadingStatus.SETUP_DONE
      18            1 :         && route.sessionId != null
      19            1 :         && (!pauseAfterSetup || route.confirmedFinishedSetup);
      20            1 : }
      21              : 
      22              : export interface AppLoadingResult {
      23              :     /// Sessions whose metadata failed validation and were refused a load (keyed by bare UUID).
      24              :     invalidSessions: Map<string, InvalidSession>;
      25              :     /// Valid restored sessions in persisted manifest order.
      26              :     restoredSessions: ReadonlyMap<string, SessionScripts>;
      27              :     /// The restored connections
      28              :     restoredConnections: Map<string, ConnectionState>;
      29              : }
      30              : 
      31              : /// The session that the app opens with: the restored session of the path, otherwise the app opens the greeter
      32            1 : export function selectStartupSession(
      33            1 :     requestedSessionId: string | null,
      34            1 :     restoredSessions: ReadonlyMap<string, SessionScripts>,
      35            1 : ): string | null {
      36            1 :     const requested = requestedSessionId == null ? undefined : restoredSessions.get(requestedSessionId);
      37            1 :     return requested != null && !isEphemeralSession(requested) ? requested.sessionId : null;
      38            1 : }
      39              : 
      40              : /// Delete the ephemeral sessions beyond the first one, the app keeps a single one.
      41              : /// A session that cannot be deleted is reported and the next launch deletes it again, it stays hidden until then.
      42            1 : export async function deleteSurplusEphemeralSessions(
      43            1 :     sessions: Iterable<SessionScripts>,
      44            1 :     deleteSession: (sessionId: string) => Promise<void>,
      45            1 :     onFailure: (sessionId: string, error: unknown) => void,
      46            1 : ): Promise<void> {
      47            1 :     for (const { sessionId } of [...sessions].filter(isEphemeralSession).slice(1)) {
      48            1 :         try {
      49            1 :             await deleteSession(sessionId);
      50            1 :         } catch (error) {
      51            1 :             onFailure(sessionId, error);
      52            1 :         }
      53              :     }
      54            1 : }
      55              : 
      56            1 : export async function shouldFinishInitialNavigation(initialInlineSetup: Promise<boolean> | null): Promise<boolean> {
      57            1 :     return initialInlineSetup == null || !await initialInlineSetup;
      58            1 : }
      59              : 
      60              : /// Restores the persisted app state
      61              : export interface AppStateRestorer {
      62              :     /// Restore the connections and sessions
      63              :     restoreAppState(core: dashql.DashQL, progressConsumer: (progress: AppStateRestorationProgress) => void): Promise<RestoredAppState>;
      64              : }
      65              : 
      66              : /// Main logic to setup the application
      67            1 : export async function loadApp(logger: TracedLogger, core: dashql.DashQL, storage: AppStateRestorer, resetConnections: Dispatch<SetConnectionRegistryAction>, resetSessionScripts: Dispatch<SetSessionScriptsRegistryAction>, consumer: AppLoadingProgressConsumer) {
      68            1 :     const traced = logger.childSpan();
      69            1 :     traced.info("Loading application", {}, "app_loading");
      70            1 :     const appLoadStartTime = performance.now();
      71              : 
      72            1 :     let progress: AppLoadingProgress = {
      73            1 :         restoreConnections: new ProgressCounter(),
      74            1 :         restoreCatalogs: new ProgressCounter(),
      75            1 :         restoreSessionScripts: new ProgressCounter(),
      76            1 :     };
      77            1 :     const partialProgressConsumer = (update: Partial<AppLoadingProgress>) => {
      78            1 :         progress = {
      79            1 :             ...progress,
      80            1 :             ...update
      81            1 :         };
      82            1 :         consumer(progress);
      83            1 :     };
      84              : 
      85            1 :     traced.info("Restoring application state", {}, "app_loading");
      86            1 :     const restoreStartTime = performance.now();
      87              : 
      88              :     /// First restore the previous app state
      89            1 :     const state = await storage.restoreAppState(core, partialProgressConsumer);
      90              : 
      91            1 :     const restoreDuration = performance.now() - restoreStartTime;
      92            1 :     traced.info("Restored application state", {
      93            1 :         connections: state.connectionStates.size.toString(),
      94            1 :         sessions: state.sessionScripts.size.toString(),
      95            1 :         durationMs: restoreDuration.toFixed(2)
      96            1 :     }, "app_loading");
      97              : 
      98              :     // Reset the connection registry
      99            1 :     traced.info("Updating connection registry", {
     100            1 :         connectionCount: state.connectionStates.size.toString()
     101            1 :     }, "app_loading");
     102            1 :     resetConnections({
     103            1 :         connections: state.connectionStates,
     104            1 :         connectionsByType: state.connectionStatesByType,
     105            1 :     });
     106              : 
     107              :     // Reset the session scripts registry
     108            1 :     traced.info("Updating session scripts registry", {
     109            1 :         sessionCount: state.sessionScripts.size.toString()
     110            1 :     }, "app_loading");
     111            1 :     resetSessionScripts({
     112            1 :         sessionScriptsMap: state.sessionScripts,
     113            1 :         sessionScriptsByConnection: state.sessionScriptsByConnection,
     114            1 :         invalidSessions: state.invalidSessions,
     115            1 :     });
     116              : 
     117            1 :     const totalAppLoadDuration = performance.now() - appLoadStartTime;
     118            1 :     traced.info("Application loading complete", {
     119            1 :         totalDurationMs: totalAppLoadDuration.toFixed(2)
     120            1 :     }, "app_loading");
     121              : 
     122            1 :     return {
     123            1 :         invalidSessions: state.invalidSessions,
     124            1 :         restoredSessions: state.sessionScripts,
     125            1 :         restoredConnections: state.connectionStates,
     126            1 :     };
     127            1 : }
        

Generated by: LCOV version 2.0-1