UNPKG

safeer-pdf-generator

Version:

Framework-agnostic PDF generation library with chunking, merging, S3 upload, and email delivery

145 lines 4.1 kB
/** * High-resolution timer for performance measurement */ export class Timer { constructor() { this.marks = new Map(); this.startTime = process.hrtime(); } /** * Mark a point in time with a label */ mark(label) { this.marks.set(label, process.hrtime(this.startTime)); } /** * Get elapsed time since timer creation in milliseconds */ elapsed() { const [seconds, nanoseconds] = process.hrtime(this.startTime); return seconds * 1000 + nanoseconds / 1e6; } /** * Get elapsed time since a specific mark in milliseconds */ elapsedSince(label) { const mark = this.marks.get(label); if (!mark) { throw new Error(`Mark "${label}" not found`); } const [seconds, nanoseconds] = process.hrtime(this.startTime); const currentTime = seconds * 1000 + nanoseconds / 1e6; const markTime = mark[0] * 1000 + mark[1] / 1e6; return currentTime - markTime; } /** * Get time between two marks in milliseconds */ duration(startLabel, endLabel) { const startMark = this.marks.get(startLabel); const endMark = this.marks.get(endLabel); if (!startMark) throw new Error(`Start mark "${startLabel}" not found`); if (!endMark) throw new Error(`End mark "${endLabel}" not found`); const startTime = startMark[0] * 1000 + startMark[1] / 1e6; const endTime = endMark[0] * 1000 + endMark[1] / 1e6; return endTime - startTime; } /** * Get all marks with their timestamps */ getAllMarks() { const result = {}; for (const [label, mark] of this.marks) { result[label] = mark[0] * 1000 + mark[1] / 1e6; } return result; } /** * Reset the timer */ reset() { this.startTime = process.hrtime(); this.marks.clear(); } } /** * Simple sleep function for delays */ export function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Timeout wrapper for promises */ export function withTimeout(promise, timeoutMs, timeoutMessage) { return Promise.race([ promise, new Promise((_, reject) => { setTimeout(() => { reject(new Error(timeoutMessage || `Operation timed out after ${timeoutMs}ms`)); }, timeoutMs); }), ]); } /** * Retry function with exponential backoff */ export async function retry(fn, options = {}) { const { attempts = 3, delay = 1000, backoff = 2, shouldRetry = () => true } = options; let lastError; for (let attempt = 1; attempt <= attempts; attempt++) { try { return await fn(); } catch (error) { lastError = error; if (attempt === attempts || !shouldRetry(error, attempt)) { throw error; } const waitTime = delay * Math.pow(backoff, attempt - 1); await sleep(waitTime); } } throw lastError; } /** * Format duration in human-readable format */ export function formatDuration(ms) { if (ms < 1000) { return `${Math.round(ms)}ms`; } const seconds = ms / 1000; if (seconds < 60) { return `${seconds.toFixed(1)}s`; } const minutes = Math.floor(seconds / 60); const remainingSeconds = Math.round(seconds % 60); return `${minutes}m ${remainingSeconds}s`; } /** * Create a debounced version of a function */ export function debounce(func, wait) { let timeout; return ((...args) => { clearTimeout(timeout); timeout = setTimeout(() => func.apply(null, args), wait); }); } /** * Create a throttled version of a function */ export function throttle(func, limit) { let inThrottle; return ((...args) => { if (!inThrottle) { func.apply(null, args); inThrottle = true; setTimeout(() => (inThrottle = false), limit); } }); } //# sourceMappingURL=timing.js.map