From 5bfe8b345d2ee92111a2363321a248adb4f5195a Mon Sep 17 00:00:00 2001 From: Joe Mou Date: Thu, 3 Sep 2026 23:08:08 -0400 Subject: Handle route errors in middleware Every page route wrapped its scrape in tryRender, which awaited the promise, rendered the template on success, and turned a failure into a redirect or an error page. Hono's onError does that mapping for all of them at once, so handlers now await their scrape inline and both wrappers are gone. Fragments under /api/ still fail as an empty body rather than a page; onError checks for them first, since a fragment turns a redirect into a 404 instead of following it. RedirectError becomes an abstract base with InternalRedirectError and ExternalRedirectError under it, so the handler dispatches on the type instead of sniffing the location for an https:// prefix. One behavior change: a thrown value that is not an Error no longer renders error.eta, because Hono only routes Error instances to onError. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01QERwoRJhuqMhKzzCUW4Skv --- cgithub/src/scraper.test.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'cgithub/src/scraper.test.ts') diff --git a/cgithub/src/scraper.test.ts b/cgithub/src/scraper.test.ts index f6be2b6..8dcb0b8 100644 --- a/cgithub/src/scraper.test.ts +++ b/cgithub/src/scraper.test.ts @@ -16,8 +16,9 @@ import { getGitHubTree, getGitHubWiki, getGitHubWikiPages, + ExternalRedirectError, GitHubHTTPError, - RedirectError, + InternalRedirectError, } from "./scraper.ts"; describe("GitHub scraper", () => { @@ -215,7 +216,7 @@ describe("GitHub scraper", () => { const oid = "b39c421b98f49d83ae50ec502c3ddfc3bf28f2c6"; await assert.rejects(getGitHubTree("actions", "deploy-pages", oid, "src/index.js"), (err) => { - assert(err instanceof RedirectError, "error should be a RedirectError"); + assert(err instanceof InternalRedirectError, "error should be an InternalRedirectError"); assert.strictEqual(err.location, `/actions/deploy-pages/blob/${oid}/src/index.js`); return true; }); @@ -490,7 +491,7 @@ describe("GitHub scraper", () => { it("should redirect to the repository when it has no wiki", async () => { await assert.rejects(getGitHubWiki("git", "git"), (err) => { - assert(err instanceof RedirectError, "error should be a RedirectError"); + assert(err instanceof InternalRedirectError, "error should be an InternalRedirectError"); assert.strictEqual(err.location, "/git/git"); return true; }); @@ -571,7 +572,7 @@ describe("GitHub scraper", () => { it("should redirect to GitHub for non-profile pages", async () => { await assert.rejects(getGitHubOwner("features"), (err) => { - assert(err instanceof RedirectError, "error should be a RedirectError"); + assert(err instanceof ExternalRedirectError, "error should be an ExternalRedirectError"); assert.strictEqual(err.location, "https://github.com/features"); return true; }); @@ -599,7 +600,7 @@ describe("GitHub scraper", () => { await assert.rejects( getGitHubBlob("earendil-works", "pi", "main", "packages/agent"), (err) => { - assert(err instanceof RedirectError, "error should be a RedirectError"); + assert(err instanceof InternalRedirectError, "error should be an InternalRedirectError"); assert.strictEqual(err.location, "/earendil-works/pi/tree/main/packages/agent"); return true; }, @@ -610,7 +611,7 @@ describe("GitHub scraper", () => { await assert.rejects( getGitHubTree("earendil-works", "pi", "main", "packages/agent/package.json"), (err) => { - assert(err instanceof RedirectError, "error should be a RedirectError"); + assert(err instanceof InternalRedirectError, "error should be an InternalRedirectError"); assert.strictEqual( err.location, "/earendil-works/pi/blob/main/packages/agent/package.json", -- cgit v1.3.1