πŸ“„ File detail

services/remoteManagedSettings/syncCacheState.ts

🧩 .tsπŸ“ 97 linesπŸ’Ύ 4,004 bytesπŸ“ text
← Back to All Files

🎯 Use case

This file lives under β€œservices/”, which covers long-lived services (LSP, MCP, OAuth, tool execution, memory, compaction, voice, settings sync, …). On the API surface it exposes setSessionCache, resetSyncCache, setEligibility, getSettingsPath, and getRemoteManagedSettingsSyncFromCache β€” mainly functions, hooks, or classes. Dependencies touch Node path helpers. It composes internal code from utils (relative imports). What the file header says: Leaf state module for the remote-managed-settings sync cache. Split from syncCache.ts to break the settings.ts β†’ syncCache.ts β†’ auth.ts β†’ settings.ts cycle. auth.ts sits inside the large settings SCC; importing it from settings.ts's own dependency chain pulls hundreds of modules.

Generated from folder role, exports, dependency roots, and inline comments β€” not hand-reviewed for every path.

🧠 Inline summary

Leaf state module for the remote-managed-settings sync cache. Split from syncCache.ts to break the settings.ts β†’ syncCache.ts β†’ auth.ts β†’ settings.ts cycle. auth.ts sits inside the large settings SCC; importing it from settings.ts's own dependency chain pulls hundreds of modules into the eagerly-evaluated SCC at startup. This module imports only leaves (path, envUtils, file, json, types, settings/settingsCache β€” also a leaf, only type-imports validation). settings.ts reads the cache from here. syncCache.ts keeps isRemoteManagedSettingsEligible (the auth-touching part) and re-exports everything from here for callers that don't care about the cycle. Eligibility is a tri-state here: undefined (not yet determined β€” return null), false (ineligible β€” return null), true (proceed). managedEnv.ts calls isRemoteManagedSettingsEligible() just before the policySettings read β€” after userSettings/flagSettings env vars are applied, so the check sees config-provided CLAUDE_CODE_USE_BEDROCK/ANTHROPIC_BASE_URL. That call computes once and mirrors the result here via setEligibility(). Every subsequent read hits the cached bool instead of re-running the auth chain.

πŸ“€ Exports (heuristic)

  • setSessionCache
  • resetSyncCache
  • setEligibility
  • getSettingsPath
  • getRemoteManagedSettingsSyncFromCache

πŸ“š External import roots

Package roots from from "…" (relative paths omitted).

  • path

πŸ–₯️ Source preview

/**
 * Leaf state module for the remote-managed-settings sync cache.
 *
 * Split from syncCache.ts to break the settings.ts β†’ syncCache.ts β†’ auth.ts β†’
 * settings.ts cycle. auth.ts sits inside the large settings SCC; importing it
 * from settings.ts's own dependency chain pulls hundreds of modules into the
 * eagerly-evaluated SCC at startup.
 *
 * This module imports only leaves (path, envUtils, file, json, types,
 * settings/settingsCache β€” also a leaf, only type-imports validation). settings.ts
 * reads the cache from here. syncCache.ts keeps isRemoteManagedSettingsEligible
 * (the auth-touching part) and re-exports everything from here for callers that
 * don't care about the cycle.
 *
 * Eligibility is a tri-state here: undefined (not yet determined β€” return
 * null), false (ineligible β€” return null), true (proceed). managedEnv.ts
 * calls isRemoteManagedSettingsEligible() just before the policySettings
 * read β€” after userSettings/flagSettings env vars are applied, so the check
 * sees config-provided CLAUDE_CODE_USE_BEDROCK/ANTHROPIC_BASE_URL. That call
 * computes once and mirrors the result here via setEligibility(). Every
 * subsequent read hits the cached bool instead of re-running the auth chain.
 */

import { join } from 'path'
import { getClaudeConfigHomeDir } from '../../utils/envUtils.js'
import { readFileSync } from '../../utils/fileRead.js'
import { stripBOM } from '../../utils/jsonRead.js'
import { resetSettingsCache } from '../../utils/settings/settingsCache.js'
import type { SettingsJson } from '../../utils/settings/types.js'
import { jsonParse } from '../../utils/slowOperations.js'

const SETTINGS_FILENAME = 'remote-settings.json'

let sessionCache: SettingsJson | null = null
let eligible: boolean | undefined

export function setSessionCache(value: SettingsJson | null): void {
  sessionCache = value
}

export function resetSyncCache(): void {
  sessionCache = null
  eligible = undefined
}

export function setEligibility(v: boolean): boolean {
  eligible = v
  return v
}

export function getSettingsPath(): string {
  return join(getClaudeConfigHomeDir(), SETTINGS_FILENAME)
}

// sync IO β€” settings pipeline is sync. fileRead and jsonRead are leaves;
// file.ts and json.ts both sit in the settings SCC.
function loadSettings(): SettingsJson | null {
  try {
    const content = readFileSync(getSettingsPath())
    const data: unknown = jsonParse(stripBOM(content))
    if (!data || typeof data !== 'object' || Array.isArray(data)) {
      return null
    }
    return data as SettingsJson
  } catch {
    return null
  }
}

export function getRemoteManagedSettingsSyncFromCache(): SettingsJson | null {
  if (eligible !== true) return null
  if (sessionCache) return sessionCache
  const cachedSettings = loadSettings()
  if (cachedSettings) {
    sessionCache = cachedSettings
    // Remote settings just became available for the first time. Any merged
    // getSettings_DEPRECATED() result cached before this moment is missing
    // the policySettings layer (the `eligible !== true` guard above returned
    // null). Flush so the next merged read re-merges with this layer visible.
    //
    // Fires at most once: subsequent calls hit `if (sessionCache)` above.
    // When called from loadSettingsFromDisk() (settings.ts:546), the merged
    // cache is still null (setSessionSettingsCache runs at :732 after
    // loadSettingsFromDisk returns) β€” no-op. The async-fetch arm (index.ts
    // setSessionCache + notifyChange) already handles its own reset.
    //
    // gh-23085: isBridgeEnabled() at main.tsx Commander-definition time
    // (before preAction β†’ init() β†’ isRemoteManagedSettingsEligible()) reached
    // getSettings_DEPRECATED() at auth.ts:115. The try/catch in bridgeEnabled
    // swallowed the later getGlobalConfig() throw, but the merged settings
    // cache was already poisoned. See managedSettingsHeadless.int.test.ts.
    resetSettingsCache()
    return cachedSettings
  }
  return null
}