1+ import { release } from 'node:os'
12import { createLogger } from '@sim/logger'
23import type { BrowserWindow } from 'electron'
34import { type EventRecorder , scrubUrl } from '@/main/observability'
@@ -6,6 +7,15 @@ const logger = createLogger('DesktopLoadHealth')
67
78const AUTO_RETRY_INTERVAL_MS = 5000
89const LOAD_WATCHDOG_MS = 30_000
10+ /** Darwin 24 is macOS 15, the first release that gates apps behind Local Network privacy. */
11+ const FIRST_LOCAL_NETWORK_PRIVACY_DARWIN = 24
12+
13+ function hasLocalNetworkPrivacy ( ) : boolean {
14+ return (
15+ process . platform === 'darwin' &&
16+ Number . parseInt ( release ( ) , 10 ) >= FIRST_LOCAL_NETWORK_PRIVACY_DARWIN
17+ )
18+ }
919
1020export type LoadErrorKind =
1121 | 'offline'
@@ -22,7 +32,7 @@ export type LoadErrorKind =
2232 */
2333export function classifyLoadError (
2434 errorCode : number ,
25- platform : NodeJS . Platform = process . platform
35+ localNetworkPrivacy : boolean = hasLocalNetworkPrivacy ( )
2636) : LoadErrorKind {
2737 // Only ERR_ABORTED (-3) and success (0) are ignored. ERR_FAILED (-2) and
2838 // ERR_IO_PENDING (-1) are real failures that must surface the offline page.
@@ -44,7 +54,7 @@ export function classifyLoadError(
4454 // macOS Local Network privacy fails the socket with EHOSTUNREACH, which
4555 // Chromium reports as ERR_ADDRESS_UNREACHABLE (-109), so a self-hosted server
4656 // on a private address looks like a routing failure until Sim is allowed.
47- if ( errorCode === - 109 && platform === 'darwin' ) {
57+ if ( errorCode === - 109 && localNetworkPrivacy ) {
4858 return 'local_network'
4959 }
5060 return 'unreachable'
0 commit comments