// Stable server-side pagination โ€” Phase 4.1 ยง16. Shared by every list endpoint that could // otherwise grow unbounded (audit log, attempt histories, assessment session history, scenario // analytics). Simple offset/limit, not cursor-based โ€” this admin/instructor scale (hundreds to // low thousands of rows, not a high-frequency infinite-scroll feed) doesn't need more than // that, and offset pagination is what every other query in this codebase already reads like. const DEFAULT_PAGE_SIZE = 50; const MAX_PAGE_SIZE = 200; // Reads ?page=&page_size= from a request, clamped to sane bounds โ€” never trusts the client for // an unbounded page_size (that would defeat the entire point). function parsePagination(req) { const page = Math.max(1, parseInt(req.query.page, 10) || 1); const pageSize = Math.min(MAX_PAGE_SIZE, Math.max(1, parseInt(req.query.page_size, 10) || DEFAULT_PAGE_SIZE)); return { page, pageSize, offset: (page - 1) * pageSize }; } function paginatedResponse(rows, total, { page, pageSize }) { return { items: rows, page, page_size: pageSize, total, total_pages: Math.max(1, Math.ceil(total / pageSize)) }; } module.exports = { parsePagination, paginatedResponse, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE };