Line data Source code
1 : import { isHyperWasmConnection } from './connection_label.js';
2 : import { isListedConnection, type ConnectionReferences, type ConnectionRegistry } from './connection_registry.js';
3 : import type { ConnectionState } from './connection_state.js';
4 :
5 : /// The connections that can replace a connection, grouped by their connector
6 : export interface ConnectionReplaceCandidates {
7 : /// The connections of the same connector
8 : sameConnector: ConnectionState[];
9 : /// The connections of other connectors
10 : otherConnectors: ConnectionState[];
11 : }
12 :
13 : /// The connections that can replace a connection.
14 : ///
15 : /// Every other listed connection can, the user can't see the others. The connections of the same
16 : /// connector come first, they are the likely replacements.
17 1 : export function connectionReplaceCandidates(
18 1 : registry: Pick<ConnectionRegistry, 'connections'>,
19 1 : references: ConnectionReferences,
20 1 : sourceId: string,
21 1 : ): ConnectionReplaceCandidates {
22 1 : const source = registry.connections.get(sourceId);
23 1 : const candidates: ConnectionReplaceCandidates = { sameConnector: [], otherConnectors: [] };
24 1 : if (source == null) return candidates;
25 1 : for (const [connectionId, connection] of registry.connections) {
26 1 : if (connectionId === sourceId || !isListedConnection(registry, references, connectionId)) continue;
27 1 : if (connection.connectorInfo.connectorType === source.connectorInfo.connectorType) {
28 1 : candidates.sameConnector.push(connection);
29 : } else {
30 1 : candidates.otherConnectors.push(connection);
31 : }
32 1 : }
33 1 : return candidates;
34 1 : }
35 :
36 : /// What keeps a connection from being replaced, null if it can be replaced.
37 : ///
38 : /// A replacement moves the scripts of every session to the other connection and removes the
39 : /// connection afterwards. Everything that would leave it half done blocks it up front:
40 : /// - The embedded Hyper is the connection that sessions fall back to, it is never removed.
41 : /// - Running queries would lose the connection they run on.
42 : /// - A session whose script changed outside the app holds its writes, its file can't follow.
43 1 : export function connectionReplaceBlocker(
44 1 : registry: Pick<ConnectionRegistry, 'connections'>,
45 1 : references: ConnectionReferences,
46 1 : sourceId: string,
47 1 : isSessionHeld: (sessionId: string) => boolean,
48 1 : ): string | null {
49 1 : const source = registry.connections.get(sourceId);
50 1 : if (source == null) return 'Unknown connection';
51 1 : if (isHyperWasmConnection(source)) return 'Sessions fall back to it';
52 1 : const running = source.queriesActiveOrdered.length;
53 1 : if (running > 0) return `${running} ${running === 1 ? 'query' : 'queries'} running`;
54 1 : const held = references.sessionsOf(sourceId).filter(isSessionHeld).length;
55 1 : if (held > 0) return `${held} ${held === 1 ? 'session' : 'sessions'} changed outside the app`;
56 1 : const candidates = connectionReplaceCandidates(registry, references, sourceId);
57 1 : if (candidates.sameConnector.length + candidates.otherConnectors.length === 0) return 'No other connection';
58 1 : return null;
59 1 : }
|