// @ts-check import fs from "node:fs"; import crypto from "node:crypto"; import { join, parse, relative } from "node:path"; import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js"; import { cwd, fsCachePath, supportedImageTypes, } from "../../utils/runtimeChecks.js"; const { fileTypeFromBuffer } = await import("file-type"); // Retry mechanism with exponential backoff async function retryWithBackoff(fn, retries = 3, baseDelay = 100) { for (let i = 0; i < retries; i++) { try { return await fn(); } catch (error) { if (i === retries - 1) { throw error; // Last attempt failed } // Check if it's a file system error that we should retry const isRetryableError = error.code === 'EBUSY' || error.code === 'ENOENT' || error.code === 'EPERM' || error.errno === -4094 || // UNKNOWN error on Windows error.message.includes('UNKNOWN: unknown error'); if (!isRetryableError) { throw error; // Don't retry non-transient errors } const delay = baseDelay * Math.pow(2, i); // Exponential backoff console.warn(`Retry attempt ${i + 1}/${retries} for file operation after ${delay}ms delay:`, error.message); await new Promise(resolve => setTimeout(resolve, delay)); } } } export default async function getResolvedSrc(src) { const token = crypto.createHash("md5").update(src).digest("hex"); let filepath = fsCachePath + token; const fileExists = await retryWithBackoff(() => { for (const type of supportedImageTypes) { const fileExists = fs.existsSync(filepath + `.${type}`); if (fileExists) { filepath += `.${type}`; return true; } } return false; }); if (!fileExists) { const buffer = Buffer.from(await (await fetch(src)).arrayBuffer()); const { ext } = (await fileTypeFromBuffer(buffer)) || {}; throwErrorIfUnsupported(src, ext); filepath += `.${ext}`; // Use retry mechanism for file write operations await retryWithBackoff(() => { return new Promise((resolve, reject) => { try { fs.writeFileSync(filepath, buffer); resolve(undefined); } catch (error) { reject(error); } }); }); } const base = /^https?:/.test(src) ? parse(new URL(src).pathname).name : undefined; src = join("/", relative(cwd, filepath)); return { src, base }; }