()
| 18 | // --------------------------------------------------------------------------- |
| 19 | |
| 20 | function makeMemFs(): { |
| 21 | store: Map<string, string>; |
| 22 | fs: AgentFs; |
| 23 | mkdirCalls: string[]; |
| 24 | writeCalls: string[]; |
| 25 | seedFile: (p: string, content: string) => void; |
| 26 | seedDir: (p: string) => void; |
| 27 | seedSymlink: (p: string) => void; |
| 28 | } { |
| 29 | const store = new Map<string, string>(); // regular files: path -> content |
| 30 | const dirs = new Set<string>(); // directories |
| 31 | const symlinks = new Set<string>(); // symlinks (we only need to know it IS one) |
| 32 | const mkdirCalls: string[] = []; |
| 33 | const writeCalls: string[] = []; |
| 34 | |
| 35 | // Record `p` and all of its ancestors as directories, modelling a real fs |
| 36 | // tree so the per-component lstat walk in inspectTargetPath can traverse. |
| 37 | const addAncestorDirs = (p: string) => { |
| 38 | let cur = path.dirname(p); |
| 39 | while (cur !== path.dirname(cur)) { |
| 40 | dirs.add(cur); |
| 41 | cur = path.dirname(cur); |
| 42 | } |
| 43 | dirs.add(cur); // filesystem root |
| 44 | }; |
| 45 | |
| 46 | const agentFs: AgentFs = { |
| 47 | async lstat(p: string) { |
| 48 | if (symlinks.has(p)) return { isFile: false, isSymbolicLink: true }; |
| 49 | if (store.has(p)) return { isFile: true, isSymbolicLink: false }; |
| 50 | if (dirs.has(p)) return { isFile: false, isSymbolicLink: false }; |
| 51 | return null; |
| 52 | }, |
| 53 | async readFile(p: string) { |
| 54 | const v = store.get(p); |
| 55 | if (v === undefined) throw Object.assign(new Error(`ENOENT: ${p}`), { code: 'ENOENT' }); |
| 56 | return v; |
| 57 | }, |
| 58 | async writeFile(p: string, data: string, opts?: { exclusive?: boolean }) { |
| 59 | if (opts?.exclusive && (store.has(p) || dirs.has(p) || symlinks.has(p))) { |
| 60 | throw Object.assign(new Error(`EEXIST: ${p}`), { code: 'EEXIST' }); |
| 61 | } |
| 62 | writeCalls.push(p); |
| 63 | store.set(p, data); |
| 64 | addAncestorDirs(p); |
| 65 | }, |
| 66 | async mkdir(p: string) { |
| 67 | mkdirCalls.push(p); |
| 68 | dirs.add(p); |
| 69 | addAncestorDirs(p); |
| 70 | }, |
| 71 | }; |
| 72 | |
| 73 | return { |
| 74 | store, |
| 75 | fs: agentFs, |
| 76 | mkdirCalls, |
| 77 | writeCalls, |
no test coverage detected