Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 40 additions & 13 deletions scripts/check-live-surfaces.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,47 @@ function check(name, condition, detail = '') {
}

const cache = new Map();
// A network failure is a failed check, not a crash: status 0 with the error in the body.
async function fetchText(url) {
if (cache.has(url)) return cache.get(url);
const res = await fetch(url, { redirect: 'manual', headers: { 'user-agent': 'next-docs-live-check/1' } });
const body = await res.text();
const out = { status: res.status, headers: res.headers, body, bytes: Buffer.byteLength(body, 'utf8') };
let out;
try {
const res = await fetch(url, {
redirect: 'manual',
headers: { 'user-agent': 'next-docs-live-check/1' },
signal: AbortSignal.timeout(20_000),
});
const body = await res.text();
out = { status: res.status, headers: res.headers, body, bytes: Buffer.byteLength(body, 'utf8') };
} catch (error) {
out = { status: 0, headers: new Headers(), body: '', bytes: 0, error: error?.cause?.code ?? error?.name ?? String(error) };
}
cache.set(url, out);
return out;
}

// Bounded concurrency so ~200 link checks do not open ~200 connections at once.
// Side effects only; callers record results themselves. An exception from one
// item is recorded as a failed check and does not stop the other workers.
async function forEachLimit(items, limit, fn) {
let next = 0;
await Promise.all(
Array.from({ length: Math.min(limit, items.length) }, async () => {
while (next < items.length) {
const item = items[next++];
try {
await fn(item);
} catch (error) {
failures.push(`check for ${String(item)} threw: ${error?.message ?? error}`);
}
}
}),
);
}

async function page(url) {
const r = await fetchText(url);
check(`200 ${url}`, r.status === 200, `status ${r.status}`);
check(`200 ${url}`, r.status === 200, `status ${r.status}${r.error ? ` (${r.error})` : ''}`);
return r;
}

Expand Down Expand Up @@ -90,15 +119,13 @@ if (capabilityMap) {
for (const w of c.webhooks) if (w.url) linked.add(w.url);
}
let broken = 0;
await Promise.all(
[...linked].map(async (u) => {
const r = await fetchText(u);
if (r.status !== 200) {
broken += 1;
failures.push(`map link ${u} returned ${r.status}`);
}
}),
);
await forEachLimit([...linked], 8, async (u) => {
const r = await fetchText(u);
if (r.status !== 200) {
broken += 1;
failures.push(`map link ${u} returned ${r.status}${r.error ? ` (${r.error})` : ''}`);
}
});
check(`all ${linked.size} capability-map links resolve`, broken === 0);

for (const b of capabilityMap.bundles) {
Expand Down