import { j as resolveTimerTimeoutMs } from "./number-coercion-CJQ8TR--.js";
import { n as retryAsync, t as resolveRetryConfig } from "./retry-BSIArBBz.js";
import "./number-runtime-DBLVDypr.js";
import { t as resolveFetch } from "./fetch-C0IyrVwh.js";
import "./fetch-runtime-hJGvq3C8.js";
import "./retry-runtime-DdapUPJ5.js";
import { n as parseRetryAfterHeaderSeconds, t as parseDiscordRetryAfterBodySeconds } from "./retry-after-67N9bXIn.js";
//#region extensions/discord/src/error-body.ts
const DISCORD_RESPONSE_BODY_SUMMARY_MAX_CHARS = 240;
function summarizeDiscordResponseBody(body, opts = {}) {
const summary = body.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, " ").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/ /gi, " ").replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/\s+/g, " ").trim();
if (!summary) return opts.emptyText;
return summary.slice(0, DISCORD_RESPONSE_BODY_SUMMARY_MAX_CHARS);
}
function isDiscordHtmlResponseBody(body, contentType) {
return /\bhtml\b/i.test(contentType ?? "") || /^\s*<!doctype\s+html\b/i.test(body) || /^\s*<html\b/i.test(body);
}
function isDiscordRateLimitResponseBody(body) {
const normalized = body.toLowerCase();
return normalized.includes("error 1015") || normalized.includes("cloudflare") || normalized.includes("rate limit");
}
//#endregion
//#region extensions/discord/src/api.ts
const DISCORD_API_BASE = "https://discord.com/api/v10";
const DISCORD_API_RETRY_DEFAULTS = {
attempts: 3,
minDelayMs: 500,
maxDelayMs: 5 * 6e4,
jitter: .1
};
const DISCORD_API_429_FALLBACK_RETRY_AFTER_SECONDS = 60;
function parseDiscordApiErrorPayload(text) {
const trimmed = text.trim();
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return null;
try {
const payload = JSON.parse(trimmed);
if (payload && typeof payload === "object") return payload;
} catch {
return null;
}
return null;
}
function parseRetryAfterSeconds(text, response) {
const retryAfter = parseDiscordRetryAfterBodySeconds(parseDiscordApiErrorPayload(text)?.retry_after);
if (retryAfter !== void 0) return retryAfter;
const header = response.headers.get("Retry-After");
if (!header) return;
return parseRetryAfterHeaderSeconds(header);
}
function formatRetryAfterSeconds(value) {
if (value === void 0 || !Number.isFinite(value) || value < 0) return;
return `${value < 10 ? value.toFixed(1) : Math.round(value).toString()}s`;
}
function formatDiscordApiErrorText(text, response) {
const trimmed = text.trim();
if (!trimmed) return;
const payload = parseDiscordApiErrorPayload(trimmed);
if (!payload) {
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return "unknown error";
const summary = summarizeDiscordResponseBody(trimmed);
if (isDiscordHtmlResponseBody(trimmed, response.headers.get("content-type"))) {
if (!summary) return response.status === 429 ? "rate limited by Discord upstream" : void 0;
return response.status === 429 ? `rate limited by Discord upstream: ${summary}` : summary;
}
return summary;
}
const message = typeof payload.message === "string" && payload.message.trim() ? payload.message.trim() : "unknown error";
const retryAfter = formatRetryAfterSeconds(parseDiscordRetryAfterBodySeconds(payload.retry_after));
return retryAfter ? `${message} (retry after ${retryAfter})` : message;
}
var DiscordApiError = class extends Error {
constructor(message, status, retryAfter) {
super(message);
this.status = status;
this.retryAfter = retryAfter;
}
};
function getDiscordApiRetryAfterMs(err, retryConfig) {
if (!(err instanceof DiscordApiError) || typeof err.retryAfter !== "number") return;
return Math.min(Math.max(0, err.retryAfter * 1e3), retryConfig.maxDelayMs);
}
function normalizeDiscordRequestBody(body, headers) {
if (body === void 0) return;
if (typeof body === "string" || body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams || body instanceof ArrayBuffer) return body;
headers.set("Content-Type", headers.get("Content-Type") ?? "application/json");
return JSON.stringify(body);
}
function resolveDiscordRequestSignal(options) {
if (options.signal || typeof options.timeoutMs !== "number") return options.signal;
return AbortSignal.timeout(resolveTimerTimeoutMs(options.timeoutMs, 1));
}
async function requestDiscord(path, token, options) {
const fetchImpl = resolveFetch(options?.fetcher ?? fetch);
if (!fetchImpl) throw new Error("fetch is not available");
const retryConfig = resolveRetryConfig(DISCORD_API_RETRY_DEFAULTS, options?.retry);
return retryAsync(async () => {
const headers = new Headers(options?.headers);
headers.set("Authorization", `Bot ${token}`);
const body = normalizeDiscordRequestBody(options?.body, headers);
const res = await fetchImpl(`${DISCORD_API_BASE}${path}`, {
method: options?.method ?? (body === void 0 ? "GET" : "POST"),
headers,
body,
signal: resolveDiscordRequestSignal(options ?? {})
});
const text = await res.text().catch(() => "");
if (!res.ok) {
const detail = formatDiscordApiErrorText(text, res);
const suffix = detail ? `: ${detail}` : "";
const retryAfter = res.status === 429 ? parseRetryAfterSeconds(text, res) ?? DISCORD_API_429_FALLBACK_RETRY_AFTER_SECONDS : void 0;
throw new DiscordApiError(`Discord API ${path} failed (${res.status})${suffix}`, res.status, retryAfter);
}
if (!text.trim()) return;
return JSON.parse(text);
}, {
...retryConfig,
label: options?.label ?? path,
shouldRetry: (err) => err instanceof DiscordApiError && err.status === 429,
retryAfterMs: (err) => getDiscordApiRetryAfterMs(err, retryConfig)
});
}
async function fetchDiscord(path, token, fetcher = fetch, options) {
return await requestDiscord(path, token, {
...options,
fetcher,
method: "GET"
});
}
//#endregion
export { summarizeDiscordResponseBody as a, isDiscordRateLimitResponseBody as i, fetchDiscord as n, requestDiscord as r, DiscordApiError as t };