Something went wrong. Try again.
Identities for entities did.bot
agent llm did
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139// The built tree is served by CloudFront in front of a private S3 bucket,// which answers by key alone — so what makes "/features/" a page is// infra/site/viewer-request.js, not the bucket. This test runs that exact// file (read from infra/, never a copy of it) over the addresses the built// pages actually link to, and checks each one lands on a file that exists.//// It is here rather than in infra/ because the thing being checked is a pair:// astro.config.mjs's `trailingSlash` and `build.format` decide what the tree// looks like, and the function decides what a request for it resolves to.// Changing either alone is what breaks a live site while every other test// still passes.import { readFileSync, statSync } from "node:fs";import { join } from "node:path";import { createContext, runInContext } from "node:vm";import { distDir, repoRoot, requireDist, walkHtmlFiles, loadDocument, fail, report,} from "./lib.mjs";
requireDist();
// The deployed source, evaluated as CloudFront evaluates it: a bare script// declaring `handler`, with no module wrapper of its own.const functionPath = join(repoRoot, "infra", "site", "viewer-request.js");const context = createContext({});runInContext(readFileSync(functionPath, "utf8"), context, { filename: functionPath });const handler = runInContext("handler", context);
function exists(path) { try { return statSync(path).isFile(); } catch { return false; }}
// One request through the function. Returns either the object key it rewrote// to, or the address it redirected to.function serve(uri) { const result = handler({ request: { uri, querystring: {} } }); if (result.statusCode) { return { redirect: result.headers.location.value, status: result.statusCode }; } // The function hands "/" straight back; the distribution's // default_root_object is what turns it into the home page. return { key: result.uri === "/" ? "/index.html" : result.uri };}
// What a reader gets for one address: at most one redirect, then a key. More// than one hop is a loop waiting to happen, so a second redirect is a failure// rather than something to follow.function resolveAddress(uri) { let hop = serve(uri); if (hop.redirect) { const second = serve(hop.redirect); if (second.redirect) { return { error: `${uri} redirects twice (${hop.redirect}, then ${second.redirect})` }; } return { key: second.key, via: hop.redirect }; } return { key: hop.key };}
function checkAddress(uri, source) { const resolved = resolveAddress(uri); if (resolved.error) { fail(`${source}: ${resolved.error}`); return; } if (!exists(join(distDir, resolved.key))) { fail(`${source}: ${uri} resolves to ${resolved.key}, which is not in the built tree`); } return resolved;}
// Every page in the tree, asked for at its own address. `dist/features/// index.html` is the page at `/features/`, and the distribution's// default_root_object covers `/` itself.const pages = walkHtmlFiles().filter((path) => !path.startsWith(`${distDir}/api/`));for (const page of pages) { const relative = page.slice(distDir.length + 1); if (relative === "index.html") continue; if (!relative.endsWith("/index.html")) { // 404.html is reached by the distribution's custom_error_response, which // names the key directly rather than going through this function. continue; } const address = `/${relative.slice(0, -"index.html".length)}`; const resolved = checkAddress(address, "built page"); if (resolved && resolved.via) { fail(`built page: ${address} should be served, not redirected to ${resolved.via}`); }
// The same page without its trailing slash is the address a hand-typed or // pasted link takes; it has to arrive at the canonical one. const withoutSlash = address.slice(0, -1); const bare = serve(withoutSlash); if (bare.redirect !== address || bare.status !== 301) { fail(`built page: ${withoutSlash} should 301 to ${address}, got ${JSON.stringify(bare)}`); }}
// Every internal address the pages link to, and every asset they pull in.// The links are the site's own claims about what it serves; an asset that// the function rewrote to a directory index would be a stylesheet arriving// as HTML.for (const page of pages) { const document = loadDocument(page); const source = page.slice(distDir.length + 1);
for (const a of document.querySelectorAll("a[href]")) { const href = a.getAttribute("href"); if (!href || !href.startsWith("/")) continue; checkAddress(href.split("#")[0].split("?")[0], source); }
for (const el of document.querySelectorAll("img[src], script[src], link[href]")) { const attr = el.tagName === "LINK" ? "href" : "src"; const value = el.getAttribute(attr); if (!value || !value.startsWith("/")) continue; const resolved = checkAddress(value, source); if (resolved && resolved.via) { fail(`${source}: asset ${value} is being redirected to ${resolved.via}`); } }}
// The rustdoc mount, whose own pages are named files and pass through// untouched — the directory index is the one thing about it this function// decides.checkAddress("/api/", "the /api/ mount");
report("check-serving");