Skip to content

eric8810/catcher

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

214 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Catcher 🪤

npm version pub version crates.io CI License: 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)

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)

Rust crates

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 Coverage

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

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)

Package Version Description
@eric8810/catcher-core npm Shared TS type definitions
@eric8810/catcher-http npm HTTP + SSE client — retry, CB, queue, interceptors
@eric8810/catcher-ws npm WebSocket — reconnect, multi-endpoint, codec
@eric8810/catcher-web npm Browser HTTP + SSE client — fetch-based
@eric8810/catcher-napi-http npm ⭐ Rust native HTTP + SSE (typed wrappers)
@eric8810/catcher-napi-ws npm ⭐ Rust native WS (typed wrappers)

pub.dev

Package Version Description
catcher_core pub Flutter dart:ffi bindings

Rust (crates.io)

Crate Version Description
catcher-core crates.io Shared types & errors
catcher-dns crates.io Shared DNS cache, host mapping, stale fallback
catcher-http crates.io HTTP — reqwest, retry, CB
catcher-ws crates.io WS — yawc, permessage-deflate, codec
catcher-ffi crates.io cdylib umbrella — 25 C ABI symbols
catcher-uniffi crates.io UniFFI → 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.17

Usage

napi (Rust native) ⭐ 推荐

// 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' }))

TypeScript (pure TS)

// 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();
}

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

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

Documentation

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

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

# Rust
cd 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

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors