Repository navigation
Expand file tree
/
Copy pathbrowserTargetResolver.ts
More file actions
88 lines (81 loc) · 3.45 KB
/
Copy pathbrowserTargetResolver.ts
File metadata and controls
88 lines (81 loc) · 3.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import type {
BrowserNavigationTarget,
EnvironmentId,
PreviewUrlResolution,
} from "@t3tools/contracts";
import { isLoopbackHost, normalizePreviewUrl } from "@t3tools/shared/preview";
import { isLocalLoopbackHost, isPrivateNetworkHost } from "@t3tools/shared/hostClassification";
import type { ConnectionTarget } from "@t3tools/client-runtime/connection";
import { isDesktopLocalConnectionTarget } from "~/connection/desktopLocal";
import { isElectron } from "~/env";
import { readPreparedConnection } from "~/state/session";
export { normalizeHostname, isLocalLoopbackHost } from "@t3tools/shared/hostClassification";
const readEnvironmentUrl = (environmentId: EnvironmentId): URL => {
const connection = readPreparedConnection(environmentId);
if (!connection) throw new Error(`Environment ${environmentId} is not connected.`);
return new URL(connection.httpBaseUrl);
};
const resolveEnvironmentPortTarget = (
environmentId: EnvironmentId,
target: Extract<BrowserNavigationTarget, { readonly kind: "environment-port" }>,
environmentUrl: URL,
requestedUrl?: string,
sourceUrl?: URL,
): PreviewUrlResolution => {
if (!isPrivateNetworkHost(environmentUrl.hostname)) {
throw new Error(
"This environment port needs the planned authenticated preview gateway; its server address is not directly private-network reachable.",
);
}
const protocol = target.protocol ?? "http";
const path = target.path?.startsWith("/") ? target.path : `/${target.path ?? ""}`;
const normalizedEnvironmentHost = environmentUrl.hostname.replace(/^\[|\]$/g, "");
// localhost lets Chromium reach loopback servers bound to either IPv4 or IPv6.
const resolvedHost = isLocalLoopbackHost(normalizedEnvironmentHost)
? "localhost"
: normalizedEnvironmentHost.includes(":")
? `[${normalizedEnvironmentHost}]`
: normalizedEnvironmentHost;
const resolved = sourceUrl
? new URL(sourceUrl)
: new URL(path, `${protocol}://${resolvedHost}:${target.port}`);
if (sourceUrl) {
resolved.hostname = resolvedHost;
resolved.port = String(target.port);
}
return {
requestedUrl: requestedUrl ?? `${protocol}://localhost:${target.port}${path}`,
resolvedUrl: resolved.toString(),
resolutionKind: isLocalLoopbackHost(normalizedEnvironmentHost)
? "direct"
: "direct-private-network",
environmentId,
};
};
// Desktop renders primary and WSL tabs locally; loopback servers require localhost even for LAN backends.
const reachesEnvironmentLoopback = (target: ConnectionTarget): boolean =>
isElectron &&
(target._tag === "PrimaryConnectionTarget" || isDesktopLocalConnectionTarget(target));
export function resolveDiscoveredServerUrl(environmentId: EnvironmentId, rawUrl: string): string {
try {
const normalizedUrl = normalizePreviewUrl(rawUrl);
const parsed = new URL(normalizedUrl);
if (!isLoopbackHost(parsed.hostname)) return normalizedUrl;
const target = readPreparedConnection(environmentId)?.target;
if (target && reachesEnvironmentLoopback(target)) return normalizedUrl;
return resolveEnvironmentPortTarget(
environmentId,
{
kind: "environment-port",
port: Number(parsed.port || (parsed.protocol === "https:" ? 443 : 80)),
protocol: parsed.protocol === "https:" ? "https" : "http",
path: `${parsed.pathname}${parsed.search}${parsed.hash}`,
},
readEnvironmentUrl(environmentId),
rawUrl,
parsed,
).resolvedUrl;
} catch {
return rawUrl;
}
}