Resilient network communication toolkit — Rust core, TypeScript wrappers, Flutter bindings, cross-platform.
"Catcher" — catches network failures before they reach your business logic.
⚠️ Breaking Changes (0.3.0+)
napi packages (Node.js native)
Change
Before
After
Entry point
client.js
dist/client.js
Config
JSON string only
Typed object or JSON string
Class names
JsHttpClient, JsWsClient
HttpClient, WsClient
Callback events
JSON strings
Typed objects (auto-parsed)
WS Message data
event.data
event.data_base64 (base64)
NAPI-RS types
—
camelCase fields (elapsedMs, timeoutMs, etc.)
TLS (wss://)
❌ napi-ws broken
✅ rustls built-in (v0.3.2)
Change
Before
After
BackoffKind::default()
Exponential
Fixed
RetryConfig::default().backoff
Exponential
Fixed
WS deflate_threshold
—
Renamed → deflate_threshold_bytes
WS max_message_size
—
Renamed → max_payload_bytes
WS ping_timeout_ms
—
Renamed → pong_timeout_ms
All config structs
snake_case only
snake_case + camelCase via #[serde(alias)]
See package-specific READMEs for detailed migration instructions.
Platform
Package
Status
Node.js (native)
@eric8810/catcher-napi-http / @eric8810/catcher-napi-ws
✅ ⭐ 推荐
Node.js (TS)
@eric8810/catcher-http / @eric8810/catcher-ws
✅ Published
Electron
same as Node.js
✅
Web
@eric8810/catcher-web
✅ Published
Rust
catcher-http / catcher-ws / catcher-core
✅ Published
Flutter
catcher_core (dart:ffi)
✅ Published
Android + iOS
catcher-uniffi (UniFFI)
✅ Published
catcher-core (Rust) @eric8810/catcher-core (TS)
│ │
▼ ┌───┴───┐
catcher-dns ▼ ▼
│ @eric8810 @eric8810
┌───┴───┐ /catcher- /catcher-
▼ ▼ http ws
catcher catcher (axios) (ws)
-http -ws (+SSE)
│ │ │ │
│ └──napi-rs──┐ ┌──napi-rs──┘
│ ▼ ▼
│ @eric8810/catcher-napi-http ⭐ Node.js 推荐
│ @eric8810/catcher-napi-ws ⭐ Node.js 推荐
│ (typed TS wrappers + .node)
│
│ ┌─────────────────────────────────┐
├───┤ catcher-ffi (cdylib umbrella) │
│ │ bridges catcher-http + ws │
│ │ exports 25 C ABI symbols │
│ └──────┬──────────┬───────────────┘
│ ▼ ▼
│ dart:ffi UniFFI
│ (Flutter) (Kotlin/Swift)
│ │ │
│ catcher_core catcher-uniffi
└─────────────────────────────────┘
┌─────────────────────┐
│ @eric8810/catcher-web│
│ (Browser, fetch) │
│ HTTP + SSE │
└─────────────────────┘
Package
Version
Description
catcher_core
Flutter dart:ffi bindings
Node.js (native — Rust via napi-rs) ⭐ 推荐
npm install @eric8810/catcher-napi-http @eric8810/catcher-napi-ws
Node.js (TS — pure TypeScript)
npm install @eric8810/catcher-http @eric8810/catcher-ws
npm install @eric8810/catcher-web
dependencies :
catcher_core : ^0.3.17
// HTTP — Rust native performance + typed config
import { HttpClient } from '@eric8810/catcher-napi-http'
const client = new HttpClient ( {
base_url : 'https://api.example.com' , // camelCase 也可以: baseUrl
connect_timeout_ms : 10000 ,
retry : { max_attempts : 3 , backoff : 'Fixed' } ,
circuit_breaker : { failure_threshold : 5 , reset_timeout_ms : 30000 } ,
dns : { cache_ttl_secs : 300 , stale_on_error : true } ,
msgpack : true , // auto JSON↔msgpack at transport layer
} )
const resp = await client . get ( '/users/1' )
console . log ( resp . status , resp . body . toString ( ) )
// SSE — auto-reconnect
import { SseClient } from '@eric8810/catcher-napi-http/sse'
const sse = new SseClient (
{ url : 'https://stream.example.com/events' ,
reconnect : { max_retries : 10 , initial_delay_ms : 1000 } } ,
( event ) => { if ( event . type === 'Line' ) console . log ( event . data ) } ,
)
// WebSocket — typed events
import { WsClient } from '@eric8810/catcher-napi-ws'
const ws = new WsClient (
{ urls : [ 'wss://cn.example.com' , 'wss://sg.example.com' ] ,
reconnect : { initial_delay_ms : 500 , max_delay_ms : 30000 } ,
dns : { cache_ttl_secs : 300 , stale_on_error : true } ,
msgpack : true } ,
( event ) => {
if ( event . type === 'Message' )
console . log ( Buffer . from ( event . data_base64 , 'base64' ) . toString ( ) )
} ,
)
ws . send ( JSON . stringify ( { event : 'hello' } ) )
// HTTP — one line to replace axios.create()
import { createHttpClient } from '@eric8810/catcher-http'
const client = createHttpClient ( {
baseURL : 'https://api.example.com' ,
keepAlive : true ,
retry : { attempts : 3 } ,
concurrency : 10 ,
circuitBreaker : { failureThreshold : 5 , resetTimeout : 30_000 } ,
} )
const data = await client . get ( '/users/1' )
const result = await client . post ( '/messages' , { text : 'hello' } )
// Per-request overrides
await client . get ( '/analytics' , { retry : false , timeout : 5000 } )
// Dynamic interceptors
client . interceptors . request . use ( config => {
config . headers [ 'Authorization' ] = `Bearer ${ token } `
return config
} )
// WebSocket — compression + reconnect + multi-endpoint
import { createResilientWS , pack , decodeWSMessage } from '@eric8810/catcher-ws'
const ws = createResilientWS ( {
url : [ 'wss://cn.example.com' , 'wss://sg.example.com' ] ,
perMessageDeflate : true ,
reconnect : { initialDelay : 1000 , maxDelay : 30_000 } ,
} )
ws . send ( pack ( { event : 'message' , data : msg } ) )
ws . addEventListener ( 'message' , e => console . log ( decodeWSMessage ( e . data ) ) )
// SSE — AI streaming (OpenAI compatible)
import { createSSEStream } from '@eric8810/catcher-http'
const stream = createSSEStream ( {
url : 'https://api.openai.com/v1/chat/completions' ,
method : 'POST' ,
headers : { Authorization : `Bearer ${ apiKey } ` } ,
body : { model : 'gpt-4' , messages : [ { role : 'user' , content : 'Hello' } ] , stream : true } ,
} )
for await ( const line of stream ) {
if ( ! line . startsWith ( 'data:' ) ) continue
const payload = line . startsWith ( 'data: ' ) ? line . slice ( 6 ) : line . slice ( 5 )
if ( payload === '[DONE]' ) break // business logic: handle termination yourself
process . stdout . write ( JSON . parse ( payload ) . choices [ 0 ] ?. delta ?. content ?? '' )
}
// loop ends = connection closed, no manual cleanup
// SSE — Rust (reqwest + tokio_stream)
// use catcher_http::sse::{SseStream, SseClientConfig};
// let config = SseClientConfig { url: "...".into(), method: SseMethod::POST, ..Default::default() };
// let mut stream = SseStream::connect(config).await?;
// while let Some(line) = stream.next().await {
// let line = line?;
// if let Some(payload) = line.strip_prefix("data: ") { println!("{}", payload); }
// }
// SSE — long-lived push with auto-reconnect
import { createSSEClient } from '@eric8810/catcher-http'
const client = createSSEClient ( {
url : 'https://api.example.com/events' ,
headers : { Authorization : 'Bearer xxx' } ,
reconnect : { initialDelay : 1000 , maxDelay : 30_000 } ,
} )
for await ( const line of client ) {
if ( line . startsWith ( 'data: ' ) ) console . log ( line . slice ( 6 ) )
}
// Flutter — HTTP via Rust FFI
import 'package:catcher_core/catcher_core.dart' ;
void main () async {
final client = CatcherHttpClient (HttpClientConfig (
baseUrl: 'https://httpbin.org' ,
retry: RetryConfig (maxAttempts: 3 ),
dns: DnsConfig (cacheTtlSecs: 300 , staleOnError: true ),
msgpack: true ,
));
final resp = await client.get ('/get' );
print ('Status: ${resp .status }, Body: ${resp .bodyAsString }' );
// POST
final created = await client.post ('/post' , body: {'key' : 'value' });
client.dispose ();
// WebSocket via Rust FFI
final ws = CatcherWsClient (WsClientConfig (
urls: ['wss://echo.example.com' ],
reconnect: WsReconnectConfig (initialDelayMs: 1000 ),
dns: DnsConfig (cacheTtlSecs: 300 , staleOnError: true ),
msgpack: true ,
));
ws.events.listen ((event) {
if (event is WsMessageEvent ) print ('Received: ${event .text }' );
});
ws.sendText ('{"event":"hello"}' );
await Future .delayed (Duration (seconds: 5 ));
ws.dispose ();
}
Shared HTTP Agent — TCP keep-alive, StaleAwareDnsResolver (676x cache hit speedup, stale-while-revalidate), TLS session reuse, idle socket eviction
Auto-retry — exponential backoff with jitter, destroys stale keepAlive sockets on retry
Circuit Breaker — trips on consecutive failures, auto-recovers, prevents retry storms
Resilient WebSocket — RFC 7692 permessage-deflate, application-layer compression (gzip/zstd), exponential reconnect with message buffering, multi-endpoint racing, heartbeat with fast pong-timeout detection
Server-Sent Events (SSE) — raw line stream, auto-reconnect, Last-Event-ID resume, AbortSignal, cross-platform (Rust + TS + Browser)
Binary codec — built-in msgpack: true transport-level codec (10% wire savings), standalone pack/unpack via @eric8810/catcher-napi-ws/codec
Dart FFI feature parity — Flutter clients can configure DNS cache and enable native MessagePack through DnsConfig, HttpClientConfig.msgpack, WsClientConfig.dns, and WsClientConfig.msgpack
Priority queue — POST before prefetch, concurrency-aware scheduling
Dynamic interceptors — use/eject/clear at runtime, per-request retry/timeout/signal overrides
interceptors → retry → circuit breaker → concurrency queue → HTTP engine
Suite
Count
Status
TS Unit + Integration (http, ws, sse, web)
346/348
✅
TS E2E (scenarios + rust-vs-vanilla)
38/38
✅
Rust Unit — catcher-core
23/23
✅
Rust Unit — catcher-dns
12/12
✅
Rust Unit — catcher-http
118/118
✅
Rust Unit — catcher-ws
35/35
✅
Rust Integration — catcher-ws (compression)
5/5
✅
NAPI Integration (dns, http, ws, msgpack)
28/28
✅
NAPI E2E (rust-vs-vanilla S1-S8)
37/37
✅
NAPI Throughput Benchmark
14/14
✅
NAPI Chaos (S9-S16 + chaos)
11/11
✅
Rust FFI Integration (http + sse + codec)
17/17
✅
Dart Unit Tests
20/20
✅
Dart Integration (real FFI + httpbin.org)
8/8
✅
Directory
Content
docs/arch-ts/
TypeScript architecture — overview, module tree, per-module design
docs/arch-rs/
Rust architecture — cargo workspace, transport, FFI, resilience
docs/user-manual/
Platform usage guides — Node.js, Browser, Flutter
docs/test/
Test architecture — proxy, profiles, scenarios
docs/research/
Research — API gaps, platform support, strategy
docs/plan/
Implementation plan — phased milestones
docs/issues/
Bug tracker — code review findings
docs/showcase.html
Performance showcase page
pnpm install # install all dependencies
pnpm build # build all TS packages
pnpm test # run integration tests (vitest)
pnpm typecheck # type-check all TS packages
pnpm bench # run benchmarks
pnpm test:napi # NAPI integration tests
pnpm test:napi-bench # NAPI throughput benchmark
pnpm bench:napi # NAPI micro-benchmarks (agent + codec)
pnpm test:napi-chaos # NAPI chaos + extreme scenarios
# Rust
cd packages && cargo build
cd packages && cargo test
MIT
This project uses the following third-party libraries: