Skip to content

Latest commit

History

226 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Catcher 🪤

npm versionpub versioncrates.ioCILicense: MIT

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)

ChangeBeforeAfter
Entry pointclient.jsdist/client.js
ConfigJSON string onlyTyped object or JSON string
Class namesJsHttpClient, JsWsClientHttpClient, WsClient
Callback eventsJSON stringsTyped objects (auto-parsed)
WS Message dataevent.dataevent.data_base64 (base64)
NAPI-RS typescamelCase fields (elapsedMs, timeoutMs, etc.)
TLS (wss://)❌ napi-ws broken✅ rustls built-in (v0.3.2)

Rust crates

ChangeBeforeAfter
BackoffKind::default()ExponentialFixed
RetryConfig::default().backoffExponentialFixed
WS deflate_thresholdRenamed → deflate_threshold_bytes
WS max_message_sizeRenamed → max_payload_bytes
WS ping_timeout_msRenamed → pong_timeout_ms
All config structssnake_case onlysnake_case + camelCase via #[serde(alias)]

See package-specific READMEs for detailed migration instructions.

Platform Coverage

PlatformPackageStatus
Node.js (native)@eric8810/catcher-napi-http / @eric8810/catcher-napi-ws✅ ⭐ 推荐
Node.js (TS)@eric8810/catcher-http / @eric8810/catcher-ws✅ Published
Electronsame as Node.js
Web@eric8810/catcher-web✅ Published
Rustcatcher-http / catcher-ws / catcher-core✅ Published
Fluttercatcher_core (dart:ffi)✅ Published
Android + iOScatcher-uniffi (UniFFI)✅ Published

Architecture

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 │
└─────────────────────┘

Packages

npm (@eric8810 scope)

PackageVersionDescription
@eric8810/catcher-corenpmShared TS type definitions
@eric8810/catcher-httpnpmHTTP + SSE client — retry, CB, queue, interceptors
@eric8810/catcher-wsnpmWebSocket — reconnect, multi-endpoint, codec
@eric8810/catcher-webnpmBrowser HTTP + SSE client — fetch-based
@eric8810/catcher-napi-httpnpm⭐ Rust native HTTP + SSE (typed wrappers)
@eric8810/catcher-napi-wsnpm⭐ Rust native WS (typed wrappers)

pub.dev

PackageVersionDescription
catcher_corepubFlutter dart:ffi bindings

Rust (crates.io)

CrateVersionDescription
catcher-corecrates.ioShared types & errors
catcher-dnscrates.ioShared DNS cache, host mapping, stale fallback
catcher-httpcrates.ioHTTP — reqwest, retry, CB
catcher-wscrates.ioWS — yawc, permessage-deflate, codec
catcher-fficrates.iocdylib umbrella — 25 C ABI symbols
catcher-unifficrates.ioUniFFI → Swift + Kotlin

Quick Start

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

Browser

npm install @eric8810/catcher-web

Flutter

dependencies:
catcher_core: ^0.3.18

Usage

napi (Rust native) ⭐ 推荐

// HTTP — Rust native performance + typed configimport{HttpClient}from'@eric8810/catcher-napi-http'constclient=newHttpClient({base_url: 'https://api.example.com',// camelCase 也可以: baseUrlconnect_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})constresp=awaitclient.get('/users/1')console.log(resp.status,resp.body.toString())// SSE — auto-reconnectimport{SseClient}from'@eric8810/catcher-napi-http/sse'constsse=newSseClient({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 eventsimport{WsClient}from'@eric8810/catcher-napi-ws'constws=newWsClient({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'}))

TypeScript (pure TS)

// HTTP — one line to replace axios.create()import{createHttpClient}from'@eric8810/catcher-http'constclient=createHttpClient({baseURL: 'https://api.example.com',keepAlive: true,retry: {attempts: 3},concurrency: 10,circuitBreaker: {failureThreshold: 5,resetTimeout: 30_000},})constdata=awaitclient.get('/users/1')constresult=awaitclient.post('/messages',{text: 'hello'})// Per-request overridesawaitclient.get('/analytics',{retry: false,timeout: 5000})// Dynamic interceptorsclient.interceptors.request.use(config=>{config.headers['Authorization']=`Bearer ${token}`returnconfig})
// WebSocket — compression + reconnect + multi-endpointimport{createResilientWS,pack,decodeWSMessage}from'@eric8810/catcher-ws'constws=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'conststream=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},})forawait(constlineofstream){if(!line.startsWith('data:'))continueconstpayload=line.startsWith('data: ') ? line.slice(6) : line.slice(5)if(payload==='[DONE]')break// business logic: handle termination yourselfprocess.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-reconnectimport{createSSEClient}from'@eric8810/catcher-http'constclient=createSSEClient({url: 'https://api.example.com/events',headers: {Authorization: 'Bearer xxx'},reconnect: {initialDelay: 1000,maxDelay: 30_000},})forawait(constlineofclient){if(line.startsWith('data: '))console.log(line.slice(6))}
// Flutter — HTTP via Rust FFIimport'package:catcher_core/catcher_core.dart';
voidmain() 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}');
// POSTfinal created =await client.post('/post', body: {'key':'value'});
client.dispose();
// WebSocket via Rust FFIfinal 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 isWsMessageEvent) print('Received: ${event.text}');
});
ws.sendText('{"event":"hello"}');
awaitFuture.delayed(Duration(seconds:5));
ws.dispose();
}

Features

  • 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

Resilience Layers

interceptors → retry → circuit breaker → concurrency queue → HTTP engine

Test Results

SuiteCountStatus
TS Unit + Integration (http, ws, sse, web)346/348
TS E2E (scenarios + rust-vs-vanilla)38/38
Rust Unit — catcher-core23/23
Rust Unit — catcher-dns12/12
Rust Unit — catcher-http118/118
Rust Unit — catcher-ws35/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 Benchmark14/14
NAPI Chaos (S9-S16 + chaos)11/11
Rust FFI Integration (http + sse + codec)17/17
Dart Unit Tests20/20
Dart Integration (real FFI + httpbin.org)8/8

Documentation

DirectoryContent
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.htmlPerformance showcase page

Development

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# Rustcd packages && cargo build
cd packages && cargo test

License

MIT

Third-party licenses

This project uses the following third-party libraries:

About

solution for mobile networking

Resources

Stars

11 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages