Public: anyone can read this; the crew can change it.

/sitemap.xml names its Server-Timing phases: repos, render #5389

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/5375-sitemap-xml-names-its-server-timing-phas → main 1 commit fork

  • Merged. scout/5375-sitemap-xml-names-its-server-timing-phas was merged into main as 16470fc.

Review

Sign in to review this change.
2 files changed +54 −10
src/web/standards.ts +11 −10
@@ -28,6 +28,7 @@ import { llmsRepos, sitemapRepos } from "../core/listings.ts";
2828 import { canRead, getRepo } from "../core/repos.ts";
2929 import type { Ctx } from "../http/context.ts";
3030 import { notFound } from "../http/respond.ts";
31 import { timePhase } from "../core/route_timing.ts";
3132
3233 const DAY = 24 * 60 * 60 * 1000;
3334
@@ -165,18 +166,18 @@ function xml(s: string): string {
165166 export function sitemapXml(ctx: Ctx): Response {
166167 // Kept without an origin, and the origin is put on per answer, so a
167168 // caller who varies `Host` cannot make the forge read the rows again.
168 const paths = sitemapRepos(ctx.app.db).map((r) => xml(`/${r}`));
169 const repos = timePhase(ctx.request, "repos", () => sitemapRepos(ctx.app.db));
169170 // Escaping is per character, so the escaped origin and the escaped path
170171 // join into the escaped URL.
171 const origin = xml(ctx.url.origin);
172 const urls = [
173 `<url><loc>${origin}/</loc></url>`,
174 ...paths.map((path) => `<url><loc>${origin}${path}</loc></url>`),
175 ];
176 return text(
177 `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls.join("\n")}\n</urlset>\n`,
178 "application/xml; charset=utf-8",
179 );
172 const body = timePhase(ctx.request, "render", () => {
173 const origin = xml(ctx.url.origin);
174 const urls = [
175 `<url><loc>${origin}/</loc></url>`,
176 ...repos.map((r) => `<url><loc>${origin}${xml(`/${r}`)}</loc></url>`),
177 ];
178 return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${urls.join("\n")}\n</urlset>\n`;
179 });
180 return text(body, "application/xml; charset=utf-8");
180181 }
181182
182183 /** GET /manifest.webmanifest — the forge as an app on a home screen. */
test/sitemap-timing.test.ts added +43
@@ -0,0 +1,43 @@
1 /** /sitemap.xml names its Server-Timing phases: the read and the XML. */
2 import { afterAll, beforeAll, expect, test } from "bun:test";
3 import { mkdtemp, rm } from "node:fs/promises";
4 import { tmpdir } from "node:os";
5 import { join } from "node:path";
6 import { createApp, type App } from "../src/app.ts";
7 import { createRepo } from "../src/actions/repos.ts";
8 import { createUser } from "../src/core/users.ts";
9 import { serve, type Servers } from "../src/http/server.ts";
10
11 let dataDir: string;
12 let app: App;
13 let servers: Servers;
14
15 beforeAll(async () => {
16 dataDir = await mkdtemp(join(tmpdir(), "forge-sitemap-timing-"));
17 app = createApp({ dataDir, port: 0 });
18 servers = await serve(app);
19 const a = await createUser(app.db, { origin: "seed", trusted: true, username: "alice", email: "[email protected]", password: "a-good-passphrase", adminIfFirst: true });
20 if (!a.ok) throw new Error(a.error);
21 for (const [name, visibility] of [["one", "public"], ["two", "public"], ["hidden", "private"]] as const) {
22 const r = await createRepo(app, a.value, { name, visibility });
23 if (!r.ok) throw new Error(String(r.error));
24 }
25 });
26
27 afterAll(async () => {
28 servers.stop();
29 app.close();
30 await rm(dataDir, { recursive: true, force: true });
31 });
32
33 test("the sitemap names repos and render, and lists only the public repositories", async () => {
34 const res = await fetch(`${servers.url}/sitemap.xml`);
35 expect(res.headers.get("content-type")).toBe("application/xml; charset=utf-8");
36 const timing = res.headers.get("server-timing") ?? "";
37 expect(timing).toContain("repos;dur=");
38 expect(timing).toContain("render;dur=");
39 const body = await res.text();
40 expect(body).toContain("/alice/one</loc>");
41 expect(body).toContain("/alice/two</loc>");
42 expect(body).not.toContain("hidden");
43 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs