Skip to content

Commit b1ad5fa

Browse files
committed
Fix duplicated port logic and negative web port probe caching
- Extract normalizeRunningPorts and portStatusLabel into @t3tools/shared/port - Update server (process/utils.ts) and web (terminalStateStore.ts) to import from the shared package instead of maintaining duplicate implementations - Update Sidebar.tsx and ThreadTerminalDrawer.tsx to use shared portStatusLabel for constructing terminal port status labels - Fix inspectWebPortCached in Manager.ts to only cache successful probe results, avoiding caching transient failures for the full TTL
1 parent da81a16 commit b1ad5fa

7 files changed

Lines changed: 51 additions & 55 deletions

File tree

apps/server/src/process/utils.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
export const MAX_PORT_NUMBER = 65_535;
1+
import { MAX_PORT_NUMBER, normalizeRunningPorts } from "@t3tools/shared/port";
22

3-
export function normalizeRunningPorts(ports: number[]): number[] {
4-
if (ports.length === 0) return [];
5-
return [...new Set(ports)]
6-
.filter((port) => Number.isInteger(port) && port > 0 && port <= MAX_PORT_NUMBER)
7-
.toSorted((left, right) => left - right);
8-
}
3+
export { MAX_PORT_NUMBER, normalizeRunningPorts };
94

105
export function parsePidList(stdout: string): number[] {
116
const pids: number[] = [];

apps/server/src/terminal/Layers/Manager.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -613,12 +613,17 @@ export const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWith
613613
return cached.isWeb;
614614
}
615615

616-
const isWeb = yield* webPortInspector(port).pipe(Effect.catch(() => Effect.succeed(false)));
617-
webPortProbeCache.set(port, {
618-
isWeb,
619-
checkedAt: Date.now(),
620-
});
621-
return isWeb;
616+
const isWeb = yield* webPortInspector(port).pipe(
617+
Effect.map((result) => ({ result, probeSucceeded: true }) as const),
618+
Effect.catch(() => Effect.succeed({ result: false, probeSucceeded: false } as const)),
619+
);
620+
if (isWeb.probeSucceeded) {
621+
webPortProbeCache.set(port, {
622+
isWeb: isWeb.result,
623+
checkedAt: Date.now(),
624+
});
625+
}
626+
return isWeb.result;
622627
});
623628

624629
const detectWebPorts = (runningPorts: number[]) =>

apps/web/src/components/Sidebar.tsx

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,8 @@ import { APP_STAGE_LABEL, APP_VERSION } from "../branding";
6161
import { isTerminalFocused } from "../lib/terminalFocus";
6262
import { isLinuxPlatform, isMacPlatform, newCommandId, newProjectId } from "../lib/utils";
6363
import { useStore } from "../store";
64-
import {
65-
normalizeRunningPorts,
66-
selectThreadTerminalState,
67-
useTerminalStateStore,
68-
} from "../terminalStateStore";
64+
import { normalizeRunningPorts, portStatusLabel } from "@t3tools/shared/port";
65+
import { selectThreadTerminalState, useTerminalStateStore } from "../terminalStateStore";
6966
import { useUiStateStore } from "../uiStateStore";
7067
import {
7168
resolveShortcutCommand,
@@ -219,15 +216,10 @@ function terminalStatusFromTerminalState(
219216
const runningPorts = normalizeRunningPorts(
220217
runningTerminalIds.flatMap((terminalId) => runningTerminalPorts[terminalId] ?? []),
221218
);
222-
const primaryWebPort = runningPorts[0] ?? null;
219+
const { label, primaryWebPort } = portStatusLabel(runningPorts);
223220

224221
return {
225-
label:
226-
primaryWebPort === null
227-
? "Terminal process running"
228-
: runningPorts.length === 1
229-
? `Open web server: http://localhost:${primaryWebPort}`
230-
: `Open web server: http://localhost:${primaryWebPort} (detected web ports: ${runningPorts.join(", ")})`,
222+
label,
231223
colorClass:
232224
primaryWebPort === null
233225
? "text-teal-600 dark:text-teal-300/90"

apps/web/src/components/ThreadTerminalDrawer.tsx

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,8 @@ import {
3333
type ThreadTerminalGroup,
3434
} from "../types";
3535
import { readNativeApi } from "~/nativeApi";
36-
import {
37-
normalizeRunningPorts,
38-
selectTerminalEventEntries,
39-
useTerminalStateStore,
40-
} from "../terminalStateStore";
36+
import { normalizeRunningPorts, portStatusLabel, type PortStatusLabel } from "@t3tools/shared/port";
37+
import { selectTerminalEventEntries, useTerminalStateStore } from "../terminalStateStore";
4138

4239
const MIN_DRAWER_HEIGHT = 180;
4340
const MAX_DRAWER_HEIGHT_RATIO = 0.75;
@@ -79,33 +76,17 @@ export function selectPendingTerminalEventEntries(
7976
return entries.filter((entry) => entry.id > lastAppliedTerminalEventId);
8077
}
8178

82-
interface TerminalRuntimeStatus {
83-
label: string;
84-
primaryWebPort: number | null;
85-
}
86-
8779
function terminalRuntimeStatus(
8880
terminalId: string,
8981
runningTerminalIds: Set<string>,
9082
runningTerminalPorts: Record<string, number[]>,
91-
): TerminalRuntimeStatus | null {
83+
): PortStatusLabel | null {
9284
if (!runningTerminalIds.has(terminalId)) {
9385
return null;
9486
}
9587

9688
const runningPorts = normalizeRunningPorts(runningTerminalPorts[terminalId]);
97-
const primaryWebPort = runningPorts[0] ?? null;
98-
const label =
99-
runningPorts.length === 0
100-
? "Terminal process running"
101-
: runningPorts.length === 1
102-
? `Open web server: http://localhost:${primaryWebPort}`
103-
: `Open web server: http://localhost:${primaryWebPort} (detected web ports: ${runningPorts.join(", ")})`;
104-
105-
return {
106-
label,
107-
primaryWebPort,
108-
};
89+
return portStatusLabel(runningPorts);
10990
}
11091

11192
function terminalThemeFromApp(): ITheme {

apps/web/src/terminalStateStore.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77

88
import { ThreadId, type TerminalEvent } from "@t3tools/contracts";
9+
import { normalizeRunningPorts } from "@t3tools/shared/port";
10+
export { normalizeRunningPorts } from "@t3tools/shared/port";
911
import { create } from "zustand";
1012
import { createJSONStorage, persist } from "zustand/middleware";
1113
import { resolveStorage } from "./lib/storage";
@@ -62,13 +64,6 @@ function normalizeRunningTerminalIds(
6264
.filter((id) => id.length > 0 && validTerminalIdSet.has(id));
6365
}
6466

65-
export function normalizeRunningPorts(ports: readonly number[] | undefined): number[] {
66-
if (!ports || ports.length === 0) return [];
67-
return [...new Set(ports)]
68-
.filter((port) => Number.isInteger(port) && port > 0 && port <= 65_535)
69-
.toSorted((left, right) => left - right);
70-
}
71-
7267
function normalizeRunningTerminalPorts(
7368
runningTerminalPorts: Record<string, number[]> | undefined,
7469
terminalIds: string[],

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+
"./port": {
56+
"types": "./src/port.ts",
57+
"import": "./src/port.ts"
5458
}
5559
},
5660
"scripts": {

packages/shared/src/port.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export const MAX_PORT_NUMBER = 65_535;
2+
3+
export function normalizeRunningPorts(ports: readonly number[] | undefined): number[] {
4+
if (!ports || ports.length === 0) return [];
5+
return [...new Set(ports)]
6+
.filter((port) => Number.isInteger(port) && port > 0 && port <= MAX_PORT_NUMBER)
7+
.toSorted((left, right) => left - right);
8+
}
9+
10+
export interface PortStatusLabel {
11+
label: string;
12+
primaryWebPort: number | null;
13+
}
14+
15+
export function portStatusLabel(runningPorts: readonly number[]): PortStatusLabel {
16+
const primaryWebPort = runningPorts[0] ?? null;
17+
const label =
18+
runningPorts.length === 0
19+
? "Terminal process running"
20+
: runningPorts.length === 1
21+
? `Open web server: http://localhost:${primaryWebPort}`
22+
: `Open web server: http://localhost:${primaryWebPort} (detected web ports: ${runningPorts.join(", ")})`;
23+
return { label, primaryWebPort };
24+
}

0 commit comments

Comments
 (0)