import * as Errors from './Errors.js';
import { getUrl } from './internal/errors.js';
import * as promise from './internal/promise.js';
import * as internal from './internal/rpcTransport.js';
/**
 * Creates a HTTP JSON-RPC Transport from a URL.
 *
 * @example
 * ```ts twoslash
 * import { RpcTransport } from 'ox'
 *
 * const transport = RpcTransport.fromHttp('https://1.rpc.thirdweb.com')
 *
 * const blockNumber = await transport.request({ method: 'eth_blockNumber' })
 * // @log: '0x1a2b3c'
 * ```
 *
 * @param url - URL to perform the JSON-RPC requests to.
 * @param options - Transport options.
 * @returns HTTP JSON-RPC Transport.
 */
export function fromHttp(url, options = {}) {
    return internal.create({
        async request(body_, options_) {
            const { fetchFn = options.fetchFn ?? fetch, fetchOptions: fetchOptions_ = options.fetchOptions, timeout = options.timeout ?? 10_000, } = options_;
            const body = JSON.stringify(body_);
            const fetchOptions = typeof fetchOptions_ === 'function'
                ? await fetchOptions_(body_)
                : fetchOptions_;
            const response = await promise.withTimeout(({ signal }) => {
                const init = {
                    ...fetchOptions,
                    body,
                    headers: {
                        'Content-Type': 'application/json',
                        ...fetchOptions?.headers,
                    },
                    method: fetchOptions?.method ?? 'POST',
                    signal: fetchOptions?.signal ?? (timeout > 0 ? signal : null),
                };
                const request = new Request(url, init);
                return fetchFn(request);
            }, {
                timeout,
                signal: true,
            });
            const data = await (async () => {
                if (response.headers.get('Content-Type')?.startsWith('application/json'))
                    return response.json();
                return response.text().then((data) => {
                    try {
                        return JSON.parse(data || '{}');
                    }
                    catch (_err) {
                        if (response.ok)
                            throw new MalformedResponseError({
                                response: data,
                            });
                        return { error: data };
                    }
                });
            })();
            if (!response.ok)
                throw new HttpError({
                    body,
                    details: JSON.stringify(data.error) ?? response.statusText,
                    response,
                    url,
                });
            return data;
        },
    }, { raw: options.raw });
}
/** Thrown when a HTTP request fails. */
export class HttpError extends Errors.BaseError {
    constructor({ body, details, response, url, }) {
        super('HTTP request failed.', {
            details,
            metaMessages: [
                `Status: ${response.status}`,
                `URL: ${getUrl(url)}`,
                body ? `Body: ${JSON.stringify(body)}` : undefined,
            ],
        });
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'RpcTransport.HttpError'
        });
    }
}
/** Thrown when a HTTP response is malformed. */
export class MalformedResponseError extends Errors.BaseError {
    constructor({ response }) {
        super('HTTP Response could not be parsed as JSON.', {
            metaMessages: [`Response: ${response}`],
        });
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 'RpcTransport.MalformedResponseError'
        });
    }
}
//# sourceMappingURL=RpcTransport.js.map