import fs from 'node:fs' import { listAppConfigEntries, upsertAppConfigEntry } from '../../repositories/app-config-repo.js' import { readJsonFile } from '../../utils/json-file-store.js' type NormalizeJsonValue = (value: unknown) => T const configCache = new Map() let databaseCacheLoaded = false export function hasAppConfigEntry(configKey: string, legacyFilePath = '') { const key = normalizeConfigKey(configKey) if (configCache.has(key)) { return true } return Boolean(legacyFilePath) && fs.existsSync(legacyFilePath) } export function readAppConfigEntry({ configKey, legacyFilePath, fallback, normalize, }: { configKey: string legacyFilePath?: string | undefined fallback: T | (() => T) normalize: NormalizeJsonValue }): T { const key = normalizeConfigKey(configKey) if (configCache.has(key)) { return normalize(cloneJsonValue(configCache.get(key))) } if (!databaseCacheLoaded && legacyFilePath) { return readJsonFile(legacyFilePath, fallback, normalize) } return normalize(resolveFallback(fallback)) } export async function saveAppConfigEntry({ configKey, value, legacyFilePath, normalize, }: { configKey: string value: unknown legacyFilePath?: string | undefined normalize: NormalizeJsonValue }): Promise { const key = normalizeConfigKey(configKey) const normalized = normalize(value) await upsertAppConfigEntry({ configKey: key, configJson: normalized, sourceFilePath: legacyFilePath, migratedFromFile: false, timestamp: new Date().toISOString(), }) configCache.set(key, cloneJsonValue(normalized)) return normalized } export async function loadAppConfigCacheFromDatabase() { const rows = await listAppConfigEntries() configCache.clear() for (const row of rows) { const key = normalizeConfigKey(row.config_key) if (!key) { continue } configCache.set(key, cloneJsonValue(row.config_json)) } databaseCacheLoaded = true } export function setAppConfigCacheEntry(configKey: string, value: unknown) { const key = normalizeConfigKey(configKey) if (!key) { return } configCache.set(key, cloneJsonValue(value)) } function normalizeConfigKey(value: unknown) { return String(value || '').trim() } function resolveFallback(fallback: T | (() => T)): T { return typeof fallback === 'function' ? (fallback as () => T)() : fallback } function cloneJsonValue(value: T): T { return JSON.parse(JSON.stringify(value ?? null)) as T }