()
| 1050 | } |
| 1051 | |
| 1052 | export function getGlobalConfig(): GlobalConfig { |
| 1053 | if (process.env.NODE_ENV === 'test') { |
| 1054 | return TEST_GLOBAL_CONFIG_FOR_TESTING |
| 1055 | } |
| 1056 | |
| 1057 | // Fast path: pure memory read. After startup, this always hits — our own |
| 1058 | // writes go write-through and other instances' writes are picked up by the |
| 1059 | // background freshness watcher (never blocks this path). |
| 1060 | if (globalConfigCache.config) { |
| 1061 | configCacheHits++ |
| 1062 | return globalConfigCache.config |
| 1063 | } |
| 1064 | |
| 1065 | // Slow path: startup load. Sync I/O here is acceptable because it runs |
| 1066 | // exactly once, before any UI is rendered. Stat before read so any race |
| 1067 | // self-corrects (old mtime + new content → watcher re-reads next tick). |
| 1068 | configCacheMisses++ |
| 1069 | try { |
| 1070 | let stats: { mtimeMs: number; size: number } | null = null |
| 1071 | try { |
| 1072 | stats = getFsImplementation().statSync(getGlobalClaudeFile()) |
| 1073 | } catch { |
| 1074 | // File doesn't exist |
| 1075 | } |
| 1076 | const config = migrateConfigFields( |
| 1077 | getConfig(getGlobalClaudeFile(), createDefaultGlobalConfig), |
| 1078 | ) |
| 1079 | globalConfigCache = { |
| 1080 | config, |
| 1081 | mtime: stats?.mtimeMs ?? Date.now(), |
| 1082 | } |
| 1083 | lastReadFileStats = stats |
| 1084 | ? { mtime: stats.mtimeMs, size: stats.size } |
| 1085 | : null |
| 1086 | startGlobalConfigFreshnessWatcher() |
| 1087 | return config |
| 1088 | } catch { |
| 1089 | // If anything goes wrong, fall back to uncached behavior |
| 1090 | return migrateConfigFields( |
| 1091 | getConfig(getGlobalClaudeFile(), createDefaultGlobalConfig), |
| 1092 | ) |
| 1093 | } |
| 1094 | } |
| 1095 | |
| 1096 | /** |
| 1097 | * Returns the effective value of remoteControlAtStartup. Precedence: |
no test coverage detected