| 62 | } |
| 63 | |
| 64 | async function walkFiles(root: string, rel = ""): Promise<string[]> { |
| 65 | const results: string[] = []; |
| 66 | let entries; |
| 67 | try { |
| 68 | entries = await fs.readdir(path.join(root, rel), { withFileTypes: true }); |
| 69 | } catch { |
| 70 | return results; |
| 71 | } |
| 72 | for (const e of entries) { |
| 73 | const child = rel ? `${rel}/${e.name}` : e.name; |
| 74 | if (e.isDirectory()) { |
| 75 | results.push(...(await walkFiles(root, child))); |
| 76 | } else { |
| 77 | results.push(child); |
| 78 | } |
| 79 | } |
| 80 | return results; |
| 81 | } |
| 82 | |
| 83 | /** Safely resolve a user-supplied relative path under SRC_ROOT (blocks path traversal). */ |
| 84 | function safePath(relPath: string): string | null { |