import { all, first, run } from "../db";
import { randomId } from "../crypto";
import { assertBelowCap } from "../limits";
import { HttpError, forbidden, notFoundError } from "../errors";
const MAX_LOCKS_PER_REPO = 1000;
const MAX_LOCK_PATH = 1024;
export const DEFAULT_LOCK_PAGE = 100;
export const MAX_LOCK_PAGE = 100;
export type LfsLock = { id: string; path: string; locked_at: string; owner: { name: string } };
type LockRow = { id: string; path: string; created_at: number; owner_id: string; owner_name: string };
const LOCK_SELECT = `SELECT l.id, l.path, l.created_at, l.owner_id, a.name AS owner_name
FROM lfs_locks l JOIN accounts a ON a.id = l.owner_id`;
export function lockJson(row: LockRow): LfsLock {
return {
id: row.id,
path: row.path,
locked_at: new Date(row.created_at).toISOString(),
owner: { name: row.owner_name },
};
}
/** Cursor is `<created_at>:<id>` for stable keyset pagination. */
function parseCursor(cursor: string | undefined): [number, string] | null {
if (!cursor) return null;
const separator = cursor.indexOf(":");
const createdAt = Number(cursor.slice(0, separator));
return separator > 0 && Number.isFinite(createdAt) ? [createdAt, cursor.slice(separator + 1)] : null;
}
export function clampLimit(value: unknown): number {
const parsed = Number(value);
if (!Number.isInteger(parsed) || parsed <= 0) return DEFAULT_LOCK_PAGE;
return Math.min(parsed, MAX_LOCK_PAGE);
}