| 153 | |
| 154 | /** Recursively list files under `dir`, capped by size and age. */ |
| 155 | const collectFiles = (dir: string, prefix: string): ZipEntry[] => { |
| 156 | // oxlint-disable-next-line executor/no-try-catch-or-throw -- boundary: fs probing of optional directories (crash dumps may not exist) |
| 157 | try { |
| 158 | const cutoff = Date.now() - EXPORT_MAX_AGE_MS; |
| 159 | return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => { |
| 160 | const full = join(dir, entry.name); |
| 161 | if (entry.isDirectory()) return collectFiles(full, `${prefix}/${entry.name}`); |
| 162 | if (!entry.isFile()) return []; |
| 163 | const info = statSync(full); |
| 164 | if (info.size > MAX_EXPORT_FILE_BYTES) return []; |
| 165 | if (info.mtimeMs < cutoff) return []; |
| 166 | return [{ name: `${prefix}/${entry.name}`, path: full }]; |
| 167 | }); |
| 168 | } catch { |
| 169 | return []; |
| 170 | } |
| 171 | }; |
| 172 | |
| 173 | const exportStamp = () => |
| 174 | new Date() |