authorgravatar for 71498452+StiensWout@users.noreply.github.comWout Stiens <71498452+StiensWout@users.noreply.github.com> 2026-06-22 19:14:16+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-22 10:14:16-07:00
logc1e2408e1f317bba2fbb48a27498b4ae4bef47d8
treeb571e46068d16fcf660fe64af6569757cfb0582d
parent8b993bc9ff79c52be691f56f077abdac26f0e69f
signature Signed by PGP key B5690EEEBB952194

[codex] fix: preserve localhost preview hosts (#3499)

Co-authored-by: Codex <codex@openai.com>

2 files changed, 19 insertions(+), 0 deletions(-)

apps/web/src/browser/browserTargetResolver.test.ts+8
......@@ -47,6 +47,14 @@ describe("browser target resolver", () => {
4747 ).toBe("http://localhost:3000/app");
4848 });
4949
50 it("preserves localhost server-picker values when the prepared base is 127.0.0.1", async () => {
51 readPreparedConnection.mockReturnValue({ httpBaseUrl: "http://127.0.0.1:3773" });
52 const { resolveDiscoveredServerUrl } = await import("./browserTargetResolver");
53 expect(
54 resolveDiscoveredServerUrl(EnvironmentId.make("environment-1"), "localhost:5173/app?x=1#top"),
55 ).toBe("http://localhost:5173/app?x=1#top");
56 });
57
5058 it("normalizes public URLs without treating them as environment ports", async () => {
5159 const { resolveDiscoveredServerUrl } = await import("./browserTargetResolver");
5260 expect(resolveDiscoveredServerUrl(EnvironmentId.make("environment-1"), "example.com/app")).toBe(
apps/web/src/browser/browserTargetResolver.ts+11
......@@ -24,6 +24,11 @@ const isPrivateNetworkHost = (host: string): boolean => {
2424 );
2525};
2626
27const isLocalLoopbackHost = (host: string): boolean => {
28 const normalized = host.toLowerCase().replace(/^\[|\]$/g, "");
29 return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1";
30};
31
2732export function resolveBrowserNavigationTarget(
2833 environmentId: EnvironmentId,
2934 target: BrowserNavigationTarget,
......@@ -68,6 +73,12 @@ export function resolveDiscoveredServerUrl(environmentId: EnvironmentId, rawUrl:
6873 const normalizedUrl = normalizePreviewUrl(rawUrl);
6974 const parsed = new URL(normalizedUrl);
7075 if (!isLoopbackHost(parsed.hostname)) return normalizedUrl;
76 const connection = readPreparedConnection(environmentId);
77 if (!connection) throw new Error(`Environment ${environmentId} is not connected.`);
78 const environmentUrl = new URL(connection.httpBaseUrl);
79 if (parsed.hostname !== "0.0.0.0" && isLocalLoopbackHost(environmentUrl.hostname)) {
80 return normalizedUrl;
81 }
7182 const port = Number(parsed.port || (parsed.protocol === "https:" ? 443 : 80));
7283 return resolveBrowserNavigationTarget(environmentId, {
7384 kind: "environment-port",