import { randomUUID } from "node:crypto"; import type { Context } from "hono"; import type { Db } from "../db/client.js"; import type { UserRole } from "../env.js"; import { can, type RbacAction } from "../auth/rbac.js"; import { errorJson } from "../http.js"; import type { AppBindings } from "../auth/middleware.js"; const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; export function isUuid(value: string): boolean { return UUID_RE.test(value); } export function requireCan(c: Context, action: RbacAction) { const user = c.get("user"); if (!can(user.role, action)) { return errorJson(c, 403, "FORBIDDEN", `Role ${user.role} is not allowed to ${action}.`); } return null; } export function caller(c: Context) { return c.get("user"); } export function iso(value: Date | string): string { return value instanceof Date ? value.toISOString() : new Date(value).toISOString(); } export function asString(value: unknown, fallback = ""): string { return typeof value === "string" ? value : fallback; } export function optionalString(value: unknown): string | null | undefined { if (value === undefined) return undefined; if (value === null) return null; if (typeof value === "string") return value; return undefined; } export function newId(): string { return randomUUID(); } export function parseJsonBody(c: Context): Promise> { return c.req.json>(); } export function asMoney(value: unknown): string | null { if (typeof value === "number" && Number.isFinite(value)) { return value.toFixed(2); } if (typeof value === "string" && /^\d+(\.\d{1,2})?$/.test(value.trim())) { return Number(value).toFixed(2); } return null; } export function moneyCents(value: string): number { return Math.round(Number(value) * 100); } export function isDateOnly(value: string): boolean { return /^\d{4}-\d{2}-\d{2}$/.test(value); } export async function rowsOf(handle: Db, table: unknown): Promise { return (await handle.db.select().from(table as never)) as T[]; } export async function firstById( handle: Db, table: unknown, id: string, ): Promise { const rows = await rowsOf(handle, table); return rows.find((row) => row.id === id); } export function isUniqueViolation(error: unknown): boolean { return ( typeof error === "object" && error !== null && "code" in error && (error as { code: unknown }).code === "23505" ); } export type { UserRole };