safeer-pdf-generator
Version:
Framework-agnostic PDF generation library with chunking, merging, S3 upload, and email delivery
145 lines • 4.1 kB
JavaScript
/**
* 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