Line data Source code
1 : import * as React from 'react';
2 : import { createRoot } from 'react-dom/client';
3 : import { Route, Routes, Navigate, BrowserRouter, HashRouter, useLocation } from 'react-router-dom';
4 :
5 : import { AppConfigProvider, useAppConfig } from '../config/app_config.js';
6 : import { AppLoader } from './loading/app_loader.js';
7 : import { AppOwnershipGate } from './app_ownership_gate.js';
8 : import { AppSettingsSync, useAppSettingsReady } from '../config/app_settings_sync.js';
9 : import { SessionCatalogLoaderProvider } from '../session/scripts/session_catalog_loader.js';
10 : import { ComputationRegistry } from '../compute/computation_registry.js';
11 : import { ComputationScheduler } from '../compute/computation_scheduler.js';
12 : import { ConnectionRegistry } from '../connections/connection_registry.js';
13 : import { ConnectionPersistenceProvider } from '../connections/connection_persistence.js';
14 : import { ComputeConnectionProvider } from '../compute/compute_connection_provider.js';
15 : import { DashQLCoreProvider } from '../runtime/core_provider.js';
16 : import { FileDropzone } from './file_dropzone.js';
17 : import { FlavorProvider, FlavorUrlKeeper } from '../config/flavor_provider.js';
18 : import { getRequestedFlavor } from '../config/flavor_resolution.js';
19 : import { GitHubTheme } from '../styles/github_theme.js';
20 : import { HttpClientProvider } from '../platform/http/http_client_provider.js';
21 : import { HyperSetupProvider } from '../connections/hyper/hyper_connection_setup.js';
22 : import { HyperLogMetricsProvider } from '../connections/hyper/hyper_log_metrics_provider.js';
23 : import { HyperDatabaseClientProvider } from '../connections/hyper/hyperdb_grpc_client_provider.js';
24 : import { LoggerToast } from '../ui/logger/logger_toast.js';
25 : import { NavBarContainer } from './navbar.js';
26 : import { PlatformEventListenerProvider } from '../platform/events/event_listener_provider.js';
27 : import { QueryExecutorProvider } from '../connections/query_executor.js';
28 : import { RouterReset } from './router/router_reset.js';
29 : import { SalesforceConnector } from '../connections/salesforce/salesforce_connector.js';
30 : import { StorageProvider } from '../storage/storage_provider.js';
31 : import { TrinoConnector } from '../connections/trino/trino_connector.js';
32 : import { VersionCheck } from '../platform/version/version_check.js';
33 : import { SessionCommands } from '../session/scripts/session_commands.js';
34 : import { SessionViewModeProvider } from '../session/session_view_mode.js';
35 : import { SessionPage } from '../session/session_page.js';
36 : import { SessionScriptsRegistryProvider } from '../session/scripts/session_scripts_registry.js';
37 : import { QueryResultCacheProvider } from '../storage/query_result_cache_provider.js';
38 : import { getGlobalLogger, LoggerProvider } from '../platform/logger/logger_provider.js';
39 : import { stringifyError } from '../platform/logger/logger.js';
40 : import { EmbeddedDatabaseProvider, type EmbeddedDatabaseOptions } from '../platform/database/embedded_database_provider.js';
41 : import { NativeSessionSync } from '../session/persistence/native_session_sync_react.js';
42 : import { SessionComputeQueryExecutionProvider } from './session_compute_query_execution.js';
43 : import { SessionImportProvider } from '../session/persistence/session_import_provider.js';
44 : import { SessionExecutionProvider } from '../session/scripts/session_execution_provider.js';
45 : import { AgentBridgeProvider } from '../agent/agent_bridge.js';
46 :
47 : import '@ankoh/dashql-ui/fonts/fonts.css';
48 : import '@ankoh/dashql-ui/styles/colors.css';
49 : import '@ankoh/dashql-ui/styles/tokens.css';
50 : import '../styles/globals.css';
51 : import '@ankoh/dashql-ui/styles/flavors/sidb.css';
52 :
53 1 : const LOG_CTX = 'app';
54 :
55 : // Note that the order among connection providers is important and non-obvious.
56 : // For example:
57 : // - SessionScriptsRegistryProvider comes first, the connection layer reads from it which sessions use a connection.
58 : // - SessionCatalogLoaderProvider requires SessionScriptsRegistryProvider to mark connection scripts as outdated.
59 1 : const SessionProviders = (props: { children: React.ReactElement }) => (
60 1 : <SessionScriptsRegistryProvider>
61 1 : <ConnectionRegistry>
62 1 : <ConnectionPersistenceProvider>
63 1 : <SalesforceConnector>
64 1 : <HyperLogMetricsProvider>
65 1 : <HyperSetupProvider>
66 1 : <TrinoConnector>
67 1 : <ComputationRegistry>
68 1 : <SessionComputeQueryExecutionProvider>
69 1 : <ComputationScheduler />
70 1 : <QueryExecutorProvider>
71 1 : <SessionExecutionProvider>
72 1 : <NativeSessionSync />
73 1 : <AgentBridgeProvider>
74 1 : <SessionCatalogLoaderProvider>
75 1 : <SessionImportProvider>
76 1 : <SessionViewModeProvider>
77 1 : <SessionCommands>
78 1 : <AppLoader>
79 1 : {props.children}
80 1 : </AppLoader>
81 1 : </SessionCommands>
82 1 : </SessionViewModeProvider>
83 1 : </SessionImportProvider>
84 1 : </SessionCatalogLoaderProvider>
85 1 : </AgentBridgeProvider>
86 1 : </SessionExecutionProvider>
87 1 : </QueryExecutorProvider>
88 1 : </SessionComputeQueryExecutionProvider>
89 1 : </ComputationRegistry>
90 1 : </TrinoConnector>
91 1 : </HyperSetupProvider>
92 1 : </HyperLogMetricsProvider>
93 1 : </SalesforceConnector>
94 1 : </ConnectionPersistenceProvider>
95 1 : </ConnectionRegistry>
96 1 : </SessionScriptsRegistryProvider>
97 : );
98 :
99 : /// The embedded database with the startup options of the app settings.
100 : /// It waits until the persisted settings are loaded, a change of the options applies with a restart of the app.
101 1 : const AppEmbeddedDatabaseProvider = (props: { children: React.ReactElement }) => {
102 1 : const config = useAppConfig();
103 1 : const settingsReady = useAppSettingsReady();
104 1 : const experimentalWasmBackend = config?.settings?.experimental_wasm_backend === true;
105 1 : const options = React.useMemo<EmbeddedDatabaseOptions | null>(
106 1 : () => (settingsReady ? { experimentalWasmBackend } : null),
107 1 : [settingsReady, experimentalWasmBackend],
108 1 : );
109 1 : return <EmbeddedDatabaseProvider options={options}>{props.children}</EmbeddedDatabaseProvider>;
110 1 : };
111 :
112 1 : const AppProviders = (props: { children: React.ReactElement }) => (
113 1 : <GitHubTheme>
114 1 : <LoggerProvider>
115 1 : <LoggerToast />
116 1 : <AppConfigProvider>
117 1 : <PlatformEventListenerProvider>
118 1 : <VersionCheck>
119 1 : <AppOwnershipGate>
120 1 : <StorageProvider>
121 1 : <HttpClientProvider>
122 1 : <AppSettingsSync>
123 1 : <HyperDatabaseClientProvider>
124 1 : <DashQLCoreProvider initialSetup={{ setupBatchWorker: true }}>
125 1 : <AppEmbeddedDatabaseProvider>
126 1 : <ComputeConnectionProvider>
127 1 : <QueryResultCacheProvider>
128 1 : <SessionProviders>
129 1 : {props.children}
130 1 : </SessionProviders>
131 1 : </QueryResultCacheProvider>
132 1 : </ComputeConnectionProvider>
133 1 : </AppEmbeddedDatabaseProvider>
134 1 : </DashQLCoreProvider>
135 1 : </HyperDatabaseClientProvider>
136 1 : </AppSettingsSync>
137 1 : </HttpClientProvider>
138 1 : </StorageProvider>
139 1 : </AppOwnershipGate>
140 1 : </VersionCheck>
141 1 : </PlatformEventListenerProvider>
142 1 : </AppConfigProvider>
143 1 : </LoggerProvider>
144 1 : </GitHubTheme>
145 : );
146 :
147 1 : const Router = process.env.DASHQL_RELATIVE_IMPORTS ? HashRouter : BrowserRouter;
148 :
149 1 : const NavigateWithState = (props: { to: string }): React.ReactElement => {
150 0 : const location = useLocation();
151 0 : return <Navigate to={props.to} replace state={location.state} />;
152 0 : };
153 :
154 0 : function logRecoverableReactError(error: unknown, errorInfo: React.ErrorInfo) {
155 : // We're not part of the provider tree.
156 : // Access the logger globally.
157 0 : const logger = getGlobalLogger();
158 : // Do nothing if it's not existing yet
159 0 : if (logger == null) {
160 0 : return;
161 : }
162 0 : console.log(error);
163 0 : console.log(errorInfo.componentStack);
164 0 : logger.info("React encountered a recoverable error", {
165 0 : error: stringifyError(error),
166 0 : stack: errorInfo.componentStack,
167 0 : }, LOG_CTX);
168 :
169 0 : }
170 :
171 1 : const element = document.getElementById('root');
172 1 : const root = createRoot(element!, {
173 1 : onRecoverableError: logRecoverableReactError
174 1 : });
175 1 : root.render(
176 1 : <FlavorProvider requested={getRequestedFlavor()}>
177 1 : <Router>
178 1 : <RouterReset />
179 1 : <FlavorUrlKeeper />
180 1 : <AppProviders>
181 1 : <FileDropzone>
182 1 : <NavBarContainer>
183 1 : <Routes>
184 1 : <Route index Component={SessionPage} />
185 1 : <Route path="/session" Component={SessionPage} />
186 1 : <Route path="/sessions/:sessionId" Component={SessionPage} />
187 1 : <Route path="*" element={<NavigateWithState to="/" />} />
188 1 : </Routes>
189 1 : </NavBarContainer>
190 1 : </FileDropzone>
191 1 : </AppProviders>
192 1 : </Router>
193 1 : </FlavorProvider>
194 1 : );
|