"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.randomBackoffRetry = randomBackoffRetry;
exports.randomBackoffStrategy = randomBackoffStrategy;
/**
 * @param fn Function to Apply the retry logic.
 * @param maxRetries How many time to try. Max Retry is 5.
 * @returns
 */
function randomBackoffRetry(fn_1) {
    return __awaiter(this, arguments, void 0, function* (fn, options = {}) {
        const { maxRetries = 5, baseDelay = 4000, maxDelay = 60000, jitter = 0.8, handleError = isRetryError, } = options;
        let lastError;
        for (let attempt = 0; attempt < maxRetries; attempt++) {
            try {
                return yield fn();
            }
            catch (err) {
                lastError = err;
                if (attempt === maxRetries - 1)
                    throw err;
                if (!handleError(err))
                    throw err;
                const tempDelay = Math.min(baseDelay * 2 ** attempt, maxDelay);
                const delay = tempDelay * (1 + Math.random() * jitter);
                yield new Promise((res) => setTimeout(res, delay));
            }
        }
        throw lastError;
    });
}
function randomBackoffStrategy(props) {
    const { count, error } = props;
    if (count > 5) {
        throw error;
    }
    if (isRetryError(error) === false) {
        throw error;
    }
    const baseDelay = 4000;
    const maxDelay = 60000;
    const jitter = 0.8;
    const tempDelay = Math.min(baseDelay * 2 ** count, maxDelay);
    const delay = tempDelay * (1 + Math.random() * jitter);
    return delay;
}
function isRetryError(error) {
    var _a, _b, _c, _d;
    // 1) Quota exceeded → No retry
    if ((error === null || error === void 0 ? void 0 : error.code) === "insufficient_quota" ||
        ((_a = error === null || error === void 0 ? void 0 : error.error) === null || _a === void 0 ? void 0 : _a.type) === "insufficient_quota") {
        return false;
    }
    // 2) 5xx / server_error → Retry
    if ((typeof (error === null || error === void 0 ? void 0 : error.status) === "number" && error.status >= 500) ||
        ((_b = error === null || error === void 0 ? void 0 : error.error) === null || _b === void 0 ? void 0 : _b.type) === "server_error") {
        return true;
    }
    // 3) HTTP 429
    if ((error === null || error === void 0 ? void 0 : error.status) === 429) {
        return true;
    }
    // 4) undici / network related
    const code = (error === null || error === void 0 ? void 0 : error.code) || ((_c = error === null || error === void 0 ? void 0 : error.cause) === null || _c === void 0 ? void 0 : _c.code);
    if ([
        "UND_ERR_SOCKET",
        "UND_ERR_CONNECT_TIMEOUT",
        "ETIMEDOUT",
        "ECONNRESET",
        "EPIPE",
    ].includes(code)) {
        return true;
    }
    // 5) fetch abort
    if ((error === null || error === void 0 ? void 0 : error.message) === "terminated" || (error === null || error === void 0 ? void 0 : error.name) === "AbortError") {
        return true;
    }
    if ((_d = error === null || error === void 0 ? void 0 : error.message) === null || _d === void 0 ? void 0 : _d.startsWith(`SyntaxError: Expected ',' or '}' after property value in JSON at position`)) {
        return true;
    }
    return false;
}
//# sourceMappingURL=backoffRetry.js.map