Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit dd71de60bd3faad0bb2ed2ebfb7e8c1bd4fa894c
parent 3b2ae052365d78e072708c05938bb706e09fa5f7
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Mon, 28 Sep 2026 14:22:47 -0400

homepage: E2E_EXPECT_SOURCE makes the /source specs fail on the empty state; unit tests for the loader and _headers

Review L9/L10: with E2E_EXPECT_SOURCE=1 (declared in playwright.config.ts and
the env registry) the four data tests of source.spec.ts fail instead of
accepting the empty state. app/lib/headers.test.ts pins _headers with a replica
of wrangler's parse-and-attach: every /source/tree override detaches
Content-Type before setting it, each path gets ONE value, and the rule count
and line length sit inside wrangler's limits. app/lib/source.test.ts: a
malformed, wrong-version or orphaned manifest is the empty state, never a
throw (`loadSourceManifest` takes the public dir as a seam).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
MENVIRONMENT.md | 1+
Mcommon/lib/envVars.ts | 1+
Ahomepage/app/lib/headers.test.ts | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahomepage/app/lib/source.test.ts | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mhomepage/app/lib/source.ts | 10+++++++---
Mhomepage/e2e/source.spec.ts | 14++++++++++++--
Mhomepage/playwright.config.ts | 4++++
7 files changed, 199 insertions(+), 5 deletions(-)

diff --git a/ENVIRONMENT.md b/ENVIRONMENT.md @@ -186,4 +186,5 @@ Read only by a test harness, a fake binary or a test-mode branch. Never set one | `E2E_RETRIES` | `0` | Retries per shard (`--retries N` wins); 0 keeps a sharded run comparable to a serial one. | scripts/run-sharded-e2e.mjs | | `E2E_IMAGE` | `yt-dlp-transcript-browser-e2e` | The sharded e2e run's image tag. | scripts/run-sharded-e2e.mjs | | `E2E_SKIP_BUILD` | off | `1` reuses the sharded e2e image instead of rebuilding it (`--no-build`). | scripts/run-sharded-e2e.mjs | +| `E2E_EXPECT_SOURCE` | off (both states pass) | `1` makes the homepage suite's `/source/` specs fail on the empty state; a gate that ran `archilyzer source publish` first sets it. | homepage/e2e/source.spec.ts | | `E2E_HOMEPAGE_SUMMARY_FILE` | `homepage/public/homepage-summary.json` | The synthetic summary the homepage's e2e dev server reads; set by `homepage/playwright.config.ts`, ignored by a production build. | homepage/app/lib/summary.ts | diff --git a/common/lib/envVars.ts b/common/lib/envVars.ts @@ -185,6 +185,7 @@ const DECLARED: EnvVarDecl[] = [ { name: "E2E_RETRIES", audience: "test", default: "`0`", readBy: "scripts/run-sharded-e2e.mjs", doc: "Retries per shard (`--retries N` wins); 0 keeps a sharded run comparable to a serial one." }, { name: "E2E_IMAGE", audience: "test", default: "`yt-dlp-transcript-browser-e2e`", readBy: "scripts/run-sharded-e2e.mjs", doc: "The sharded e2e run's image tag." }, { name: "E2E_SKIP_BUILD", audience: "test", default: "off", readBy: "scripts/run-sharded-e2e.mjs", doc: "`1` reuses the sharded e2e image instead of rebuilding it (`--no-build`)." }, + { name: "E2E_EXPECT_SOURCE", audience: "test", default: "off (both states pass)", readBy: "homepage/e2e/source.spec.ts", doc: "`1` makes the homepage suite's `/source/` specs fail on the empty state; a gate that ran `archilyzer source publish` first sets it." }, { name: "E2E_HOMEPAGE_SUMMARY_FILE", audience: "test", default: "`homepage/public/homepage-summary.json`", readBy: "homepage/app/lib/summary.ts", doc: "The synthetic summary the homepage's e2e dev server reads; set by `homepage/playwright.config.ts`, ignored by a production build." }, ]; diff --git a/homepage/app/lib/headers.test.ts b/homepage/app/lib/headers.test.ts @@ -0,0 +1,103 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// Run with: +// pnpm --filter homepage test +// +// public/_headers is what keeps the raw source tree from being served as +// code on this origin, and nothing but the Pages edge applies it. This pins +// its security property with a small replica of wrangler's `_headers` +// handling (pages-shared parseHeaders + generateRulesMatcher + attachHeaders, +// read in wrangler 4.88 and re-checked by the review in 4.142): every +// matching rule applies in file order, `! Name` detaches a header, and a +// header a LATER rule sets again is APPENDED ("a, b"), not replaced. + +const FILE = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "public", "_headers"); +const TEXT = readFileSync(FILE, "utf8"); + +type Rule = { path: string; set: Record<string, string>; unset: string[]; re: RegExp; lines: string[] }; + +function parse(text: string): Rule[] { + const rules: Rule[] = []; + let rule: Rule | undefined; + for (const raw of text.split("\n")) { + const line = raw.trim(); + if (!line || line.startsWith("#")) continue; + if (/^([^\s]+:\/\/|^\/)/.test(line)) { + if (rule) rules.push(rule); + const esc = (s: string) => s.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); + rule = { path: line, set: {}, unset: [], re: new RegExp(`^${line.split("*").map(esc).join("(?<splat>.*)")}$`), lines: [] }; + continue; + } + rule!.lines.push(line); + if (line.startsWith("! ")) { + rule!.unset.push(line.slice(2).toLowerCase()); + continue; + } + const i = line.indexOf(":"); + const name = line.slice(0, i).trim().toLowerCase(); + const value = line.slice(i + 1).trim(); + rule!.set[name] = rule!.set[name] ? `${rule!.set[name]}, ${value}` : value; + } + if (rule) rules.push(rule); + return rules; +} + +function served(rules: Rule[], pathname: string, contentType: string): Headers { + const h = new Headers({ "content-type": contentType }); + const setOnce = new Set<string>(); + for (const r of rules.filter((x) => x.re.test(pathname))) { + for (const k of r.unset) h.delete(k); + for (const [k, v] of Object.entries(r.set)) { + if (setOnce.has(k)) h.append(k, v); + else { + h.set(k, v); + setOnce.add(k); + } + } + } + return h; +} + +const RULES = parse(TEXT); + +test("wrangler's limits: at most 100 rules, no line over 2,000 characters, one splat a rule", () => { + assert.ok(RULES.length <= 100, `${RULES.length} rules`); + for (const line of TEXT.split("\n")) assert.ok(line.length <= 2000, line.slice(0, 60)); + for (const r of RULES) assert.ok((r.path.match(/\*/g) ?? []).length <= 1, r.path); +}); + +test("every /source/tree override detaches Content-Type before it sets its own", () => { + const overrides = RULES.filter((r) => r.path.startsWith("/source/tree/") && r.path !== "/source/tree/*" && "content-type" in r.set); + assert.ok(overrides.length >= 7, overrides.map((r) => r.path).join(" ")); + for (const r of overrides) { + const unset = r.lines.findIndex((l) => l.toLowerCase() === "! content-type"); + const set = r.lines.findIndex((l) => /^content-type:/i.test(l)); + assert.ok(unset !== -1 && unset < set, `${r.path} must say "! Content-Type" before it sets one`); + } +}); + +test("the raw tree is text, its pages HTML, its binaries their own type — each ONE value", () => { + const cases: Array<[string, string, string]> = [ + ["/source/tree/common/lib/paths.ts", "video/mp2t", "text/plain; charset=utf-8"], + ["/source/tree/README.md", "text/markdown", "text/plain; charset=utf-8"], + ["/source/tree/", "text/html", "text/html; charset=utf-8"], + ["/source/tree/common/", "text/html", "text/html; charset=utf-8"], + ["/source/tree/umtool/report-to-video/fonts/Archivo%5Bwdth%2Cwght%5D.ttf", "font/ttf", "font/ttf"], + ["/source/tree/homepage/app/docs/%5Bslug%5D/page.tsx", "application/octet-stream", "text/plain; charset=utf-8"], + ["/source/tree/umtool/models/yunet.onnx", "application/octet-stream", "application/octet-stream"], + ["/source/tree/homepage/public/icon.svg", "image/svg+xml", "image/svg+xml"], + // A missing directory: Pages answers with a 404 page, on this rule. + ["/source/tree/no/such/dir/", "text/html", "text/html; charset=utf-8"], + ]; + for (const [p, served0, want] of cases) { + const h = served(RULES, p, served0); + assert.equal(h.get("content-type"), want, p); + assert.equal(h.get("x-content-type-options"), "nosniff", p); + } + assert.match(served(RULES, "/source/tree/a.svg", "image/svg+xml").get("content-security-policy")!, /default-src 'none'/); + assert.equal(served(RULES, "/source/manifest.json", "application/json").get("content-type"), "application/json"); +}); diff --git a/homepage/app/lib/source.test.ts b/homepage/app/lib/source.test.ts @@ -0,0 +1,71 @@ +import { test, after } from "node:test"; +import assert from "node:assert/strict"; +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { loadSourceManifest } from "./source"; + +// Run with: +// pnpm --filter homepage test +// +// The /source/ page's loader: the manifest, believed only when it parses AND +// the mirror's info/refs and the tarball are beside it. Anything else is the +// empty state — a bad file must never crash `next build` (review L10). + +const TMP = mkdtempSync(path.join(os.tmpdir(), "homepage-source-")); +after(() => rmSync(TMP, { recursive: true, force: true })); + +const manifest = { + version: 1, + generatedAt: "2026-09-28T12:00:00.000Z", + branch: "main", + sourceCommit: "1".repeat(40), + mirrorHead: "2".repeat(40), + subject: "s", + files: 10, + bytes: 100, + mirror: { files: 9, bytes: 80, packs: 2 }, + tree: { files: 5, dirs: 2, bytes: 20 }, + tarball: { href: "/downloads/archilyzer-source.tar.gz", bytes: 7, sha256: "3".repeat(64) }, + cloneUrl: "https://archilyzer.pages.dev/source/archilyzer.git", + treeHref: "/source/tree/", + audit: { objects: 3, commits: 1, gitleaks: "clean" }, + tools: { git: "2.55.0", filterRepo: "x" }, +}; + +let n = 0; +function pub(opts: { manifest?: unknown; refs?: boolean; tarball?: boolean }): string { + const p = path.join(TMP, `p${n++}`); + mkdirSync(path.join(p, "source", "archilyzer.git", "info"), { recursive: true }); + mkdirSync(path.join(p, "downloads"), { recursive: true }); + if (opts.manifest !== undefined) { + writeFileSync( + path.join(p, "source", "manifest.json"), + typeof opts.manifest === "string" ? opts.manifest : JSON.stringify(opts.manifest), + ); + } + if (opts.refs !== false) writeFileSync(path.join(p, "source", "archilyzer.git", "info", "refs"), "x\trefs/heads/main\n"); + if (opts.tarball !== false) writeFileSync(path.join(p, "downloads", "archilyzer-source.tar.gz"), "t"); + return p; +} + +test("a good manifest with its files beside it is believed", () => { + assert.equal(loadSourceManifest(pub({ manifest }))?.mirrorHead, "2".repeat(40)); +}); + +test("malformed, wrong-version or orphaned manifests are the empty state, never a throw", () => { + const cases: Array<[string, string]> = [ + ["no manifest", pub({})], + ["not JSON", pub({ manifest: "{ half" })], + ["version 2", pub({ manifest: { ...manifest, version: 2 } })], + ["tree.files missing (the page reads it)", pub({ manifest: { ...manifest, tree: { dirs: 1, bytes: 1 } } })], + ["mirror.packs a string", pub({ manifest: { ...manifest, mirror: { ...manifest.mirror, packs: "2" } } })], + ["no info/refs", pub({ manifest, refs: false })], + ["no tarball", pub({ manifest, tarball: false })], + ["no public dir at all", path.join(TMP, "missing")], + ]; + for (const [what, dir] of cases) { + assert.doesNotThrow(() => loadSourceManifest(dir), what); + assert.equal(loadSourceManifest(dir), null, what); + } +}); diff --git a/homepage/app/lib/source.ts b/homepage/app/lib/source.ts @@ -15,10 +15,14 @@ import { // // Believed only when the files it describes are here too (the snapshot.ts // rule): a manifest beside a missing mirror or tarball would advertise a -// clone that fails and a download that 404s. -export function loadSourceManifest(): SourceManifest | null { +// clone that fails and a download that 404s. A malformed or wrong-version +// manifest is null too (parseSourceManifest checks every number the page +// reads), so a bad file is the empty state, never a crash in `next build`. +// `pub` is the test's seam. +export function loadSourceManifest( + pub: string = path.join(process.cwd(), "public"), +): SourceManifest | null { try { - const pub = path.join(process.cwd(), "public"); const manifest = parseSourceManifest( JSON.parse(fs.readFileSync(path.join(pub, "source", "manifest.json"), "utf8")), ); diff --git a/homepage/e2e/source.spec.ts b/homepage/e2e/source.spec.ts @@ -10,11 +10,21 @@ import { test, expect, type Page } from "@playwright/test"; // This suite runs against `next dev`, which serves public/ from disk but does // NOT serve a directory's index.html at `/<dir>/` (Pages does), and ignores // _headers — so indexes are requested by name and content types are left to -// the preview deploy's checks. +// the preview deploy's checks (and to app/lib/headers.test.ts). +// +// E2E_EXPECT_SOURCE=1 (declared in playwright.config.ts) makes the empty state +// a FAILURE of the four data tests, so a gate that published first cannot +// pass with a publish that silently produced nothing, or a loader that always +// says "empty". +const EXPECT_SOURCE = process.env.E2E_EXPECT_SOURCE === "1"; async function published(page: Page): Promise<boolean> { await page.goto("/source/"); - return (await page.getByTestId("source-clone").count()) > 0; + const has = (await page.getByTestId("source-clone").count()) > 0; + if (EXPECT_SOURCE) { + expect(has, "E2E_EXPECT_SOURCE=1, and /source/ shows its empty state: run `archilyzer source publish` first").toBe(true); + } + return has; } test("says what the source is, and that nothing here takes a push", async ({ page }) => { diff --git a/homepage/playwright.config.ts b/homepage/playwright.config.ts @@ -20,6 +20,10 @@ import { // summary it reads instead of // public/homepage-summary.json (app/lib/summary.ts, // ignored by a production build) +// E2E_EXPECT_SOURCE `1` (set by whoever runs the suite, after +// `archilyzer source publish`): e2e/source.spec.ts +// FAILS on the /source/ page's empty state instead +// of accepting it const PORT = portFor("HOMEPAGE_E2E_PORT"); const baseURL = `http://localhost:${PORT}`;