import { api } from "#lib/api/client.js"; export interface Change { base: string; hash: string; path: string; space: string; } export interface Revision { changes: Change[]; machine: string; main_branch: string; main_sha: string; seq: number; time: number; } export interface Repo { key: string; name: string; seq: number; time: number; } export interface Machine { created_at: number; id: number; last_push: number; name: string; revisions: number; } export interface FileEntry { hash: string; time: number; } export interface RepoState { seq: number; spaces: Record>; } export interface Code { code: string; command: string; expires_at: number; } export const SHARED = "shared"; const PATTERNS = 7; const WEB = "web"; const REVOKED = "revoked"; const SHORT_SHA = 7; const BRANCH_PREFIX = "branch:"; export const lithology = (machine: string, machines: string[]): string => { if (machine === WEB) { return "lith lith-web"; } const index = machines.indexOf(machine); if (machine === REVOKED || index < 0) { return "lith lith-gone"; } return `lith lith-${index % PATTERNS}`; }; export const shortSha = (sha: string): string => sha ? sha.slice(0, SHORT_SHA) : "no commit"; export const spaceName = (space: string): string => space.startsWith(BRANCH_PREFIX) ? space.slice(BRANCH_PREFIX.length) : space; export const fileId = (space: string, path: string): string => `${space}\n${path}`; export const changeKind = ( change: Change ): "added" | "deleted" | "modified" => { if (!change.base) { return "added"; } return change.hash ? "modified" : "deleted"; }; const UNITS: [Intl.RelativeTimeFormatUnit, number][] = [ ["year", 31_536_000_000], ["month", 2_592_000_000], ["day", 86_400_000], ["hour", 3_600_000], ["minute", 60_000], ]; const relative = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); const absolute = new Intl.DateTimeFormat("en-GB", { dateStyle: "medium", timeStyle: "short", }); export const ago = (time: number): string => { if (!time) { return "never"; } const delta = time - Date.now(); for (const [unit, size] of UNITS) { if (Math.abs(delta) >= size) { return relative.format(Math.round(delta / size), unit); } } return "just now"; }; export const exact = (time: number): string => absolute.format(time); const blobs = new Map>(); export const blob = (hash: string): Promise => { let pending = blobs.get(hash); if (!pending) { pending = api.bytes(`/blobs/${hash}`); blobs.set(hash, pending); } return pending; }; const decoder = new TextDecoder("utf-8", { fatal: true }); export const text = (bytes: Uint8Array): string | null => { if (bytes.includes(0)) { return null; } try { return decoder.decode(bytes); } catch { return null; } };