aboutsummaryrefslogtreecommitdiff
path: root/src/scraper.ts
diff options
context:
space:
mode:
authorJoe Mou <dev@mou.fo>2026-09-03 23:08:08 -0400
committerJoe Mou <dev@mou.fo>2026-09-03 23:28:52 -0400
commitae000d743ef54368d21596d590e1acc0d90b370e (patch)
tree97bbf60e5e6ba994c8b7cf8c0cbfa7bd38bf16d1 /src/scraper.ts
parentb9fb8851bb869a27048e02a51b28ef3aaffdd471 (diff)
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QERwoRJhuqMhKzzCUW4Skv
Diffstat (limited to 'src/scraper.ts')
-rw-r--r--src/scraper.ts12
1 files changed, 8 insertions, 4 deletions
diff --git a/src/scraper.ts b/src/scraper.ts
index 6461b19..fb48700 100644
--- a/src/scraper.ts
+++ b/src/scraper.ts
@@ -13,16 +13,20 @@ export class GitHubHTTPError extends Error {
}
}
-export class RedirectError extends Error {
+export abstract class RedirectError extends Error {
location: string;
constructor(location: string) {
super(`Redirect to ${location}`);
- this.name = "RedirectError";
+ this.name = this.constructor.name;
this.location = location;
}
}
+export class InternalRedirectError extends RedirectError {}
+
+export class ExternalRedirectError extends RedirectError {}
+
interface TreeItem {
contentType: "directory" | "file";
name: string;
@@ -423,7 +427,7 @@ async function fetchGitHubResponse(path: string, accept: string): Promise<Respon
if (location) {
const url = new URL(location);
if (url.hostname === "github.com") {
- throw new RedirectError(url.pathname);
+ throw new InternalRedirectError(url.pathname);
}
}
}
@@ -1127,7 +1131,7 @@ export async function getGitHubOwner(owner: string): Promise<GitHubOwner> {
return parseOrganization(document, owner);
}
// Not a profile page (marketing pages like github.com/features, etc.).
- throw new RedirectError(`https://github.com/${owner}`);
+ throw new ExternalRedirectError(`https://github.com/${owner}`);
}
// The text directly inside an element, ignoring any nested elements' text.