@bigmi/core
Version:
TypeScript library for Bitcoin apps.
178 lines (167 loc) • 5.23 kB
text/typescript
import { RpcRequestError } from '../errors/request.js'
import {
UrlRequiredError,
type UrlRequiredErrorType,
} from '../errors/transport.js'
import type { ErrorType } from '../errors/utils.js'
import { createTransport } from '../factories/createTransport.js'
import type { BtcRpcRequestFn, RpcSchema } from '../types/request.js'
import type {
CreateTransportErrorType,
Transport,
TransportConfig,
} from '../types/transport.js'
import { createBatchScheduler } from '../utils/createBatchScheduler.js'
import {
getHttpRpcClient,
type HttpRpcClientOptions,
type RpcRequest,
} from './getHttpRpcClient.js'
import { UTXOAPISchemaMethods } from './types.js'
export type HttpTransportConfig<
rpcSchema extends RpcSchema | undefined = undefined,
raw extends boolean = false,
> = {
/**
* Whether to enable Batch JSON-RPC.
* @link https://www.jsonrpc.org/specification#batch
*/
batch?:
| boolean
| {
/** The maximum number of JSON-RPC requests to send in a batch. @default 1_000 */
batchSize?: number | undefined
/** The maximum number of milliseconds to wait before sending a batch. @default 0 */
wait?: number | undefined
}
| undefined
/**
* Request configuration to pass to `fetch`.
* @link https://developer.mozilla.org/en-US/docs/Web/API/fetch
*/
fetchOptions?: HttpRpcClientOptions['fetchOptions'] | undefined
/** A callback to handle the response from `fetch`. */
onFetchRequest?: HttpRpcClientOptions['onRequest'] | undefined
/** A callback to handle the response from `fetch`. */
onFetchResponse?: HttpRpcClientOptions['onResponse'] | undefined
/** The key of the HTTP transport. */
key?: TransportConfig['key'] | undefined
/** Methods to include or exclude from executing RPC requests. */
methods?: TransportConfig['methods'] | undefined
/** The name of the HTTP transport. */
name?: TransportConfig['name'] | undefined
/** Whether to return JSON RPC errors as responses instead of throwing. */
raw?: raw | boolean | undefined
/** The max number of times to retry. */
retryCount?: TransportConfig['retryCount'] | undefined
/** The base delay (in ms) between retries. */
retryDelay?: TransportConfig['retryDelay'] | undefined
/** Typed JSON-RPC schema for the transport. */
rpcSchema?: rpcSchema | RpcSchema | undefined
/** The timeout (in ms) for the HTTP request. Default: 10_000 */
timeout?: TransportConfig['timeout'] | undefined
}
export type HttpTransport<
rpcSchema extends RpcSchema | undefined = undefined,
raw extends boolean = false,
> = Transport<
'http',
{
fetchOptions?: HttpTransportConfig['fetchOptions'] | undefined
url?: string | undefined
},
BtcRpcRequestFn<rpcSchema, raw>
>
export type HttpTransportErrorType =
| CreateTransportErrorType
| UrlRequiredErrorType
| ErrorType
/**
* @description Creates a HTTP transport that connects to a JSON-RPC API.
*/
export function http<
rpcSchema extends RpcSchema | undefined = undefined,
raw extends boolean = false,
>(
/** URL of the JSON-RPC API. Defaults to the chain's public RPC URL. */
url?: string | undefined,
config: HttpTransportConfig<rpcSchema, raw> = {}
): HttpTransport<rpcSchema, raw> {
const {
batch,
fetchOptions,
key = 'http',
methods = { exclude: UTXOAPISchemaMethods },
name = 'HTTP JSON-RPC',
onFetchRequest,
onFetchResponse,
retryDelay,
raw,
} = config
return ({ chain, retryCount: retryCount_, timeout: timeout_ }) => {
const { batchSize = 1000, wait = 0 } =
typeof batch === 'object' ? batch : {}
const retryCount = config.retryCount ?? retryCount_
const timeout = timeout_ ?? config.timeout ?? 10_000
const url_ = url || chain?.rpcUrls.default.http[0]
if (!url_) {
throw new UrlRequiredError()
}
const rpcClient = getHttpRpcClient(url_, {
fetchOptions,
onRequest: onFetchRequest,
onResponse: onFetchResponse,
timeout,
})
return createTransport(
{
key,
methods,
name,
async request({ method, params }) {
const body = { method, params }
const { schedule } = createBatchScheduler({
id: url_,
wait,
shouldSplitBatch(requests) {
return requests.length > batchSize
},
fn: (body: RpcRequest[]) =>
rpcClient.request({
body,
}),
sort: (a, b) => a.id - b.id,
})
const fn = async (body: RpcRequest) =>
batch
? schedule(body)
: [
await rpcClient.request({
body,
}),
]
const [{ error, result }] = await fn(body)
if (raw) {
return { error, result }
}
if (error) {
throw new RpcRequestError({
body,
error,
url: url_,
})
}
return result
},
retryCount,
retryDelay,
timeout,
type: 'http',
},
{
fetchOptions,
url: url_,
}
)
}
}