(url: string)
| 364 | } |
| 365 | |
| 366 | async function fetchBuffer(url: string): Promise<Buffer | null> { |
| 367 | try { |
| 368 | const res = await safeFetch(url, { |
| 369 | signal: AbortSignal.timeout(10000), |
| 370 | headers: { "User-Agent": "HyperFrames/1.0" }, |
| 371 | }); |
| 372 | if (!res || !res.ok) return null; |
| 373 | // Reject XML/HTML error pages disguised as 200 OK (common with S3/CloudFront) |
| 374 | const ct = res.headers.get("content-type") || ""; |
| 375 | if (ct.includes("text/xml") || ct.includes("text/html") || ct.includes("application/xml")) { |
| 376 | return null; |
| 377 | } |
| 378 | const ab = await res.arrayBuffer(); |
| 379 | return Buffer.from(ab); |
| 380 | } catch { |
| 381 | return null; |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | function slugify(text: string): string { |
| 386 | return text |
no test coverage detected