/** * lazy-render-server * Backend helpers for lazy-render * * Provides: * - Cursor-based pagination * - Batch optimization * - API performance helpers * - Caching utilities * - Database adapters */ export * from './adapters'; export * from './cursorPagination'; export { paginationMiddleware } from './middleware'; export { rateLimiter } from './middleware/rate-limiter'; export interface PaginationParams { page: number; limit: number; cursor?: string; sortBy?: string; sortOrder?: 'asc' | 'desc'; } export interface PaginatedResponse { data: T[]; pagination: { page: number; limit: number; total: number; totalPages: number; hasMore: boolean; nextCursor?: string; prevCursor?: string; }; meta?: { sortBy?: string; sortOrder?: 'asc' | 'desc'; timestamp: string; }; } /** * Calculate pagination metadata */ export function calculatePagination( items: T[], page: number, limit: number, total: number, options?: { sortBy?: string; sortOrder?: 'asc' | 'desc'; } ): PaginatedResponse { const totalPages = Math.ceil(total / limit); const hasMore = page < totalPages; return { data: items, pagination: { page, limit, total, totalPages, hasMore, nextCursor: hasMore ? String(page + 1) : undefined, prevCursor: page > 1 ? String(page - 1) : undefined }, meta: options ? { sortBy: options.sortBy, sortOrder: options.sortOrder, timestamp: new Date().toISOString() } : undefined }; } /** * Validate pagination parameters */ export function validatePaginationParams(params: PaginationParams): { page: number; limit: number; cursor?: string; sortBy?: string; sortOrder: 'asc' | 'desc'; errors: string[]; } { const errors: string[] = []; // Validate page const page = parseInt(String(params.page)) || 1; if (page < 1) { errors.push('Page must be greater than 0'); } // Validate limit let limit = parseInt(String(params.limit)) || 50; if (limit < 1) { errors.push('Limit must be greater than 0'); limit = 50; } if (limit > 1000) { errors.push('Limit cannot exceed 1000 items per page'); limit = 1000; } // Validate sort order const sortOrder: 'asc' | 'desc' = params.sortOrder === 'asc' ? 'asc' : 'desc'; return { page: page < 1 ? 1 : page, limit, cursor: params.cursor, sortBy: params.sortBy, sortOrder, errors }; } /** * Batch data into chunks */ export function batchData(data: T[], batchSize: number): T[][] { const batches: T[][] = []; for (let i = 0; i < data.length; i += batchSize) { batches.push(data.slice(i, i + batchSize)); } return batches; } /** * Cursor-based pagination helper */ export function createCursor(page: number, limit: number): string { return Buffer.from(JSON.stringify({ page, limit })).toString('base64'); } /** * Decode cursor */ export function decodeCursor(cursor: string): { page: number; limit: number } | null { try { return JSON.parse(Buffer.from(cursor, 'base64').toString('utf-8')); } catch { return null; } } /** * Cache helper with TTL */ export class Cache { private cache: Map = new Map(); private defaultTTL: number; constructor(defaultTTL: number = 60000) { // Default 1 minute this.defaultTTL = defaultTTL; } set(key: string, value: T, ttl?: number): void { const expiry = Date.now() + (ttl || this.defaultTTL); this.cache.set(key, { data: value, expiry }); } get(key: string): T | null { const item = this.cache.get(key); if (!item) return null; if (Date.now() > item.expiry) { this.cache.delete(key); return null; } return item.data; } delete(key: string): boolean { return this.cache.delete(key); } clear(): void { this.cache.clear(); } size(): number { return this.cache.size; } } export default { calculatePagination, validatePaginationParams, batchData, createCursor, decodeCursor, Cache };"export { AIStreamingServer } from './AIStreamingServer'; export type { AIStreamConfig, StreamMessage, ConversationContext } from './AIStreamingServer';"