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

forge.json's app.looks lists every top-level page that answers on main today #2785

Merged opened this

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

Merged

scout/forge:scout/2783-forge-json-s-app-looks-lists-every-top-l → main 3 commits fork 1 thread · 1 unresolved

  • Merged. scout/2783-forge-json-s-app-looks-lists-every-top-l was merged into main as 74363e0.

Review

Sign in to review this change.
2 files changed +103 −0
forge.json +1
@@ -3,6 +3,7 @@
33 "api.anthropic.com"
44 ],
55 "app": {
6 "looks": ["/", "/forges", "/family", "/front", "/explore", "/board", "/boards", "/asked", "/search", "/new", "/audit", "/releases", "/apps", "/migrate", "/map", "/flow", "/system", "/contribute", "/resilience", "/settings"],
67 "run": "bun run src/main.ts",
78 "install": "",
89 "health": "/healthz",
test/forge-looks.test.ts added +102
@@ -0,0 +1,102 @@
1 /**
2 * forge.json's `app.looks` pictures the forge's own top-level pages (#2783):
3 * every page the router answers at one path segment is in the list, or in
4 * NOT_A_PAGE with the reason it is not worth a picture; each listed path
5 * answers 200 to an admin.
6 */
7
8 import { afterAll, beforeAll, expect, test } from "bun:test";
9 import { readFileSync } from "node:fs";
10 import { mkdtemp, rm } from "node:fs/promises";
11 import { tmpdir } from "node:os";
12 import { join } from "node:path";
13 import { createApp, type App } from "../src/app.ts";
14 import { createToken } from "../src/core/tokens.ts";
15 import { createUser } from "../src/core/users.ts";
16 import { buildRouter, serve, type Servers } from "../src/http/server.ts";
17 import { quietLogs } from "./helpers/globals.ts";
18
19 quietLogs();
20
21 /** Top-level routes that are not pages, or are not worth a picture, and why. */
22 const NOT_A_PAGE: Record<string, string> = {
23 "/llms.txt": "plain markdown for machines, not a page to picture",
24 "/robots.txt": "plain text for crawlers",
25 "/releases.json": "JSON feed of /releases",
26 "/sitemap.xml": "XML for crawlers",
27 "/manifest.webmanifest": "the install manifest, JSON",
28 "/sw.js": "the service worker script",
29 "/audit.csv": "a CSV download of /audit",
30 "/claim": "answers 404 to an admin: it exists only for a link-holder",
31 "/first-admin": "the setup form for a forge with no admin yet",
32 "/signin": "a sign-in form, not a page of the forge",
33 "/signup": "a sign-up form, not a page of the forge",
34 "/recover": "an account-recovery form, not a page of the forge",
35 "/verify": "answers 400 without the emailed code",
36 "/logout": "a mutation, answers 404 to a GET",
37 "/theme": "a mutation, answers 404 to a GET",
38 "/tell": "answers 404 to a GET",
39 "/asks": "redirects to /#needs-you",
40 "/big": "redirects to /board?wall",
41 "/mcp": "a protocol endpoint, answers 405 to a GET",
42 "/_measure": "the page-load beacon, a POST endpoint",
43 "/events": "a live event stream that never ends",
44 };
45
46 let dir: string;
47 let app: App;
48 let servers: Servers;
49 let token: string;
50
51 beforeAll(async () => {
52 dir = await mkdtemp(join(tmpdir(), "forge-looks-"));
53 app = createApp({ dataDir: dir, port: 0 });
54 servers = await serve(app);
55 const r = await createUser(app.db, {
56 origin: "seed",
57 trusted: true,
58 username: "alice",
59 email: "[email protected]",
60 password: "a-good-passphrase",
61 adminIfFirst: true,
62 });
63 if (!r.ok) throw new Error(r.error);
64 token = createToken(app.db, r.value.id, "t", "admin").token;
65 });
66
67 afterAll(async () => {
68 servers.stop();
69 app.close();
70 await rm(dir, { recursive: true, force: true });
71 });
72
73 const topLevel = () =>
74 buildRouter()
75 .patterns()
76 .filter((p) => p === "/" || /^\/[^/:*]+$/.test(p));
77
78 const declared = (): string[] =>
79 JSON.parse(readFileSync(join(import.meta.dir, "..", "forge.json"), "utf8")).app.looks;
80
81 const get = (path: string) =>
82 fetch(`${servers.url}${path}`, {
83 redirect: "manual",
84 headers: { authorization: `Bearer ${token}` },
85 });
86
87 test("app.looks is the router's top-level pages minus the not-a-page list", () => {
88 const pages = topLevel().filter((p) => !(p in NOT_A_PAGE));
89 expect([...declared()].sort()).toEqual([...pages].sort());
90 });
91
92 test("every not-a-page entry is a real route with a reason", () => {
93 const routes = new Set(topLevel());
94 for (const [path, why] of Object.entries(NOT_A_PAGE)) {
95 expect(routes.has(path)).toBe(true);
96 expect(why.length).toBeGreaterThan(10);
97 }
98 });
99
100 test("every listed path answers 200 to an admin", async () => {
101 for (const p of declared()) expect([p, (await get(p)).status]).toEqual([p, 200]);

Nit: 20 sequential fetches. Promise.all would keep this fast as the list grows toward the cap.

102 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs