export interface WslUncPath { readonly distro: string; readonly linuxPath: string; } export interface WslEnvironmentCandidate { readonly environmentId: TEnvironmentId; readonly backendId: string; readonly runningDistro: string | null; } export interface WslEnvironmentConfiguration { readonly enabled: boolean; readonly wslOnly: boolean; readonly distro: string | null; readonly distros: ReadonlyArray<{ readonly name: string; readonly isDefault: boolean; }>; } export interface WslProjectSelection extends WslUncPath { readonly environmentId: TEnvironmentId; } const WSL_UNC_PREFIXES = ["\\\\wsl.localhost\\", "\\\\wsl$\\"] as const; const WSL_DISTRO_NAME_PATTERN = /^[A-Za-z0-9._-]+$/; const WSL_DEFAULT_BACKEND_ID = "wsl:default"; export function parseWslUncPath(input: string): WslUncPath | null { const normalized = input.trim().replaceAll("/", "\\"); const prefix = WSL_UNC_PREFIXES.find((candidate) => normalized.toLowerCase().startsWith(candidate.toLowerCase()), ); if (!prefix) { return null; } const rest = normalized.slice(prefix.length); const segments = rest.split("\\").filter((segment) => segment.length > 0); const distro = segments.shift(); if (!distro || !WSL_DISTRO_NAME_PATTERN.test(distro)) { return null; } return { distro, linuxPath: segments.length === 0 ? "/" : `/${segments.join("/")}`, }; } export function resolveWslProjectSelection( input: string, candidates: ReadonlyArray>, ): WslProjectSelection | null { const parsed = parseWslUncPath(input); if (!parsed) { return null; } const exact = candidates.find((candidate) => { if (!candidate.backendId.startsWith("wsl:")) { return false; } const backendDistro = candidate.backendId.slice("wsl:".length); const runningDistro = candidate.runningDistro ?? (backendDistro.length > 0 && backendDistro.toLowerCase() !== "default" ? backendDistro : null); return runningDistro?.toLowerCase() === parsed.distro.toLowerCase(); }); return exact ? { ...parsed, environmentId: exact.environmentId } : null; } interface ConfiguredWslBackend { readonly backendId: string; readonly runningDistro: string | null; } function resolveConfiguredWslBackend( configuration: WslEnvironmentConfiguration | null, ): ConfiguredWslBackend | null { if (!configuration) { return null; } if (configuration.distro === null) { return { backendId: WSL_DEFAULT_BACKEND_ID, runningDistro: null }; } const configuredDistro = configuration.distro.trim(); if (configuredDistro.length === 0) { return null; } const installedDistro = configuration.distros.find( (distro) => distro.name.toLowerCase() === configuredDistro.toLowerCase(), ); const resolvedDistro = installedDistro?.name ?? configuredDistro; return { backendId: `wsl:${resolvedDistro}`, runningDistro: resolvedDistro }; } export function resolveProjectPickerTarget(input: { readonly browseEnvironmentId: TEnvironmentId | null; readonly primaryEnvironmentId: TEnvironmentId | null; readonly desktopInstanceId: string | null; readonly wslConfiguration: WslEnvironmentConfiguration | null; }): string | null { if (input.desktopInstanceId !== null) { return input.desktopInstanceId; } if ( input.browseEnvironmentId === null || input.browseEnvironmentId !== input.primaryEnvironmentId || !input.wslConfiguration?.enabled || !input.wslConfiguration.wslOnly ) { return null; } return resolveConfiguredWslBackend(input.wslConfiguration)?.backendId ?? null; } export function applyWslEnvironmentConfiguration( candidates: ReadonlyArray>, primaryEnvironmentId: TEnvironmentId | null, configuration: WslEnvironmentConfiguration | null, primaryRunningDistro: string | null = null, ): ReadonlyArray> { if (!configuration) { return candidates; } const configuredBackend = resolveConfiguredWslBackend(configuration); if (!configuredBackend) { return candidates; } if ( configuration.enabled && configuration.wslOnly && primaryEnvironmentId !== null && !candidates.some((candidate) => candidate.environmentId === primaryEnvironmentId) ) { return [ ...candidates, { environmentId: primaryEnvironmentId, ...configuredBackend, runningDistro: primaryRunningDistro ?? configuredBackend.runningDistro, }, ]; } return candidates; }