Spaces:
Running
Running
File size: 3,176 Bytes
4badc3b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import path from 'path';
import { config } from '../config.js';
/**
* Shared Utility Functions
*
* General-purpose helper functions used across multiple modules.
*/
/**
* Get the package version from package.json
* @param {string} [defaultVersion='1.0.0'] - Default version if package.json cannot be read
* @returns {string} The package version
*/
export function getPackageVersion(defaultVersion = '1.0.0') {
try {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const packageJsonPath = path.join(__dirname, '../../package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
return packageJson.version || defaultVersion;
} catch {
return defaultVersion;
}
}
/**
* Format duration in milliseconds to human-readable string
* @param {number} ms - Duration in milliseconds
* @returns {string} Human-readable duration (e.g., "1h23m45s")
*/
export function formatDuration(ms) {
const seconds = Math.floor(ms / 1000);
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (hours > 0) {
return `${hours}h${minutes}m${secs}s`;
} else if (minutes > 0) {
return `${minutes}m${secs}s`;
}
return `${secs}s`;
}
/**
* Sleep for specified milliseconds
* @param {number} ms - Duration to sleep in milliseconds
* @returns {Promise<void>} Resolves after the specified duration
*/
export function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Check if an error is a network error (transient)
* @param {Error} error - The error to check
* @returns {boolean} True if it is a network error
*/
export function isNetworkError(error) {
const msg = error.message.toLowerCase();
return (
msg.includes('fetch failed') ||
msg.includes('network error') ||
msg.includes('econnreset') ||
msg.includes('etimedout') ||
msg.includes('socket hang up') ||
msg.includes('timeout')
);
}
/**
* Throttled fetch that applies a configurable delay before each request
* Only applies delay when requestThrottlingEnabled is true
* @param {string|URL} url - The URL to fetch
* @param {RequestInit} [options] - Fetch options
* @returns {Promise<Response>} Fetch response
*/
export async function throttledFetch(url, options) {
if (config.requestThrottlingEnabled) {
const delayMs = config.requestDelayMs || 200;
if (delayMs > 0) {
await sleep(delayMs);
}
}
return fetch(url, options);
}
/**
* Generate random jitter for backoff timing (Thundering Herd Prevention)
* Prevents all clients from retrying at the exact same moment after errors.
* @param {number} maxJitterMs - Maximum jitter range (result will be ±maxJitterMs/2)
* @returns {number} Random jitter value between -maxJitterMs/2 and +maxJitterMs/2
*/
export function generateJitter(maxJitterMs) {
return Math.random() * maxJitterMs - (maxJitterMs / 2);
}
|