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

            Line data    Source code
       1              : import * as React from 'react';
       2              : import { OPEN_EPHEMERAL_SESSION, useRouterNavigate } from '../app/router/router.js';
       3              : import { useConnectionAllocator, useConnectionRegistryStore, sessionConnectionFromState } from '../connections/connection_registry.js';
       4              : import { ConnectionState } from '../connections/connection_state.js';
       5              : import { findEmbeddedConnection, newSessionConnection } from '../connections/embedded_connection.js';
       6              : import { getHyperLinkTabState, isHyperLinkParams } from '../connections/hyper/hyper_link.js';
       7              : import { useStorageWriter } from '../storage/storage_provider.js';
       8              : import { groupSessionManifestWrites, WRITE_SESSION_MANIFEST } from '../storage/storage_writer.js';
       9              : import {
      10              :     EPHEMERAL_SESSION_NAME,
      11              :     freeSessionName,
      12              :     isEphemeralSession,
      13              :     newScriptConnection,
      14              :     scriptConnectionOf,
      15              :     SessionScripts,
      16              :     SET_PENDING_CONNECTION,
      17              :     takenSessionNames,
      18              : } from './scripts/session_scripts.js';
      19              : import { useSessionScriptsAllocator, useSessionScriptsDispatch, useSessionScriptsRegistryReader } from './scripts/session_scripts_registry.js';
      20              : import { createEmptyMetadata } from './scripts/script_types.js';
      21              : import { SessionViewMode, useSessionViewMode } from './session_view_mode.js';
      22              : 
      23              : /// Create a session whose first script runs on a connection, a session without a name gets a free one
      24              : export type SessionCreation = (database: ConnectionState, name?: string) => SessionScripts;
      25              : 
      26              : /// Allocate a session without scripts and write its first manifest
      27            1 : function useSessionAllocation(): (database: ConnectionState, name: string, ephemeral: boolean) => SessionScripts {
      28            1 :     const allocateSessionScripts = useSessionScriptsAllocator();
      29            1 :     const storageWriter = useStorageWriter();
      30              : 
      31              :     // New sessions start without scripts; the feed greets them with the composer's landing prompt.
      32            1 :     return React.useCallback((database: ConnectionState, name: string, ephemeral: boolean) => {
      33            1 :         const sessionId = crypto.randomUUID();
      34            1 :         const [_sessionId, sessionScripts] = allocateSessionScripts({
      35            1 :             sessionMetadata: { ...createEmptyMetadata(), ...(ephemeral ? { ephemeral } : {}) },
      36            1 :             instance: database.instance,
      37            1 :             sessionId,
      38            1 :             name,
      39            1 :             pendingConnection: scriptConnectionOf(database),
      40            1 :             scripts: {},
      41            1 :             scriptRefs: {},
      42            1 :             scriptOrder: [],
      43            1 :             scriptFocus: { fileName: '' },
      44            1 :             semanticUserFocus: null,
      45            1 :         });
      46            1 :         void storageWriter.write(
      47            1 :             groupSessionManifestWrites(sessionId),
      48            1 :             { type: WRITE_SESSION_MANIFEST, value: [sessionId, [sessionConnectionFromState(database)], { ephemeral }] },
      49            1 :         );
      50            1 :         return sessionScripts;
      51            1 :     }, [allocateSessionScripts, storageWriter]);
      52            1 : }
      53              : 
      54            1 : export function useSessionCreation(): SessionCreation {
      55            1 :     const allocate = useSessionAllocation();
      56            1 :     const readRegistry = useSessionScriptsRegistryReader();
      57            1 :     return React.useCallback((database: ConnectionState, name?: string) => (
      58            1 :         allocate(database, name ?? freeSessionName(takenSessionNames(readRegistry().sessionScriptsMap.values())), false)
      59            1 :     ), [allocate, readRegistry]);
      60            1 : }
      61              : 
      62              : /// The ephemeral session of the greeter, created on the connection if there is none.
      63              : ///
      64              : /// There is one at most: the app creates it at launch and again after every promotion. It keeps the connection that the
      65              : /// user picked, unless the connection is the one of a Hyper link: a link moves the session to its connection, and a
      66              : /// session on the connection of a link that is gone moves back.
      67            1 : export function useEphemeralSession(): (database: ConnectionState) => SessionScripts {
      68            1 :     const allocate = useSessionAllocation();
      69            1 :     const readRegistry = useSessionScriptsRegistryReader();
      70            1 :     const modifySessionScripts = useSessionScriptsDispatch();
      71            1 :     return React.useCallback((database: ConnectionState) => {
      72            1 :         const existing = [...readRegistry().sessionScriptsMap.values()].find(isEphemeralSession);
      73            1 :         if (existing == null) return allocate(database, EPHEMERAL_SESSION_NAME, true);
      74            1 :         const current = newScriptConnection(existing, null);
      75            1 :         if (current?.connectionId !== database.connectionId && (
      76            1 :             getHyperLinkTabState().current === database.connectionId || (current?.params != null && isHyperLinkParams(current.params))
      77              :         )) {
      78            1 :             void modifySessionScripts(existing.sessionId, { type: SET_PENDING_CONNECTION, value: scriptConnectionOf(database) });
      79              :         }
      80            1 :         return existing;
      81            1 :     }, [allocate, modifySessionScripts, readRegistry]);
      82            1 : }
      83              : 
      84              : /// The ephemeral session on the connection that new sessions start on, null before the app set up its connections
      85            1 : export function useEnsureEphemeralSession(): () => SessionScripts | null {
      86            1 :     const registryStore = useConnectionRegistryStore();
      87            1 :     const allocateConnection = useConnectionAllocator();
      88            1 :     const ephemeralSession = useEphemeralSession();
      89            1 :     return React.useCallback(() => {
      90            1 :         const { connections } = registryStore.current;
      91            1 :         const embedded = findEmbeddedConnection(connections.values());
      92            1 :         if (embedded == null) return null;
      93            1 :         return ephemeralSession(newSessionConnection(connections, allocateConnection, embedded.instance));
      94            1 :     }, [allocateConnection, ephemeralSession, registryStore]);
      95            1 : }
      96              : 
      97              : /// Open the greeter, the feed of the ephemeral session at the root of the app
      98            1 : export function useOpenGreeter(): () => void {
      99            1 :     const ensureEphemeralSession = useEnsureEphemeralSession();
     100            1 :     const navigate = useRouterNavigate();
     101            1 :     const { setMode } = useSessionViewMode();
     102            1 :     return React.useCallback(() => {
     103            1 :         const session = ensureEphemeralSession();
     104            1 :         if (session == null) return;
     105            1 :         setMode(SessionViewMode.Session);
     106            1 :         navigate({ type: OPEN_EPHEMERAL_SESSION, value: session.sessionId });
     107            1 :     }, [ensureEphemeralSession, navigate, setMode]);
     108            1 : }
        

Generated by: LCOV version 2.0-1