(urls: string[])
| 37 | * Fetch cross-origin stylesheets that the browser couldn't read due to CORS. |
| 38 | */ |
| 39 | export async function fetchCrossOriginCSS(urls: string[]): Promise<string[]> { |
| 40 | const results: string[] = []; |
| 41 | const uniqueUrls = [...new Set(urls)]; |
| 42 | |
| 43 | for (const url of uniqueUrls) { |
| 44 | try { |
| 45 | const safety = await validateUrlSafety(url); |
| 46 | if (!safety.valid) continue; |
| 47 | |
| 48 | const controller = new AbortController(); |
| 49 | const timeout = setTimeout(() => controller.abort(), 5000); |
| 50 | |
| 51 | const res = await fetch(url, { |
| 52 | signal: controller.signal, |
| 53 | redirect: 'manual', |
| 54 | headers: { |
| 55 | 'User-Agent': 'Mozilla/5.0 (compatible; DesignClaw/1.0)', |
| 56 | }, |
| 57 | }); |
| 58 | |
| 59 | clearTimeout(timeout); |
| 60 | |
| 61 | if (res.ok) { |
| 62 | const contentType = res.headers.get('content-type')?.toLowerCase() || ''; |
| 63 | if (contentType && !contentType.includes('text/css')) continue; |
| 64 | |
| 65 | const text = await res.text(); |
| 66 | results.push(text); |
| 67 | } |
| 68 | } catch { |
| 69 | // Skip unreachable sheets |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | return results; |
| 74 | } |
no test coverage detected