| 1 | import { NETWORK_BLOCKING_HINT } from "./network.ts"; |
| 2 | |
| 3 | const escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); |
| 4 | |
| 5 | const 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 | */ |
| 26 | export 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 | */ |
| 44 | export function sanitizeThreadErrorMessage(message: string | null | undefined): string | null { |
| 45 | return isTransportConnectionErrorMessage(message) ? null : (message ?? null); |
| 46 | } |