Skip to content

Commit a07754f

Browse files
committed
Extract shared isLoopbackHostname utility to @t3tools/shared/hostname
Consolidate three duplicate isLoopbackHostname implementations (in apps/server/src/http.ts, apps/web/src/environments/primary/target.ts, and apps/web/src/components/settings/ConnectionsSettings.tsx) into a single shared module at packages/shared/src/hostname.ts. This eliminates the risk of the implementations drifting apart when the set of recognized loopback hostnames changes.
1 parent 04e211a commit a07754f

File tree

6 files changed

+24
-33
lines changed

6 files changed

+24
-33
lines changed

apps/server/src/http.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { describe, expect, it } from "vitest";
22

3-
import { isLoopbackHostname, resolveDevRedirectUrl } from "./http.ts";
3+
import { isLoopbackHostname } from "@t3tools/shared/hostname";
4+
5+
import { resolveDevRedirectUrl } from "./http.ts";
46

57
describe("http dev routing", () => {
68
it("treats localhost and loopback addresses as local", () => {

apps/server/src/http.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
} from "effect/unstable/http";
1212
import { OtlpTracer } from "effect/unstable/observability";
1313

14+
import { isLoopbackHostname } from "@t3tools/shared/hostname";
15+
1416
import {
1517
ATTACHMENTS_ROUTE_PREFIX,
1618
normalizeAttachmentRelativePath,
@@ -28,22 +30,13 @@ import { ServerEnvironment } from "./environment/Services/ServerEnvironment.ts";
2830
const PROJECT_FAVICON_CACHE_CONTROL = "public, max-age=3600";
2931
const FALLBACK_PROJECT_FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="#6b728080" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-fallback="project-favicon"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-8l-2-2H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2Z"/></svg>`;
3032
const OTLP_TRACES_PROXY_PATH = "/api/observability/v1/traces";
31-
const LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "::1", "localhost"]);
3233

3334
export const browserApiCorsLayer = HttpRouter.cors({
3435
allowedMethods: ["GET", "POST", "OPTIONS"],
3536
allowedHeaders: ["authorization", "b3", "traceparent", "content-type"],
3637
maxAge: 600,
3738
});
3839

39-
export function isLoopbackHostname(hostname: string): boolean {
40-
const normalizedHostname = hostname
41-
.trim()
42-
.toLowerCase()
43-
.replace(/^\[(.*)\]$/, "$1");
44-
return LOOPBACK_HOSTNAMES.has(normalizedHostname);
45-
}
46-
4740
export function resolveDevRedirectUrl(devUrl: URL, requestUrl: URL): string {
4841
const redirectUrl = new URL(devUrl.toString());
4942
redirectUrl.pathname = requestUrl.pathname;

apps/web/src/components/settings/ConnectionsSettings.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
4646
import { Button } from "../ui/button";
4747
import { Textarea } from "../ui/textarea";
4848
import { setPairingTokenOnUrl } from "../../pairingUrl";
49+
import { isLoopbackHostname } from "@t3tools/shared/hostname";
50+
4951
import {
5052
createServerPairingCredential,
5153
fetchSessionState,
@@ -253,16 +255,6 @@ function resolveCurrentOriginPairingUrl(credential: string): string {
253255
return setPairingTokenOnUrl(url, credential).toString();
254256
}
255257

256-
function isLoopbackHostname(hostname: string): boolean {
257-
const normalized = hostname.trim().toLowerCase();
258-
return (
259-
normalized === "localhost" ||
260-
normalized === "127.0.0.1" ||
261-
normalized === "::1" ||
262-
normalized === "[::1]"
263-
);
264-
}
265-
266258
type PairingLinkListRowProps = {
267259
pairingLink: ServerPairingLinkRecord;
268260
endpointUrl: string | null | undefined;

apps/web/src/environments/primary/target.ts

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import type { DesktopEnvironmentBootstrap } from "@t3tools/contracts";
22
import type { KnownEnvironment } from "@t3tools/client-runtime";
3+
import { isLoopbackHostname } from "@t3tools/shared/hostname";
34

45
export interface PrimaryEnvironmentTarget {
56
readonly source: KnownEnvironment["source"];
67
readonly target: KnownEnvironment["target"];
78
}
89

9-
const LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "::1", "localhost"]);
10-
1110
function getDesktopLocalEnvironmentBootstrap(): DesktopEnvironmentBootstrap | null {
1211
return window.desktopBridge?.getLocalEnvironmentBootstrap() ?? null;
1312
}
@@ -16,17 +15,6 @@ function normalizeBaseUrl(rawValue: string): string {
1615
return new URL(rawValue, window.location.origin).toString();
1716
}
1817

19-
function normalizeHostname(hostname: string): string {
20-
return hostname
21-
.trim()
22-
.toLowerCase()
23-
.replace(/^\[(.*)\]$/, "$1");
24-
}
25-
26-
function isLoopbackHostname(hostname: string): boolean {
27-
return LOOPBACK_HOSTNAMES.has(normalizeHostname(hostname));
28-
}
29-
3018
function resolveHttpRequestBaseUrl(httpBaseUrl: string): string {
3119
const configuredDevServerUrl = import.meta.env.VITE_DEV_SERVER_URL?.trim();
3220
if (!configuredDevServerUrl) {

packages/shared/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151
"./projectScripts": {
5252
"types": "./src/projectScripts.ts",
5353
"import": "./src/projectScripts.ts"
54+
},
55+
"./hostname": {
56+
"types": "./src/hostname.ts",
57+
"import": "./src/hostname.ts"
5458
}
5559
},
5660
"scripts": {

packages/shared/src/hostname.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "::1", "localhost"]);
2+
3+
function normalizeHostname(hostname: string): string {
4+
return hostname
5+
.trim()
6+
.toLowerCase()
7+
.replace(/^\[(.*)\]$/, "$1");
8+
}
9+
10+
export function isLoopbackHostname(hostname: string): boolean {
11+
return LOOPBACK_HOSTNAMES.has(normalizeHostname(hostname));
12+
}

0 commit comments

Comments
 (0)