// 檔案系統與時鐘由外部注入,核心不直接碰真實環境 export interface Fs { read(path: string): Promise /** 原子寫入:先寫暫存檔再改名 */ write(path: string, data: string): Promise } export type Clock = () => number export function memoryFs(): Fs & { files: Map } { const files = new Map() return { files, async read(p) { return files.get(p) ?? null }, async write(p, d) { files.set(p, d) }, } } export async function readJson(fs: Fs, path: string, fallback: T): Promise { const raw = await fs.read(path) if (raw === null) return fallback try { return JSON.parse(raw) as T } catch { return fallback } } export const writeJson = (fs: Fs, path: string, value: unknown) => fs.write(path, JSON.stringify(value, null, 2)) export async function nodeFs(): Promise { const { readFile, writeFile, rename, mkdir } = await import('node:fs/promises') const { dirname } = await import('node:path') return { async read(p) { try { return await readFile(p, 'utf8') } catch { return null } }, async write(p, d) { await mkdir(dirname(p), { recursive: true }) const tmp = `${p}.${process.pid}.tmp` await writeFile(tmp, d) await rename(tmp, p) }, } }