All files / api validation.ts

100% Statements 21/21
100% Branches 21/21
100% Functions 2/2
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49              188x 188x 19x   169x                     41x 41x   41x 41x   41x 41x 21x 21x 7x   14x     34x 34x 13x 13x 2x   11x     32x    
import type { Context } from "hono";
 
/**
 * Parse a route parameter as a positive integer.
 * Returns the number if valid, or a 400 Response if NaN/negative/zero.
 */
export function parseIntParam(c: Context, name: string, value: string): number | Response {
	const n = Number(value);
	if (!Number.isFinite(n) || n < 1 || n !== Math.floor(n)) {
		return c.json({ error: `Invalid ${name}: must be a positive integer` }, 400);
	}
	return n;
}
 
/**
 * Parse limit/offset query parameters with bounds enforcement.
 * Limit is clamped to [1, maxLimit] (default 200). Offset must be >= 0.
 */
export function parsePagination(
	c: Context,
	opts?: { defaultLimit?: number; maxLimit?: number },
): { limit: number; offset: number } | Response {
	const defaultLimit = opts?.defaultLimit ?? 50;
	const maxLimit = opts?.maxLimit ?? 200;
 
	const rawLimit = c.req.query("limit");
	const rawOffset = c.req.query("offset");
 
	let limit = defaultLimit;
	if (rawLimit !== undefined) {
		limit = Number(rawLimit);
		if (!Number.isFinite(limit) || limit < 1) {
			return c.json({ error: "limit must be a positive integer" }, 400);
		}
		limit = Math.min(Math.floor(limit), maxLimit);
	}
 
	let offset = 0;
	if (rawOffset !== undefined) {
		offset = Number(rawOffset);
		if (!Number.isFinite(offset) || offset < 0) {
			return c.json({ error: "offset must be a non-negative integer" }, 400);
		}
		offset = Math.floor(offset);
	}
 
	return { limit, offset };
}