import { t } from "./i18n/instance.js";
import { assert, check, delay } from "./utils.browser.js";
import { drop_request_headers } from "./net.common.js";
export * from "./net.common.js";
async function fetch_retry(url, options, timeout, retries = 0, count = 0) {
    try {
        // 旧的浏览器可能没有这个函数
        if (AbortSignal.timeout)
            options.signal = AbortSignal.timeout(timeout);
        return await fetch(url, options);
    }
    catch (error) {
        if (count >= retries ||
            error.name !== 'TimeoutError' && !['ECONNRESET', 'ETIMEDOUT', 'ESOCKETTIMEDOUT'].includes(error.cause?.code))
            throw error;
        else {
            const duration = 2 ** count;
            console.log(`${t('等待 {{duration}} 秒后重试 fetch ({{_count}}) …', { duration, _count: count })}  ${url.toString()}`);
            await delay(1000 * duration);
            return fetch_retry(url, options, timeout, retries, count + 1);
        }
    }
}
export async function request(url, options = {}) {
    const { queries, headers: _headers, body, type = 'application/json', encoding, timeout = 5 * 1000, auth, cors, raw = false, full = false, credentials = 'include', } = options;
    let { method, retries, } = options;
    url = new URL(url, location.href);
    if (queries)
        for (const key in queries) {
            let value = queries[key];
            if (typeof value === 'boolean')
                value = value ? '1' : '0';
            url.searchParams.append(key, value);
        }
    if (body !== undefined && !method)
        method = 'POST';
    if (retries === true)
        retries = 2;
    // --- headers, http/2 开始都用小写的 headers
    let headers = new Headers();
    if (body !== undefined)
        headers.set('content-type', type);
    if (auth)
        headers.set('authorization', auth.type === 'basic' ? `Basic ${`${auth.username}:${auth.password}`.to_base64()}` : `Bearer ${auth.token}`);
    if (_headers)
        if (_headers instanceof Headers)
            // @ts-ignore: ts 类型不支持，实际上已经有了
            for (const [key, value] of _headers) {
                if (!key.startsWith(':') && !key.startsWith('sec-') && !drop_request_headers.has(key))
                    headers.set(key, value);
            }
        else
            for (const key in _headers)
                if (!key.startsWith(':') && !key.startsWith('sec-') && !drop_request_headers.has(key)) { // 可能在 http/2 的 response 中会有这样开头的保留 headers, 在透传时忽略比较好
                    assert(key === key.toLowerCase(), `传入 request 的 headers 参数中 key 应该都是小写的，实际为 ${key}`);
                    headers.set(key, _headers[key]);
                }
    let fetch_options = {
        ...method ? { method } : {},
        keepalive: true,
        redirect: 'follow',
        credentials,
        ...cors ? { mode: 'cors' } : {},
        headers,
        // --- body
        body: get_request_body(body, type),
    };
    let response;
    try {
        response = await fetch_retry(url, fetch_options, timeout, retries);
        if (!response.ok)
            throw Object.assign(new Error(t('状态码 {{status}}, 非 2xx: {{url}}', { status: response.status, url })), { name: 'StatusCodeError' });
    }
    catch (error) {
        ;
        error.url = url;
        error.options = options;
        if (response)
            error.response = {
                status: response.status,
                headers: response.headers,
                body: await response.text(),
            };
        throw error;
    }
    if (raw)
        return response;
    const body_ = await get_response_body(response, encoding);
    return full ? {
        status: response.status,
        headers: response.headers,
        body: body_
    }
        :
            body_;
}
function get_request_body(body, type) {
    if (body === undefined)
        return;
    if (type === 'application/json') {
        return typeof body === 'string' ||
            ArrayBuffer.isView(body) ||
            body instanceof ArrayBuffer ||
            body instanceof Blob ?
            body
            :
                JSON.stringify(body);
    }
    if (type === 'application/x-www-form-urlencoded')
        return body instanceof URLSearchParams ? body : new URLSearchParams(body);
    check(type === 'multipart/form-data');
    if (body instanceof FormData)
        return body;
    let form = new FormData();
    for (const key in body) {
        let value = body[key];
        form.set(key, value);
    }
    return form;
}
async function get_response_body(response, encoding) {
    if (!response.body)
        return encoding === 'binary' ? new ArrayBuffer(0) : '';
    if (encoding === 'binary')
        return response.arrayBuffer();
    return response.text();
}
/** 发起 http 请求并将响应体作为 json 解析 */
export async function request_json(url, options) {
    const body = await request(url, options);
    if (!body)
        return;
    try {
        return JSON.parse(body);
    }
    catch (error) {
        console.error(body);
        throw error;
    }
}
//# sourceMappingURL=net.browser.js.map