Changelog
2.0.0
Breaking: Explicit Params
The request() and subscribe() APIs now take params as a single explicit argument instead of rest/spread parameters. This aligns the TypeScript API with the JSON-RPC 2.0 wire format and adds unambiguous support for both positional (array) and named (object) params.
// Before (1.x)
await transport.request('method', param1, param2)
await transport.subscribe('method', param1, (data) => { ... })
// After (2.0)
await transport.request('method', [param1, param2])
await transport.subscribe('method', [param1], (data) => { ... })
// Named params (new)
await transport.request('daemon.passthrough', { method: 'foo', params: [] })Migration
request(method, ...params)→request(method, params?)— wrap positional args in an array. No-arg calls likerequest('server.ping')are unchanged.subscribe(method, ...params, callback)→subscribe(method, params, callback)— params is always the second argument, callback is always the third.SchemaEntry.params— now acceptsunknown[] | Record<string, unknown>to support named params.
Other changes
- Removed the auto-unwrap heuristic in WebSocket and TCP transports that guessed whether a single object argument was named params. The params argument now maps directly to the JSON-RPC
paramsfield — no ambiguity. daemon.passthrough— Uses plain object params naturally inElectrumCashSchema.
1.0.3
Subscription Dispatch Chain
Subscription notification handlers are now serialized via a dispatchChain on each subscription entry, preventing concurrent handler execution and ensuring notifications are processed in order.
- WebSocket & TCP transports — Notification handlers are dispatched through a promise chain, so each notification waits for the previous one's handlers to complete before firing.
- Error isolation — A failing handler no longer breaks the dispatch chain for the subscription. Each handler is individually caught.
- Graceful cleanup —
unsubscribe()andclose()now await in-flight dispatch chains, ensuring all pending handlers complete before teardown.
1.0.2
Batch Auto-Disable
When a server can't handle batch requests (e.g. the batch is too large or the server doesn't support batching), the BatchScheduler now automatically falls back to sending requests individually and temporarily disables batching. After a cooldown period (default: 5 seconds), batching is re-enabled.
BatchScheduler— AddedsendSingle,isBatchRejection, anddisabledCooldownoptions. Addeddisabledproperty.- WebSocket & TCP transports — Now provide a
sendSinglefallback toBatchScheduler, enabling transparent auto-disable on batch rejection. parse()— AddeddisabledCooldownquery parameter support (e.g.wss://example.com?batchSize=10&disabledCooldown=10000).BatchConfig— AddeddisabledCooldownoption.bump.mjs— Skip package directories withoutpackage.json.
Detection
The following errors trigger auto-disable by default:
- Batch timeout — server couldn't process the batch in time
- Parse error (JSON-RPC code
-32700) - Invalid request (JSON-RPC code
-32600)
Custom detection can be provided via the isBatchRejection option.
1.0.0
Initial release.
Core
Transportinterface withrequest(),subscribe(),connect(),close()- Spread-style parameters —
request('method', param1, param2)instead of array wrapping - Schema-based generics for type-safe method names, parameters, and return types
BatchSchedulerfor automatic request batching with configurable batch size and wait timewithRetry()utility with exponential backoffparse()for creating transports from URL strings (supports nested meta-transports and query-string options)createParse()for building custom parse functions with overridden package mapscreateParseSync()for building synchronous parse functions using pre-imported factory functionsElectrumCashSchematype definitions (protocol v1.5 and v1.6)EthereumSchematype definitions (EIP-1474 standard methods)
Transports
- WebSocket — Full-duplex communication with subscriptions, keep-alive, reconnection, connection pooling
- TCP — Newline-delimited JSON-RPC with TLS support, keep-alive, reconnection (Node.js)
- HTTP — Stateless JSON-RPC over HTTP POST with custom headers, fetch options, and raw mode
All transports support lazy connections — no resources are allocated until the first request(), subscribe(), or connect() call.
Meta-Transports
- Fallback — Automatic failover across multiple transports with optional health-based ranking
- Default
shouldThrowstops fallback on deterministic JSON-RPC errors (parse error, invalid request, invalid params) eagerConnectprioritizes the fastest-connecting transportonScoreslistener andscoresproperty for monitoring transport healthonResponsehook for observing requests across all transports
- Default
- Cluster — m-of-n quorum consensus with deep-equality response matching
onResponsehook for observing individual transport responses
- Single-element passthrough —
fallback([t])andcluster([t])return the input transport directly
Electrum Cash Variants
Protocol-specific subpath imports (@rpckit/*/electrum-cash) with pre-configured defaults:
server.versionhandshake with configurableclientNameandprotocolVersionserver.pingkeep-alivesubscribe/unsubscribemethod conventionServer-VersionHTTP header- Fallback variant with
server.pinghealth probing and protocol-awareshouldThrow(retries transient server errors like OOM, warmup, syncing)
Ethereum Variants
Protocol-specific subpath imports (@rpckit/*/ethereum) for Ethereum JSON-RPC:
eth_subscriptionnotification routing by subscription IDeth_unsubscribecalled automatically on cleanup- Subscription ID suppressed from callbacks (handled internally)
- Custom
parse()function for Ethereum transports
Features
- Automatic request batching with configurable batch size and wait time
- Subscription support with automatic resubscription on reconnect
- Handshake re-execution on reconnect (WebSocket and TCP)
- Connection pooling with ref counting for WebSocket and TCP transports
- Retry with exponential backoff on all transports
- Raw mode for HTTP transport (return full JSON-RPC envelopes instead of throwing on error)
- Request/response hooks on HTTP transport
- Socket access via
getSocket()andgetSocketAsync()on WebSocket and TCP transports - Subscription sharing — multiple listeners on the same method+params share one server subscription
- Smart unsubscribe — server unsubscribe only sent when last listener removes
- Fresh data for new subscribers — new listeners receive most recent notification, not stale initial result
- Race condition prevention — concurrent subscription calls safely coalesce
notificationFiltercallback for protocol-specific notification routingtransformInitialResultoption to normalize or suppress initial subscription results- HTTP transport includes response body in error messages for better debugging