import { windowParams, clampLimit, clampOffset, SORTABLE_KEYS } from "./query.js"; // Enums are rejected when present-but-invalid (400); numeric/date params are // leniently clamped so an odd page size still returns sensible data. const TYPES = ["core", "audit"]; const DIRS = ["asc", "desc"]; const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; export const isUuid = (s) => typeof s === "string" && UUID_RE.test(s); export function parseWindow(query = {}) { return windowParams(query); } const MAX_VERIFY_TARGETS = 200; const splitList = (v) => (typeof v === "string" ? v.split(",") : []).map((s) => s.trim()).filter(Boolean); // `ids` are event UUIDs (core tab); `auditIds` are audit records (audit tab, // where rows share an event UUID). The cap applies to both lists combined. export function parseVerifyTargets(query = {}) { const eventUuids = [...new Set(splitList(query.ids).filter(isUuid))].slice(0, MAX_VERIFY_TARGETS); const auditIds = [...new Set(splitList(query.auditIds).map(Number))] .filter((n) => Number.isInteger(n) && n > 0) .slice(0, Math.max(0, MAX_VERIFY_TARGETS - eventUuids.length)); return { eventUuids, auditIds }; } export function parseTransactionsQuery(query = {}) { const type = query.type ?? "core"; if (!TYPES.includes(type)) { return { ok: false, error: `type must be one of ${TYPES.join(", ")}` }; } let sort = query.sort; if (sort != null && !SORTABLE_KEYS.includes(sort)) { return { ok: false, error: `sort must be one of ${SORTABLE_KEYS.join(", ")}` }; } if (sort == null) sort = undefined; const dir = query.dir ?? "desc"; if (!DIRS.includes(dir)) { return { ok: false, error: `dir must be one of ${DIRS.join(", ")}` }; } const { from, to } = windowParams(query); const q = typeof query.q === "string" ? query.q.slice(0, 200) : ""; return { ok: true, value: { type, from, to, q, sort, dir, limit: clampLimit(query.limit), offset: clampOffset(query.offset), }, }; }