LCOV - code coverage report
Current view: top level - dashql-app/src/config - app_config.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 96.4 % 55 53
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : 
       3              : import { ConnectorConfigs, readConnectorConfigs } from '../connections/connector_configs.js';
       4              : import { useLogger } from '../platform/logger/logger_provider.js';
       5              : import { Logger } from '../platform/logger/logger.js';
       6              : import { createTrace } from '../platform/logger/trace_context.js';
       7              : import { AppFlavor, applyFlavorSettings } from './flavors.js';
       8              : import { useAppFlavor } from './flavor_provider.js';
       9              : 
      10            1 : const CONFIG_URL = new URL('../../static/config.json', import.meta.url);
      11              : 
      12              : export interface AppSettings {
      13              :     pauseAfterAppSetup?: boolean;
      14              :     enableTableColumnPlots?: boolean;
      15              :     experimental_file_attachments?: boolean;
      16              :     /// Run the embedded HyperDB on its experimental wasm backend, applied by a restart of the app.
      17              :     /// A hyperdb-wasm without the backend starts on the default backend.
      18              :     experimental_wasm_backend?: boolean;
      19              :     narrowScreenLayout?: boolean;
      20              :     /// Lay out the app like the desktop app on macOS, with placeholders of the traffic lights, to check the layout in the browser
      21              :     macWindowLayout?: boolean;
      22              :     /// Show the labels of the buttons in a wide rail on the left
      23              :     navRailWide?: boolean;
      24              :     scriptDebugMode?: boolean;
      25              :     tableDebugMode?: boolean;
      26              :     formattingDebugMode?: boolean;
      27              :     keywordCase?: 'lower' | 'upper';
      28              :     /// Force the OAuth identity provider to re-prompt for login instead of silently reusing an
      29              :     /// existing browser session. Adds `prompt=login` to the authorization request.
      30              :     forceReLogin?: boolean;
      31              :     minLogLevel?: number;
      32              :     /// The id of the app flavor, the flavor query parameter of the url wins
      33              :     flavor?: string;
      34              :     /// The color scheme of the app, the one of the OS if unset
      35              :     colorScheme?: 'light' | 'dark';
      36              :     /// The sessions whose scripts agents may read, edit and run through the local agent server
      37              :     agentSessions?: string[];
      38              :     /// The port of the local agent server, the build's default if unset
      39              :     agentPort?: number;
      40              :     /// The harness that runs the requests of the agent, the default harness of the agent server if unset
      41              :     agentHarness?: string;
      42              :     /// The model of the harness's requests, one of the models of the harness, the harness's own default if unset
      43              :     agentModel?: string;
      44              :     /// The effort of the harness's requests, one of the efforts of the harness, the harness's own default if unset
      45              :     agentEffort?: string;
      46              : }
      47              : 
      48              : export interface AppConfig {
      49              :     settings?: AppSettings;
      50              :     connectors?: ConnectorConfigs;
      51              : }
      52              : 
      53            1 : function readAppConfig(object: Record<string, object>): AppConfig {
      54            1 :     if (object.connectors) {
      55            1 :         object.connectors = readConnectorConfigs(object.connectors);
      56              :     }
      57            1 :     return object as AppConfig;
      58            1 : }
      59              : 
      60              : export interface AppConfigResult {
      61              :     config: AppConfig;
      62              :     traceId: number;
      63              : }
      64              : 
      65            1 : export async function downloadAppConfig(logger: Logger, flavor: AppFlavor): Promise<AppConfigResult> {
      66            1 :     const trace = createTrace();
      67            1 :     const traced = logger.withTrace(trace);
      68            1 :     try {
      69            1 :         const resp = await fetch(CONFIG_URL as unknown as string);
      70            1 :         const body = await resp.json();
      71            1 :         const config = readAppConfig(body);
      72            1 :         config.settings = applyFlavorSettings(config.settings, flavor);
      73            1 :         traced.info("Loaded app config", {}, "app_config");
      74            1 :         return { config, traceId: trace.traceId };
      75            1 :     } catch (e: any) {
      76            1 :         console.error(e);
      77            1 :         throw e;
      78            1 :     }
      79            1 : };
      80              : 
      81              : type ReconfigureStateFn = (res: AppConfig | null) => (AppConfig | null);
      82            1 : const RECONFIGURE_CTX = React.createContext<((config: ReconfigureStateFn) => void) | null>(null);
      83            1 : const CONFIG_CTX = React.createContext<AppConfig | null>(null);
      84            1 : const CONFIG_TRACE_ID_CTX = React.createContext<number | null>(null);
      85              : 
      86              : type Props = {
      87              :     children: React.ReactElement;
      88              : };
      89              : 
      90            1 : export const AppConfigProvider: React.FC<Props> = (props: Props) => {
      91            1 :     const logger = useLogger();
      92            1 :     const flavor = useAppFlavor();
      93            1 :     const configPromise = React.useRef<Promise<AppConfigResult>>(null);
      94            1 :     const [config, setConfig] = React.useState<AppConfig | null>(null);
      95            1 :     const [traceId, setTraceId] = React.useState<number | null>(null);
      96              : 
      97            1 :     React.useEffect(() => {
      98            1 :         if (!configPromise.current) {
      99            1 :             configPromise.current = downloadAppConfig(logger, flavor);
     100            1 :             configPromise.current.then(result => {
     101            1 :                 setConfig(result.config);
     102            1 :                 setTraceId(result.traceId);
     103            1 :             }).catch(() => {
     104            0 :                 setConfig(null);
     105            0 :                 setTraceId(null);
     106            1 :             });
     107            1 :         }
     108            1 :     }, []);
     109              : 
     110            1 :     const reconfigure = React.useCallback((mapper: ReconfigureStateFn) => {
     111            1 :         logger.info(`Reconfiguring application`, {}, "app_config");
     112            1 :         setConfig(c => mapper(c));
     113            1 :     }, []);
     114              : 
     115            1 :     return (
     116            1 :         <CONFIG_CTX.Provider value={config}>
     117            1 :             <CONFIG_TRACE_ID_CTX.Provider value={traceId}>
     118            1 :                 <RECONFIGURE_CTX.Provider value={reconfigure}>{props.children}</RECONFIGURE_CTX.Provider>
     119            1 :             </CONFIG_TRACE_ID_CTX.Provider>
     120            1 :         </CONFIG_CTX.Provider>
     121              :     );
     122            1 : };
     123              : 
     124            1 : export const useAppConfig = (): AppConfig | null => React.useContext(CONFIG_CTX);
     125              : export const useAppConfigTraceId = (): number | null => React.useContext(CONFIG_TRACE_ID_CTX);
     126            1 : export const useAppReconfigure = (): ((config: ReconfigureStateFn) => void) => React.useContext(RECONFIGURE_CTX)!;
        

Generated by: LCOV version 2.0-1