UNPKG

@ai2070/l0

Version:

L0: The Missing Reliability Substrate for AI

168 lines 5.35 kB
import { RETRY_DEFAULTS } from "../types/retry"; export function exponentialBackoff(attempt, baseDelay = RETRY_DEFAULTS.baseDelay, maxDelay = RETRY_DEFAULTS.maxDelay) { const rawDelay = baseDelay * Math.pow(2, attempt); const delay = Math.min(rawDelay, maxDelay); return { delay, cappedAtMax: rawDelay > maxDelay, rawDelay, }; } export function linearBackoff(attempt, baseDelay = RETRY_DEFAULTS.baseDelay, maxDelay = RETRY_DEFAULTS.maxDelay) { const rawDelay = baseDelay * (attempt + 1); const delay = Math.min(rawDelay, maxDelay); return { delay, cappedAtMax: rawDelay > maxDelay, rawDelay, }; } export function fixedBackoff(baseDelay = RETRY_DEFAULTS.baseDelay) { return { delay: baseDelay, cappedAtMax: false, rawDelay: baseDelay, }; } export function fixedJitterBackoff(baseDelay = RETRY_DEFAULTS.baseDelay, maxDelay = RETRY_DEFAULTS.maxDelay) { const jitter = Math.random() * baseDelay * 0.5; const rawDelay = baseDelay + jitter; const delay = Math.min(Math.floor(rawDelay), maxDelay); return { delay, cappedAtMax: rawDelay > maxDelay, rawDelay, }; } export function fullJitterBackoff(attempt, baseDelay = RETRY_DEFAULTS.baseDelay, maxDelay = RETRY_DEFAULTS.maxDelay) { const exponential = baseDelay * Math.pow(2, attempt); const cappedExponential = Math.min(exponential, maxDelay); const rawDelay = Math.random() * cappedExponential; const delay = Math.floor(rawDelay); return { delay, cappedAtMax: exponential > maxDelay, rawDelay, }; } export function decorrelatedJitterBackoff(attempt, baseDelay = RETRY_DEFAULTS.baseDelay, maxDelay = RETRY_DEFAULTS.maxDelay, previousDelay) { const prev = previousDelay ?? baseDelay * Math.pow(2, attempt); const rawDelay = Math.random() * (prev * 3 - baseDelay) + baseDelay; const delay = Math.min(Math.floor(rawDelay), maxDelay); return { delay, cappedAtMax: rawDelay > maxDelay, rawDelay, }; } export function calculateBackoff(strategy, attempt, baseDelay = RETRY_DEFAULTS.baseDelay, maxDelay = RETRY_DEFAULTS.maxDelay) { switch (strategy) { case "exponential": return exponentialBackoff(attempt, baseDelay, maxDelay); case "linear": return linearBackoff(attempt, baseDelay, maxDelay); case "fixed": return fixedBackoff(baseDelay); case "full-jitter": return fullJitterBackoff(attempt, baseDelay, maxDelay); case "fixed-jitter": return fixedJitterBackoff(baseDelay, maxDelay); default: return exponentialBackoff(attempt, baseDelay, maxDelay); } } export function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } export function timeout(ms, message = "Timeout") { return new Promise((_, reject) => { setTimeout(() => reject(new Error(message)), ms); }); } export async function withTimeout(promise, timeoutMs, timeoutMessage) { return Promise.race([promise, timeout(timeoutMs, timeoutMessage)]); } export class Timer { startTime; endTime; pauseTime; totalPausedTime = 0; start() { this.startTime = Date.now(); this.endTime = undefined; this.pauseTime = undefined; this.totalPausedTime = 0; } pause() { if (!this.startTime || this.pauseTime) return; this.pauseTime = Date.now(); } resume() { if (!this.pauseTime) return; this.totalPausedTime += Date.now() - this.pauseTime; this.pauseTime = undefined; } stop() { if (!this.startTime) return; if (this.pauseTime) { this.resume(); } this.endTime = Date.now(); } elapsed() { if (!this.startTime) return 0; const end = this.endTime ?? Date.now(); const paused = this.pauseTime ? this.totalPausedTime + (Date.now() - this.pauseTime) : this.totalPausedTime; return end - this.startTime - paused; } reset() { this.startTime = undefined; this.endTime = undefined; this.pauseTime = undefined; this.totalPausedTime = 0; } isRunning() { return !!this.startTime && !this.endTime && !this.pauseTime; } isPaused() { return !!this.pauseTime; } } export function debounce(fn, delay) { let timeoutId = null; return (...args) => { if (timeoutId) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { fn(...args); timeoutId = null; }, delay); }; } export function throttle(fn, delay) { let lastCall = 0; let timeoutId = null; return (...args) => { const now = Date.now(); const timeSinceLastCall = now - lastCall; if (timeSinceLastCall >= delay) { lastCall = now; fn(...args); } else if (!timeoutId) { timeoutId = setTimeout(() => { lastCall = Date.now(); fn(...args); timeoutId = null; }, delay - timeSinceLastCall); } }; } //# sourceMappingURL=timers.js.map