import type { z } from "zod";
import { first } from "../db";
import { badRequest, forbidden, HttpError, notFoundError, unauthorized } from "../errors";
import { publicUrl } from "../events/emit";
import { hasPermission, loadRepo } from "../repos";
import { scopedRepoPermission, tokenCredential } from "../token-scope";
import type { RequestCredential, TokenArea } from "../token-scope";
import type { PullRow } from "../pulls";
import type { Permission, RepoRow, Viewer } from "~/lib/types";
export type ApiContext = { request: Request; url: URL; credential: RequestCredential; viewer: Viewer };
export function unprocessable(message: string): HttpError {
return new HttpError(422, message);
}
export const notImplemented = (): Response => Response.json({ error: "not implemented" }, { status: 501 });
export const noContent = (): Response => new Response(null, { status: 204 });
/** Token-only: the API never honours session cookies, so it carries no ambient browser authority. */
export async function apiContext(request: Request, url: URL): Promise<ApiContext> {
const credential = await tokenCredential(request);
if (!credential?.viewer) throw unauthorized("A valid personal access token is required");
return { request, url, credential, viewer: credential.viewer };
}
/** Account-level actions (creating repos, managing tokens) are outside any fine-grained repo grant. */
export function requireAccountScope(ctx: ApiContext): void {
const grant = ctx.credential.grant;
if (grant?.kind === "classic") return;
if (grant?.kind === "fine" && grant.allRepos && grant.permissions.admin === "write") return;
throw forbidden("This token cannot perform account-level actions");
}
export async function readJson<T>(request: Request, schema: z.ZodType<T>): Promise<T> {
let raw: unknown;
try {
const text = await request.text();
raw = text.trim() === "" ? {} : JSON.parse(text);