import { safeFetchJson, SOURCE_LIMITS } from './safe-fetch.js' /** Fully collect bounded cursor pages. Never accept an incomplete snapshot. */ export async function fetchEvobgpPages(base: string, token: string, path: string, limit: number): Promise[]> { const pages: Record[] = [] const seen = new Set() let cursor: string | undefined let elements = 0 let bytes = 0 const deadline = Date.now() + SOURCE_LIMITS.timeoutMs for (let page = 0; page < SOURCE_LIMITS.pages; page++) { if (Date.now() >= deadline) throw new Error('EvoBGP snapshot deadline exceeded') if (bytes >= SOURCE_LIMITS.totalBytes) throw new Error('EvoBGP snapshot byte limit exceeded') const url = new URL(`${base.replace(/\/$/, '')}${path}`) url.searchParams.set('limit', String(limit)) if (cursor) url.searchParams.set('cursor', cursor) const response = await safeFetchJson(url, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }, timeoutMs: deadline - Date.now(), maxBytes: Math.min(SOURCE_LIMITS.bytes, SOURCE_LIMITS.totalBytes - bytes), }) const data = response.data bytes += response.bytes if (bytes > SOURCE_LIMITS.totalBytes) throw new Error('EvoBGP snapshot byte limit exceeded') if (!data || typeof data !== 'object' || Array.isArray(data)) throw new Error('Invalid EvoBGP page') const body = data as Record const items = body.prefixes ?? body.items if (!Array.isArray(items)) throw new Error('Invalid EvoBGP page items') elements += items.length if (elements > SOURCE_LIMITS.elements) throw new Error('EvoBGP snapshot element limit exceeded') if (body.has_more !== undefined && typeof body.has_more !== 'boolean') throw new Error('Invalid EvoBGP has_more') if (body.next_cursor !== undefined && body.next_cursor !== null && (typeof body.next_cursor !== 'string' || body.next_cursor.length > 2048)) throw new Error('Invalid EvoBGP next_cursor') pages.push(body) if (body.has_more !== true && !body.next_cursor) return pages const next = body.next_cursor if (typeof next !== 'string' || !next || seen.has(next)) throw new Error('Incomplete or repeated EvoBGP cursor') seen.add(next) cursor = next } throw new Error('EvoBGP page limit exceeded') }