// The registry's metadata cache (src/kernel/npm/registry.ts) in a real // browser: Cache Storage, and fetches page.route answers from the recordings. The module // is bundled here and imported into a stub page on the test origin, so no test hook // enters the host API or the kernel protocol. import { build } from "esbuild"; import satisfies from "semver/functions/satisfies.js"; import { test, expect, type Page } from "@playwright/test"; import type { Registry } from "../src/kernel/npm/registry"; import type { Spec } from "../src/kernel/npm/spec"; // @ts-expect-error: an untyped .mjs script import { loadRecording } from "../scripts/npm-oracle.mjs"; interface Under { createRegistry(nodeVersion?: string, options?: { gateways?: string[] }): Registry; parseSpec(name: string, raw: string): Spec; } declare global { interface Window { __registry: Under } } type Doc = { "dist-tags": Record; versions: Record }; const REPO = new URL("..", import.meta.url).pathname; let bundle = ""; test.beforeAll(async () => { const out = await build({ stdin: { contents: 'export { createRegistry } from "./src/kernel/npm/registry"; export { parseSpec } from "./src/kernel/npm/spec";', resolveDir: REPO, loader: "ts", }, bundle: true, format: "esm", write: false, }); bundle = out.outputFiles[0]!.text; }); function recorded(fixture: string, name: string): Doc { const rec = (loadRecording(fixture) as Map).get(`/${name.replace("/", "%2f")}`)!; return JSON.parse(new TextDecoder().decode(rec.body)); } /** A packument cut back to the versions before `version`, as the registry served it * before `version` was published. */ function before(doc: Doc, version: string, latest: string): Doc { const keys = Object.keys(doc.versions); const versions = Object.fromEntries(keys.slice(0, keys.indexOf(version)).map((v) => [v, doc.versions[v]])); return { ...doc, "dist-tags": { latest }, versions }; } /** How the routed registry answers, read at each request: `cacheControl` as the * registry's header (none when undefined), and `offline` aborts every request. */ interface Network { cacheControl?: string; offline?: boolean; } /** * Serves `docs` as the registry: a packument for the corgi Accept, and * `GET //` from the packument's `versions[version]`. Every request lands * in the returned log as " ", with " if-none-match" when it carries * one. Routing turns the browser's HTTP cache off, so this sees every fetch, and cannot * see a refetch's `cache: "no-cache"`. */ async function serve(page: Page, docs: Map, net: Network = {}): Promise { const log: string[] = []; await page.route("**/__registry/index.html", (route) => route.fulfill({ contentType: "text/html", body: "registry" })); await page.route("**/__registry/registry.js", (route) => route.fulfill({ contentType: "text/javascript", body: bundle })); await page.route("https://registry.npmjs.org/**", async (route) => { const req = route.request(); const path = decodeURIComponent(new URL(req.url()).pathname.slice(1)); const headers = await req.allHeaders(); const corgi = (headers.accept ?? "").startsWith("application/vnd.npm.install-v1+json"); log.push(`${corgi ? "corgi" : "json"} /${path}${headers["if-none-match"] ? " if-none-match" : ""}`); if (net.offline) return route.abort("internetdisconnected"); const at = path.indexOf("/", path.startsWith("@") ? path.indexOf("/") + 1 : 0); const name = at === -1 ? path : path.slice(0, at); const doc = docs.get(name); const body = at === -1 ? (corgi ? doc : undefined) : doc?.versions[path.slice(at + 1)]; const cors = { "access-control-allow-origin": "*" }; if (body === undefined) return route.fulfill({ status: 404, headers: cors, contentType: "application/json", body: '{"error":"Not found"}' }); const sent: Record = { ...cors, ...(net.cacheControl !== undefined && { "cache-control": net.cacheControl }) }; return route.fulfill({ headers: sent, contentType: "application/json", body: JSON.stringify(body) }); }); await page.clock.install(); await page.goto("/__registry/index.html"); await page.evaluate(async () => { window.__registry = await import(`${location.origin}/__registry/registry.js`); }); return log; } /** In the page: a new Registry (as a new install makes one), and `name@raw` from it. * Answers the chosen version, or the error's code and message, with the counts. */ async function manifests(page: Page, wanted: [string, string][]) { return await page.evaluate(async (wanted) => { const { createRegistry, parseSpec } = window.__registry; const registry = createRegistry(); const picked = await Promise.all(wanted.map(async ([name, raw]) => { const spec = parseSpec(name, raw); if (spec.type === "alias" || spec.type === "unsupported") throw new Error(raw); try { return (await registry.manifest(spec)).version as string; } catch (e) { return `${(e as { code: string }).code}: ${(e as Error).message}`; } })); return { picked, counts: { ...registry.counts }, ...(registry.warnings.length && { warnings: [...registry.warnings] }) }; }, wanted); } test("a stored packument answers a spec it satisfies with no request, though the registry has moved on", async ({ page }) => { const hono = recorded("hono", "hono"); const docs = new Map([["hono", before(hono, "4.13.13", "4.13.12")]]); const log = await serve(page, docs); expect(await manifests(page, [["hono", "^4.0.0"]])).toEqual({ picked: ["4.13.12"], counts: { cached: 0, fetched: 1 } }); expect(log).toEqual(["corgi /hono"]); docs.set("hono", hono); log.length = 0; expect(await manifests(page, [["hono", "^4.0.0"], ["hono", "4.13.12"], ["hono", "latest"]])).toEqual({ picked: ["4.13.12", "4.13.12", "4.13.12"], counts: { cached: 1, fetched: 0 }, }); expect(log).toEqual([]); }); test("a packument within the max-age the registry sent answers with no request", async ({ page }) => { const hono = recorded("hono", "hono"); const docs = new Map([["hono", before(hono, "4.13.13", "4.13.12")]]); const log = await serve(page, docs, { cacheControl: "public, max-age=60" }); await manifests(page, [["hono", "^4.0.0"]]); docs.set("hono", hono); log.length = 0; await page.clock.fastForward(59_000); expect(await manifests(page, [["hono", "^4.0.0"]])).toEqual({ picked: ["4.13.12"], counts: { cached: 1, fetched: 0 } }); expect(log).toEqual([]); }); test("past its max-age a packument is asked for once more: the same document is kept for another max-age, a new one replaces it", async ({ page }) => { const hono = recorded("hono", "hono"); const docs = new Map([["hono", before(hono, "4.13.13", "4.13.12")]]); const log = await serve(page, docs, { cacheControl: "public, max-age=1" }); await manifests(page, [["hono", "^4.0.0"]]); log.length = 0; await page.clock.fastForward(1_500); expect(await manifests(page, [["hono", "^4.0.0"], ["hono", "latest"]])).toEqual({ picked: ["4.13.12", "4.13.12"], counts: { cached: 0, fetched: 1 } }); expect(await manifests(page, [["hono", "^4.0.0"]])).toEqual({ picked: ["4.13.12"], counts: { cached: 1, fetched: 0 } }); // A cross-origin If-None-Match needs a preflight, which registry.npmjs.org answers 404. expect(log).toEqual(["corgi /hono"]); docs.set("hono", hono); log.length = 0; await page.clock.fastForward(1_500); expect(await manifests(page, [["hono", "^4.0.0"]])).toEqual({ picked: ["4.13.14"], counts: { cached: 0, fetched: 1 } }); expect(await manifests(page, [["hono", "^4.0.0"]])).toEqual({ picked: ["4.13.14"], counts: { cached: 1, fetched: 0 } }); expect(log).toEqual(["corgi /hono"]); }); test("without a max-age from the registry a packument is fresh for 300 s", async ({ page }) => { const log = await serve(page, new Map([["hono", recorded("hono", "hono")]])); await manifests(page, [["hono", "^4.0.0"]]); await page.clock.fastForward(299_000); expect(await manifests(page, [["hono", "^4.0.0"]])).toEqual({ picked: ["4.13.14"], counts: { cached: 1, fetched: 0 } }); await page.clock.fastForward(2_000); expect(await manifests(page, [["hono", "^4.0.0"]])).toEqual({ picked: ["4.13.14"], counts: { cached: 0, fetched: 1 } }); expect(log).toEqual(["corgi /hono", "corgi /hono"]); }); test("offline past its max-age, the stored packument answers with a warning", async ({ page }) => { const net: Network = { cacheControl: "public, max-age=1" }; const log = await serve(page, new Map([["hono", recorded("hono", "hono")]]), net); await manifests(page, [["hono", "^4.0.0"]]); net.offline = true; log.length = 0; await page.clock.fastForward(1_500); expect(await manifests(page, [["hono", "^4.0.0"], ["hono", "4.13.13"]])).toEqual({ picked: ["4.13.14", "4.13.13"], counts: { cached: 1, fetched: 0 }, warnings: ["could not revalidate https://registry.npmjs.org/hono (Failed to fetch); using the stored copy"], }); expect(log).toEqual(["corgi /hono"]); // Offline with nothing stored is the fetch's own failure. expect(await manifests(page, [["@hono/node-server", "^1.0.0"]])).toEqual({ picked: ["undefined: Failed to fetch"], counts: { cached: 0, fetched: 0 } }); }); test("a spec the stored packument cannot answer refetches it once and stores the new copy", async ({ page }) => { const hono = recorded("hono", "hono"); const docs = new Map([["hono", before(hono, "4.13.13", "4.13.12")]]); const log = await serve(page, docs); await manifests(page, [["hono", "^4.0.0"]]); docs.set("hono", hono); log.length = 0; expect(await manifests(page, [["hono", "4.13.13"], ["hono", "^4.13.13"]])).toEqual({ picked: ["4.13.13", "4.13.14"], counts: { cached: 1, fetched: 1 } }); expect(log).toEqual(["corgi /hono"]); log.length = 0; expect(await manifests(page, [["hono", "4.13.13"]])).toEqual({ picked: ["4.13.13"], counts: { cached: 1, fetched: 0 } }); expect(log).toEqual([]); expect(await manifests(page, [["hono", "^99.0.0"]])).toEqual({ picked: ["ETARGET: No matching version found for hono@^99.0.0."], counts: { cached: 1, fetched: 1 }, }); expect(log).toEqual(["corgi /hono"]); }); test("edges asking for one package at once share one fetch", async ({ page }) => { const log = await serve(page, new Map([["hono", recorded("hono", "hono")], ["@hono/node-server", recorded("hono", "@hono/node-server")]])); expect(await manifests(page, [["hono", "^4.0.0"], ["hono", "4.13.13"], ["hono", "latest"], ["@hono/node-server", "^1.0.0"], ["@hono/node-server", "*"]])).toEqual({ picked: ["4.13.14", "4.13.13", "4.13.14", "1.19.17", "2.1.5"], counts: { cached: 0, fetched: 2 }, }); expect(log.sort()).toEqual(["corgi /@hono/node-server", "corgi /hono"]); }); test("a package the registry does not have is E404, with the registry's explanation as npm-registry-fetch adds it", async ({ page }) => { const log = await serve(page, new Map()); expect(await manifests(page, [["no-such-package", "^1.0.0"]])).toEqual({ picked: ["E404: 404 Not Found - GET https://registry.npmjs.org/no-such-package - Not found"], counts: { cached: 0, fetched: 1 }, }); expect(log).toEqual(["corgi /no-such-package"]); }); test("lockMeta reads a version's license and funding once, and serves it from the store after", async ({ page }) => { const log = await serve(page, new Map([["make-dir", recorded("conflict", "make-dir")], ["postcss", recorded("conflict", "postcss")]])); const lockMeta = () => page.evaluate(async () => { const registry = window.__registry.createRegistry(); const metas = await Promise.all([ registry.lockMeta("make-dir", "3.1.0" as never), registry.lockMeta("make-dir", "3.1.0" as never), registry.lockMeta("postcss", "8.5.6" as never), ]); return { metas, counts: { ...registry.counts } }; }); const metas = [ { license: "MIT", funding: { url: "https://github.com/sponsors/sindresorhus" } }, { license: "MIT", funding: { url: "https://github.com/sponsors/sindresorhus" } }, { license: "MIT", funding: [ { url: "https://opencollective.com/postcss/", type: "opencollective" }, { url: "https://tidelift.com/funding/github/npm/postcss", type: "tidelift" }, { url: "https://github.com/sponsors/ai", type: "github" }, ], }, ]; expect(await lockMeta()).toEqual({ metas, counts: { cached: 0, fetched: 2 } }); expect(log.sort()).toEqual(["json /make-dir/3.1.0", "json /postcss/8.5.6"]); log.length = 0; expect(await lockMeta()).toEqual({ metas, counts: { cached: 2, fetched: 0 } }); expect(log).toEqual([]); }); /** Answers dev.nibling.npm.getClosure on each of `gateways` (a closure, or a status that * fails), logging each ask as " ". */ async function gateways(page: Page, answers: Record): Promise { const asked: string[] = []; await page.route("https://*.gw.test/xrpc/dev.nibling.npm.getClosure**", (route) => { const url = new URL(route.request().url()); asked.push(`${url.host} ${url.searchParams.getAll("spec").join(",")}`); const answer = answers[url.host]; const cors = { "access-control-allow-origin": "*" }; if (typeof answer === "number") return route.fulfill({ status: answer, headers: cors, body: "" }); return route.fulfill({ headers: cors, contentType: "application/x-ndjson", body: typeof answer === "string" ? answer : ndjson(answer as ClosureView) }); }); return asked; } interface ClosureView { registry: string; packuments: Record; versions: Record; complete: string[] }>; meta: Record>; absent: string[]; end?: boolean; } /** `view` as the lines a gateway streams: each name's versions on its first spec line. */ function ndjson(view: ClosureView): string { const lines: unknown[] = [{ t: "closure", v: 1, registry: view.registry }]; for (const [name, p] of Object.entries(view.packuments)) { p.complete.forEach((raw, i) => lines.push({ t: "spec", name, raw, ...(i === 0 && { distTags: p["dist-tags"] }), versions: i === 0 ? p.versions : {} })); } for (const [id, fields] of Object.entries(view.meta)) lines.push({ t: "meta", id, ...fields }); for (const name of view.absent) lines.push({ t: "absent", name }); if (view.end !== false) lines.push({ t: "end" }); return lines.map((l) => `${JSON.stringify(l)}\n`).join(""); } /** `doc` cut to `range`'s versions and its latest, as a gateway answers it. */ function closureOf(name: string, doc: Doc, range: string, extra: Record = {}) { const latest = doc["dist-tags"].latest!; const keep = [...Object.keys(doc.versions).filter((v) => satisfies(v, range)), latest]; const cut = { ...doc, versions: Object.fromEntries(keep.map((v) => [v, doc.versions[v]])), complete: [range] }; return { registry: "https://registry.npmjs.org", packuments: { [name]: cut }, meta: {}, absent: [], ...extra } as ClosureView & { packuments: Record }; } /** In the page: a Registry over `gateways`, seeded with hono@^4.0.0, then each spec and probe. */ const seededRun = (page: Page, gatewayUrls: string[], raws: string[], probes: [string, string][] = [], withMeta = false) => page.evaluate(async ({ gatewayUrls, raws, probes, withMeta }) => { const { createRegistry, parseSpec } = window.__registry; const registry = createRegistry(undefined, { gateways: gatewayUrls }); await registry.seed([{ name: "hono", raw: "^4.0.0" }]); const picked: string[] = []; for (const raw of raws) picked.push((await registry.manifest(parseSpec("hono", raw) as never)).version); const probed = await Promise.all(probes.map(async ([n, v]) => (await registry.probe(n, v as never))?.version ?? null)); const meta = withMeta ? await registry.lockMeta("hono", picked[0] as never) : undefined; return { picked, probed, meta, counts: { ...registry.counts } }; }, { gatewayUrls, raws, probes, withMeta }); test("a closure answers the specs it lists with no request, and the same version the whole packument would", async ({ page }) => { const hono = recorded("hono", "hono"); const log = await serve(page, new Map([["hono", hono]])); const latest = hono["dist-tags"].latest!; const asked = await gateways(page, { "a.gw.test": closureOf("hono", hono, "^4.0.0", { meta: { [`hono@${latest}`]: { license: "MIT", funding: "https://fund.test" } } }) }); const seeded = await seededRun(page, ["https://a.gw.test"], ["^4.0.0"], [], true); expect(asked).toEqual(["a.gw.test hono@^4.0.0"]); expect(log).toEqual([]); expect(seeded.meta).toEqual({ license: "MIT", funding: { url: "https://fund.test" } }); expect(seeded.counts).toEqual({ cached: 2, fetched: 1 }); expect(seeded.picked).toEqual((await manifests(page, [["hono", "^4.0.0"]])).picked); // The cut copy holds no 3.x, so a 3.x answer is the whole packument's. expect((await seededRun(page, ["https://a.gw.test"], ["^3.0.0"])).picked[0]).toMatch(/^3\./); }); test("gateways are asked in order until one answers, and with none answering the install fetches as before", async ({ page }) => { const hono = recorded("hono", "hono"); const log = await serve(page, new Map([["hono", hono]])); const asked = await gateways(page, { "down.gw.test": 503, "b.gw.test": closureOf("hono", hono, "^4.0.0") }); await seededRun(page, ["https://down.gw.test", "https://b.gw.test"], ["^4.0.0"]); expect(asked).toEqual(["down.gw.test hono@^4.0.0", "b.gw.test hono@^4.0.0"]); expect(log).toEqual([]); asked.length = 0; const alone = await seededRun(page, ["https://down.gw.test"], ["^4.0.0"]); expect(asked).toEqual(["down.gw.test hono@^4.0.0"]); expect(log).toEqual(["corgi /hono"]); expect(alone.counts).toEqual({ cached: 0, fetched: 1 }); }); test("a gateway is not trusted with tarball locations, another package's documents, or another registry's closure", async ({ browser }) => { const hono = recorded("hono", "hono"); const good = closureOf("hono", hono, "^4.0.0"); const someVersion = Object.keys(good.packuments.hono!.versions)[0]!; const tamper = (edit: (mani: { dist: { tarball: string }; name: string }) => void) => { const copy = structuredClone(good) as typeof good & { packuments: { hono: { versions: Record } } }; edit(copy.packuments.hono.versions[someVersion]!); return copy; }; const answers = { "t.gw.test": tamper((m) => (m.dist.tarball = "https://evil.test/hono.tgz")), "r.gw.test": tamper((m) => (m.name = "not-hono")), "o.gw.test": { ...good, registry: "https://other.test" }, }; for (const host of Object.keys(answers)) { // A fresh context each time: an empty store, so only a refused closure fetches. const context = await browser.newContext({ baseURL: test.info().project.use.baseURL }); const page = await context.newPage(); const log = await serve(page, new Map([["hono", hono]])); await gateways(page, answers); await seededRun(page, [`https://${host}`], ["^4.0.0"]); expect(log, host).toEqual(["corgi /hono"]); await context.close(); } }); test("a stream cut short answers what arrived, and loads the rest", async ({ page }) => { const hono = recorded("hono", "hono"); const log = await serve(page, new Map([["hono", hono]])); await gateways(page, { "cut.gw.test": closureOf("hono", hono, "^4.0.0", { end: false }) }); const run = await seededRun(page, ["https://cut.gw.test"], ["^4.0.0", "^3.0.0"]); // ^4.0.0 arrived before the cut; ^3.0.0 never did, so it loaded the packument. expect(log).toEqual(["corgi /hono"]); expect(run.picked[1]).toMatch(/^3\./); }); test("a name the closure lists as absent probes as absent with no request", async ({ page }) => { const hono = recorded("hono", "hono"); const log = await serve(page, new Map([["hono", hono]])); await gateways(page, { "a.gw.test": closureOf("hono", hono, "^4.0.0", { absent: ["hono-wasm32-wasi"] }) }); const run = await seededRun(page, ["https://a.gw.test"], [], [["hono-wasm32-wasi", "4.0.0"]]); expect(run.probed).toEqual([null]); expect(log).toEqual([]); }); test("a probe answered by a 404 without CORS headers, as the registry sends them, is absent with a warning", async ({ page }) => { await serve(page, new Map()); // What the page sees of the registry's CORS-less 404 (checked against registry.npmjs.org, // 2026-11-06: net::ERR_FAILED). A fulfilled route skips the CORS check, so abort instead. // Registered after serve()'s route, so it answers first. await page.route("https://registry.npmjs.org/@img%2fsharp-wasm32-wasi", (route) => route.abort("failed")); const result = await page.evaluate(async () => { const registry = window.__registry.createRegistry(); const probed = await registry.probe("@img/sharp-wasm32-wasi", "0.34.5" as never); return { probed: probed ?? null, warnings: [...registry.warnings] }; }); expect(result.probed).toBeNull(); expect(result.warnings).toEqual([expect.stringMatching(/^could not read https:\/\/registry\.npmjs\.org\/@img%2fsharp-wasm32-wasi \(.+\); treating it as absent$/)]); });