feat(docs): add OpenAPI API reference from oRPC contract

Generate an OpenAPI 3.1 spec from the oRPC contract at build time
using @orpc/openapi, then use fumadocs-openapi to render interactive
API reference pages grouped by tag (Titles, Episodes, Seasons, etc.).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-14 13:07:42 -04:00
co-authored by Claude Opus 4.6
parent 17ddffef41
commit 3148574d5b
50 changed files with 5709 additions and 64 deletions
-2
View File
@@ -9,7 +9,6 @@
},
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
}
@@ -21,7 +20,6 @@
"ascAppId": "6760432427"
},
"android": {
"serviceAccountKeyPath": "./google-service-account.json",
"track": "internal"
}
}
+24 -5
View File
@@ -1,23 +1,41 @@
import { Hono } from "hono";
import { cors } from "hono/cors";
import { getLatestVersion } from "./lib/github";
const GITHUB_RELEASES_URL =
"https://api.github.com/repos/jakejarvis/sofa/releases/latest";
const app = new Hono();
app.use("*", cors());
app.get("/health", (c) => c.json({ status: "healthy" }));
app.get("/v1/version", async (c) => {
try {
const latest = await getLatestVersion();
const res = await fetch(GITHUB_RELEASES_URL, {
headers: {
Accept: "application/vnd.github.v3+json",
"User-Agent": "sofa-public-api",
...(process.env.GITHUB_TOKEN && {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
}),
},
signal: AbortSignal.timeout(10_000),
});
if (!res.ok) throw new Error(`GitHub API returned ${res.status}`);
const data = (await res.json()) as {
tag_name: string;
html_url: string;
};
c.header(
"Cache-Control",
"public, s-maxage=900, stale-while-revalidate=3600",
);
return c.json(latest);
return c.json({
version: data.tag_name.replace(/^v/, ""),
release_url: data.html_url,
});
} catch (e) {
return c.json(
{ error: e instanceof Error ? e.message : "Failed to fetch version" },
@@ -35,6 +53,7 @@ app.post("/v1/telemetry", async (c) => {
const posthogKey = process.env.POSTHOG_API_KEY;
if (!posthogKey) {
// return okay, this isn't the user's problem
return c.body(null, 204);
}
-31
View File
@@ -1,31 +0,0 @@
const GITHUB_RELEASES_URL =
"https://api.github.com/repos/jakejarvis/sofa/releases/latest";
export interface VersionInfo {
version: string;
releaseUrl: string;
}
export async function getLatestVersion(): Promise<VersionInfo> {
const res = await fetch(GITHUB_RELEASES_URL, {
headers: {
Accept: "application/vnd.github.v3+json",
"User-Agent": "sofa-public-api",
...(process.env.GITHUB_TOKEN && {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
}),
},
signal: AbortSignal.timeout(10_000),
});
if (!res.ok) throw new Error(`GitHub API ${res.status}`);
const data = (await res.json()) as {
tag_name: string;
html_url: string;
};
return {
version: data.tag_name.replace(/^v/, ""),
releaseUrl: data.html_url,
};
}