| 1 | import type { ConnectionTarget } from "@t3tools/client-runtime/connection"; |
| 2 | import { |
| 3 | PRIMARY_LOCAL_ENVIRONMENT_ID, |
| 4 | type DesktopBridge, |
| 5 | type DesktopEnvironmentBootstrap, |
| 6 | } from "@t3tools/contracts"; |
| 7 | |
| 8 | /** |
| 9 | * Desktop-local secondary backends (e.g. a parallel WSL backend) are registered |
| 10 | * by the connection platform source as bearer connections whose id carries this |
| 11 | * prefix. It is the renderer's single signal that an environment is a |
| 12 | * host-managed local backend rather than a user-saved remote, SSH, or relay |
| 13 | * environment. |
| 14 | * |
| 15 | * Keep this the one source of truth: the producer (`connection/platform.ts`) |
| 16 | * mints ids via {@link desktopLocalConnectionId} and every consumer classifies |
| 17 | * via {@link isDesktopLocalConnectionTarget}, so the convention can never drift |
| 18 | * between the two. |
| 19 | */ |
| 20 | const DESKTOP_LOCAL_CONNECTION_ID_PREFIX = "local:"; |
| 21 | |
| 22 | export function desktopLocalConnectionId(backendId: string): string { |
| 23 | return `${DESKTOP_LOCAL_CONNECTION_ID_PREFIX}${backendId}`; |
| 24 | } |
| 25 | |
| 26 | export function isDesktopLocalConnectionTarget( |
| 27 | target: ConnectionTarget, |
| 28 | ): target is Extract<ConnectionTarget, { readonly _tag: "BearerConnectionTarget" }> { |
| 29 | return ( |
| 30 | target._tag === "BearerConnectionTarget" && |
| 31 | target.connectionId.startsWith(DESKTOP_LOCAL_CONNECTION_ID_PREFIX) |
| 32 | ); |
| 33 | } |
| 34 | |
| 35 | export function desktopLocalBackendId(target: ConnectionTarget): string | null { |
| 36 | return isDesktopLocalConnectionTarget(target) |
| 37 | ? target.connectionId.slice(DESKTOP_LOCAL_CONNECTION_ID_PREFIX.length) |
| 38 | : null; |
| 39 | } |
| 40 | |
| 41 | export function isWslConnectionTarget(target: ConnectionTarget): boolean { |
| 42 | return desktopLocalBackendId(target)?.startsWith("wsl:") === true; |
| 43 | } |
| 44 | |
| 45 | export type DesktopSecondaryBootstrapsRead = |
| 46 | | { |
| 47 | readonly _tag: "Success"; |
| 48 | readonly bootstraps: ReadonlyArray<DesktopEnvironmentBootstrap>; |
| 49 | } |
| 50 | | { |
| 51 | readonly _tag: "Failure"; |
| 52 | readonly cause: unknown; |
| 53 | }; |
| 54 | |
| 55 | export interface DesktopSecondaryBootstrapsReader { |
| 56 | readonly readResult: () => DesktopSecondaryBootstrapsRead; |
| 57 | readonly readSnapshot: () => ReadonlyArray<DesktopEnvironmentBootstrap>; |
| 58 | } |
| 59 | |
| 60 | /** |
| 61 | * Build a topology reader whose snapshot advances only after successful bridge |
| 62 | * reads. A successful empty read is authoritative; a thrown read preserves the |
| 63 | * previous snapshot so UI consumers cannot temporarily disagree with the |
| 64 | * platform's retained registrations. |
| 65 | */ |
| 66 | export function createDesktopSecondaryBootstrapsReader( |
| 67 | resolveBridge: () => Pick<DesktopBridge, "getLocalEnvironmentBootstraps"> | undefined, |
| 68 | ): DesktopSecondaryBootstrapsReader { |
| 69 | let snapshot: ReadonlyArray<DesktopEnvironmentBootstrap> = []; |
| 70 | |
| 71 | const readResult = (): DesktopSecondaryBootstrapsRead => { |
| 72 | const bridge = resolveBridge(); |
| 73 | if (bridge === undefined) { |
| 74 | if (snapshot.length > 0) snapshot = []; |
| 75 | return { _tag: "Success", bootstraps: snapshot }; |
| 76 | } |
| 77 | try { |
| 78 | const next = bridge |
| 79 | .getLocalEnvironmentBootstraps() |
| 80 | .filter((entry) => entry.id !== PRIMARY_LOCAL_ENVIRONMENT_ID); |
| 81 | if ( |
| 82 | next.length !== snapshot.length || |
| 83 | next.some((entry, index) => { |
| 84 | const previous = snapshot[index]; |
| 85 | return ( |
| 86 | previous === undefined || |
| 87 | entry.id !== previous.id || |
| 88 | entry.label !== previous.label || |
| 89 | entry.runningDistro !== previous.runningDistro || |
| 90 | entry.httpBaseUrl !== previous.httpBaseUrl || |
| 91 | entry.wsBaseUrl !== previous.wsBaseUrl || |
| 92 | entry.bootstrapToken !== previous.bootstrapToken |
| 93 | ); |
| 94 | }) |
| 95 | ) { |
| 96 | snapshot = next; |
| 97 | } |
| 98 | return { _tag: "Success", bootstraps: snapshot }; |
| 99 | } catch (cause) { |
| 100 | return { _tag: "Failure", cause }; |
| 101 | } |
| 102 | }; |
| 103 | |
| 104 | return { |
| 105 | readResult, |
| 106 | readSnapshot: () => { |
| 107 | const result = readResult(); |
| 108 | return result._tag === "Success" ? result.bootstraps : snapshot; |
| 109 | }, |
| 110 | }; |
| 111 | } |
| 112 | |
| 113 | const desktopSecondaryBootstrapsReader = createDesktopSecondaryBootstrapsReader( |
| 114 | () => window.desktopBridge, |
| 115 | ); |
| 116 | |
| 117 | /** Read the topology while preserving failures for platform cache policy. */ |
| 118 | export function readDesktopSecondaryBootstrapsResult(): DesktopSecondaryBootstrapsRead { |
| 119 | return desktopSecondaryBootstrapsReader.readResult(); |
| 120 | } |
| 121 | |
| 122 | /** Read the latest successful topology snapshot for renderer consumers. */ |
| 123 | export function readDesktopSecondaryBootstraps(): ReadonlyArray<DesktopEnvironmentBootstrap> { |
| 124 | return desktopSecondaryBootstrapsReader.readSnapshot(); |
| 125 | } |