1import { NETWORK_BLOCKING_HINT } from "./network.ts";
2
3const escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4
5const TRANSPORT_ERROR_PATTERNS = [
6 /\bSocketCloseError\b/i,
7 /\bSocketOpenError\b/i,
8 /\bSocket is not connected\b/i,
9 /Unable to connect to the T3 server WebSocket\./i,
10 // The RPC session appends the network hint for relay connections. Any other
11 // trailing text means a different error that the user should still see.
12 new RegExp(
13 `\\b(?:is not connected|disconnected|stopped responding|could not establish a WebSocket connection)\\.(?: ${escapeRegExp(NETWORK_BLOCKING_HINT)})?$`,
14 "i",
15 ),
16 /\bClientProtocolError\b/i,
17 /\bRpcClientError\b/i,
18 /\bping timeout\b/i,
19] as const;
20
21/**
22 * Check whether an error message originates from a transport-level connection
23 * failure (socket close, socket open, ping timeout, etc.) rather than a
24 * business-logic error.
25 */
26export function isTransportConnectionErrorMessage(message: string | null | undefined): boolean {
27 if (typeof message !== "string") {
28 return false;
29 }
30
31 const normalizedMessage = message.trim();
32 if (normalizedMessage.length === 0) {
33 return false;
34 }
35
36 return TRANSPORT_ERROR_PATTERNS.some((pattern) => pattern.test(normalizedMessage));
37}
38
39/**
40 * Strip transport connection errors from user-facing error messages.
41 * Returns `null` for transport errors so the UI can distinguish between
42 * real errors and transient connection issues.
43 */
44export function sanitizeThreadErrorMessage(message: string | null | undefined): string | null {
45 return isTransportConnectionErrorMessage(message) ? null : (message ?? null);
46}