@ai2070/l0
Version:
L0: The Missing Reliability Substrate for AI
168 lines • 5.35 kB
JavaScript
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