diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8396dc0..ccd4a12 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,15 +1,15 @@ { - "packages/catcher-core-ts": "0.3.18", - "packages/catcher-http-ts": "0.3.18", - "packages/catcher-ws-ts": "0.3.18", - "packages/catcher-web": "0.3.18", - "packages/catcher-napi-http": "0.3.18", - "packages/catcher-napi-ws": "0.3.18", - "packages/catcher-core": "0.3.18", - "packages/catcher-dns": "0.3.18", - "packages/catcher-http": "0.3.18", - "packages/catcher-ws": "0.3.18", - "packages/catcher-ffi": "0.3.18", - "packages/catcher-uniffi": "0.3.18", - "packages/catcher_core": "0.3.18" + "packages/catcher-core-ts": "0.3.19", + "packages/catcher-http-ts": "0.3.19", + "packages/catcher-ws-ts": "0.3.19", + "packages/catcher-web": "0.3.19", + "packages/catcher-napi-http": "0.3.19", + "packages/catcher-napi-ws": "0.3.19", + "packages/catcher-core": "0.3.19", + "packages/catcher-dns": "0.3.19", + "packages/catcher-http": "0.3.19", + "packages/catcher-ws": "0.3.19", + "packages/catcher-ffi": "0.3.19", + "packages/catcher-uniffi": "0.3.19", + "packages/catcher_core": "0.3.19" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a59a15..a53462c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ All notable changes to this project will be documented in this file. See [release-please](https://github.com/googleapis/release-please) for automated management. +## 0.3.19 (2026-08-13) + +> 修复 N-API HTTP 传输异常被压缩成 `GenericFailure`、导致上层无法识别失败阶段和最终重试原因的问题。 + +### 🐛 Bug Fixes + +- **结构化传输异常**:连接拒绝、DNS、TLS、连接超时、请求超时及其他传输失败均提供稳定的 `code`、`phase`、`retryable` 和白名单 `details`。 +- **保留重试根因**:`RETRY_EXHAUSTED` 记录实际执行总次数,并在 `details.lastError` 中保留最终结构化异常,不再只保存字符串。 +- **N-API 错误类型**:JavaScript 调用方收到导出的 `CatcherError` / `HttpError`,同时保留旧版 HTTP 文本错误兼容解析。 +- **安全诊断信息**:原生错误链序列化前移除请求 URL,避免查询参数和 token 泄漏。 +- **跨平台契约覆盖**:CI 在 Linux、macOS 和 Windows 上验证非法配置、连接拒绝、重试耗尽和请求超时契约。 + +### 📦 Packaging + +- 所有 npm、Rust、NAPI 和 Flutter 包统一同步到 `0.3.19`。 + ## 0.3.18 (2026-08-07) > 修复代理/VPN/网络路径变化后服务端返回 HTTP 421(Misdirected Request)时客户端持续失败的问题,并为 NAPI 调用方提供结构化 HTTP 状态错误。 diff --git a/docs/releases/0.3.19.md b/docs/releases/0.3.19.md new file mode 100644 index 0000000..9f5b896 --- /dev/null +++ b/docs/releases/0.3.19.md @@ -0,0 +1,29 @@ +# Catcher 0.3.19 + +发布日期:2026-08-13 + +## 重点修复 + +本版本修复 N-API HTTP 传输异常被折叠为 `GenericFailure` 的问题。上层现在可以直接依据稳定字段判断异常类型,而不需要解析平台相关的错误文本。 + +结构化异常字段包括: + +- `code`:例如 `CONNECTION_ERROR`、`REQUEST_TIMEOUT`、`RETRY_EXHAUSTED` +- `phase`:例如 `connect`、`request`、`response` +- `retryable`:当前异常是否适合重试 +- `details`:经过白名单限制的状态、超时、主机和原因信息 + +重试耗尽时,`details.attempts` 表示包含首次请求在内的实际执行总次数,`details.lastError` 保留最后一次请求的完整结构化异常。 + +## 安全与兼容性 + +- 原生错误链在跨越 N-API 边界前移除请求 URL,避免查询参数泄漏。 +- HTTP 状态错误继续以 `HttpError` 暴露 `status` 和 `body`。 +- 旧版原生 HTTP 文本错误仍保留兼容解析。 +- HTTP 421 的连接池恢复行为与 0.3.18 保持不变。 + +## 验证 + +- Rust workspace check、clippy 和测试 +- TypeScript build、typecheck 和测试 +- Linux、macOS、Windows N-API HTTP 错误契约测试 diff --git a/packages/Cargo.lock b/packages/Cargo.lock index b8112dc..a8c5d4b 100644 --- a/packages/Cargo.lock +++ b/packages/Cargo.lock @@ -324,7 +324,7 @@ dependencies = [ [[package]] name = "catcher-core" -version = "0.3.18" +version = "0.3.19" dependencies = [ "serde", "serde_json", @@ -333,7 +333,7 @@ dependencies = [ [[package]] name = "catcher-dns" -version = "0.3.18" +version = "0.3.19" dependencies = [ "catcher-core", "hickory-proto", @@ -349,7 +349,7 @@ dependencies = [ [[package]] name = "catcher-ffi" -version = "0.3.18" +version = "0.3.19" dependencies = [ "catcher-core", "catcher-http", @@ -362,7 +362,7 @@ dependencies = [ [[package]] name = "catcher-http" -version = "0.3.18" +version = "0.3.19" dependencies = [ "async-trait", "backon", @@ -395,7 +395,7 @@ dependencies = [ [[package]] name = "catcher-napi-http" -version = "0.3.18" +version = "0.3.19" dependencies = [ "base64", "bytes", @@ -413,7 +413,7 @@ dependencies = [ [[package]] name = "catcher-napi-ws" -version = "0.3.18" +version = "0.3.19" dependencies = [ "catcher-ws", "napi", @@ -425,14 +425,14 @@ dependencies = [ [[package]] name = "catcher-test-support" -version = "0.3.18" +version = "0.3.19" dependencies = [ "tokio", ] [[package]] name = "catcher-uniffi" -version = "0.3.18" +version = "0.3.19" dependencies = [ "catcher-core", "catcher-http", @@ -446,7 +446,7 @@ dependencies = [ [[package]] name = "catcher-ws" -version = "0.3.18" +version = "0.3.19" dependencies = [ "backon", "base64", diff --git a/packages/catcher-core-ts/package.json b/packages/catcher-core-ts/package.json index 1e27f24..0c17fe9 100644 --- a/packages/catcher-core-ts/package.json +++ b/packages/catcher-core-ts/package.json @@ -1,6 +1,6 @@ { "name": "@eric8810/catcher-core", - "version": "0.3.18", + "version": "0.3.19", "description": "Catcher shared type definitions — zero runtime dependencies", "type": "module", "main": "./dist/index.js", diff --git a/packages/catcher-core/Cargo.toml b/packages/catcher-core/Cargo.toml index 229d11f..a600384 100644 --- a/packages/catcher-core/Cargo.toml +++ b/packages/catcher-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "catcher-core" -version = "0.3.18" +version = "0.3.19" edition = "2021" description = "Shared core types and errors for catcher — zero I/O dependencies" license = "MIT" diff --git a/packages/catcher-dns/Cargo.toml b/packages/catcher-dns/Cargo.toml index 7bb6f08..99ac8c8 100644 --- a/packages/catcher-dns/Cargo.toml +++ b/packages/catcher-dns/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "catcher-dns" -version = "0.3.18" +version = "0.3.19" edition = "2021" description = "Shared DNS resolver and system proxy detection for catcher" license = "MIT" @@ -13,7 +13,7 @@ reqwest-resolver = ["dep:reqwest"] system-proxy = ["dep:proxy_cfg"] [dependencies] -catcher-core = { path = "../catcher-core", version = "0.3.18" } +catcher-core = { path = "../catcher-core", version = "0.3.19" } serde = { version = "1", features = ["derive"] } tokio = { version = "1", features = ["net", "rt", "time", "macros"] } parking_lot = "0.12" diff --git a/packages/catcher-ffi/Cargo.toml b/packages/catcher-ffi/Cargo.toml index bdef8dc..29a5561 100644 --- a/packages/catcher-ffi/Cargo.toml +++ b/packages/catcher-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "catcher-ffi" -version = "0.3.18" +version = "0.3.19" edition = "2021" description = "Unified C ABI library for catcher — links HTTP + WS + codec into a single .so/.dylib" license = "MIT" @@ -11,15 +11,15 @@ crate-type = ["cdylib", "lib"] name = "catcher_ffi" [dependencies] -catcher-core = { path = "../catcher-core", version = "0.3.18" } -catcher-http = { path = "../catcher-http", version = "0.3.18" } -catcher-ws = { path = "../catcher-ws", version = "0.3.18" } +catcher-core = { path = "../catcher-core", version = "0.3.19" } +catcher-http = { path = "../catcher-http", version = "0.3.19" } +catcher-ws = { path = "../catcher-ws", version = "0.3.19" } serde_json = "1" tokio = { version = "1", features = ["rt-multi-thread", "macros"] } [dev-dependencies] -catcher-core = { path = "../catcher-core", version = "0.3.18" } -catcher-http = { path = "../catcher-http", version = "0.3.18" } -catcher-ws = { path = "../catcher-ws", version = "0.3.18" } +catcher-core = { path = "../catcher-core", version = "0.3.19" } +catcher-http = { path = "../catcher-http", version = "0.3.19" } +catcher-ws = { path = "../catcher-ws", version = "0.3.19" } wiremock = "0.6" tokio-test = "0.4" diff --git a/packages/catcher-http-ts/package.json b/packages/catcher-http-ts/package.json index a5132d9..467173d 100644 --- a/packages/catcher-http-ts/package.json +++ b/packages/catcher-http-ts/package.json @@ -1,6 +1,6 @@ { "name": "@eric8810/catcher-http", - "version": "0.3.18", + "version": "0.3.19", "description": "Catcher HTTP client — resilient transport with retry, circuit breaker, priority scheduling", "type": "module", "main": "./dist/index.js", diff --git a/packages/catcher-http/Cargo.toml b/packages/catcher-http/Cargo.toml index 49dbab6..d7e3934 100644 --- a/packages/catcher-http/Cargo.toml +++ b/packages/catcher-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "catcher-http" -version = "0.3.18" +version = "0.3.19" edition = "2021" description = "HTTP client for catcher — resilient transport with retry, circuit breaker, priority scheduling" license = "MIT" @@ -15,8 +15,8 @@ napi = ["dep:napi", "dep:napi-derive"] system-proxy = ["catcher-dns/system-proxy"] [dependencies] -catcher-core = { path = "../catcher-core", version = "0.3.18" } -catcher-dns = { path = "../catcher-dns", version = "0.3.18", default-features = false } +catcher-core = { path = "../catcher-core", version = "0.3.19" } +catcher-dns = { path = "../catcher-dns", version = "0.3.19", default-features = false } # ── 异步运行时 ── tokio = { version = "1", features = ["sync", "time", "net", "io-util", "macros"] } diff --git a/packages/catcher-napi-http/Cargo.toml b/packages/catcher-napi-http/Cargo.toml index c2b9128..a6ccd93 100644 --- a/packages/catcher-napi-http/Cargo.toml +++ b/packages/catcher-napi-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "catcher-napi-http" -version = "0.3.18" +version = "0.3.19" edition = "2021" description = "catcher HTTP napi-rs Node.js native bindings" license = "MIT" @@ -9,8 +9,8 @@ license = "MIT" crate-type = ["cdylib"] [dependencies] -catcher-core = { path = "../catcher-core", version = "0.3.18" } -catcher-http = { path = "../catcher-http", version = "0.3.18", features = ["system-proxy"] } +catcher-core = { path = "../catcher-core", version = "0.3.19" } +catcher-http = { path = "../catcher-http", version = "0.3.19", features = ["system-proxy"] } napi = { version = "2", default-features = false, features = ["napi4", "tokio_rt", "serde-json"] } napi-derive = "2" serde = { version = "1", features = ["derive"] } diff --git a/packages/catcher-napi-http/package.json b/packages/catcher-napi-http/package.json index 1a1ac29..72d45db 100644 --- a/packages/catcher-napi-http/package.json +++ b/packages/catcher-napi-http/package.json @@ -1,6 +1,6 @@ { "name": "@eric8810/catcher-napi-http", - "version": "0.3.18", + "version": "0.3.19", "description": "catcher HTTP native addon for Node.js via napi-rs", "main": "dist/client.js", "types": "dist/client.d.ts", diff --git a/packages/catcher-napi-ws/Cargo.toml b/packages/catcher-napi-ws/Cargo.toml index 868e9ad..4a20472 100644 --- a/packages/catcher-napi-ws/Cargo.toml +++ b/packages/catcher-napi-ws/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "catcher-napi-ws" -version = "0.3.18" +version = "0.3.19" edition = "2021" description = "catcher WebSocket napi-rs Node.js native bindings" license = "MIT" @@ -9,7 +9,7 @@ license = "MIT" crate-type = ["cdylib"] [dependencies] -catcher-ws = { path = "../catcher-ws", version = "0.3.18", features = ["rustls-tls", "system-proxy"] } +catcher-ws = { path = "../catcher-ws", version = "0.3.19", features = ["rustls-tls", "system-proxy"] } napi = { version = "2", default-features = false, features = ["napi4", "tokio_rt", "serde-json"] } napi-derive = "2" serde_json = "1" diff --git a/packages/catcher-napi-ws/package.json b/packages/catcher-napi-ws/package.json index 79d4edb..892ff1d 100644 --- a/packages/catcher-napi-ws/package.json +++ b/packages/catcher-napi-ws/package.json @@ -1,6 +1,6 @@ { "name": "@eric8810/catcher-napi-ws", - "version": "0.3.18", + "version": "0.3.19", "description": "catcher WebSocket native addon for Node.js via napi-rs", "main": "dist/client.js", "types": "dist/client.d.ts", diff --git a/packages/catcher-test-support/Cargo.toml b/packages/catcher-test-support/Cargo.toml index 2ba65bb..587b0fd 100644 --- a/packages/catcher-test-support/Cargo.toml +++ b/packages/catcher-test-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "catcher-test-support" -version = "0.3.18" +version = "0.3.19" edition = "2021" publish = false diff --git a/packages/catcher-uniffi/Cargo.toml b/packages/catcher-uniffi/Cargo.toml index a674822..4f0eed8 100644 --- a/packages/catcher-uniffi/Cargo.toml +++ b/packages/catcher-uniffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "catcher-uniffi" -version = "0.3.18" +version = "0.3.19" edition = "2021" description = "UniFFI bindings for catcher — auto-generates Swift + Kotlin from Rust API" license = "MIT" @@ -11,9 +11,9 @@ crate-type = ["cdylib", "staticlib"] name = "catcher_uniffi" [dependencies] -catcher-core = { path = "../catcher-core", version = "0.3.18" } -catcher-http = { path = "../catcher-http", version = "0.3.18" } -catcher-ws = { path = "../catcher-ws", version = "0.3.18" } +catcher-core = { path = "../catcher-core", version = "0.3.19" } +catcher-http = { path = "../catcher-http", version = "0.3.19" } +catcher-ws = { path = "../catcher-ws", version = "0.3.19" } uniffi = { version = "0.28", features = ["cli"] } serde_json = "1" thiserror = "1" diff --git a/packages/catcher-web/package.json b/packages/catcher-web/package.json index 04c9dff..216d733 100644 --- a/packages/catcher-web/package.json +++ b/packages/catcher-web/package.json @@ -1,6 +1,6 @@ { "name": "@eric8810/catcher-web", - "version": "0.3.18", + "version": "0.3.19", "description": "Catcher HTTP client for browsers — fetch-based with retry, circuit breaker & priority queue", "type": "module", "main": "./dist/index.js", diff --git a/packages/catcher-ws-ts/package.json b/packages/catcher-ws-ts/package.json index 80f05e2..2b8fd32 100644 --- a/packages/catcher-ws-ts/package.json +++ b/packages/catcher-ws-ts/package.json @@ -1,6 +1,6 @@ { "name": "@eric8810/catcher-ws", - "version": "0.3.18", + "version": "0.3.19", "description": "Catcher WebSocket client — resilient reconnect, multi-endpoint racing, msgpack codec", "type": "module", "main": "./dist/index.js", diff --git a/packages/catcher-ws/Cargo.toml b/packages/catcher-ws/Cargo.toml index 435c8f5..0ef7a0e 100644 --- a/packages/catcher-ws/Cargo.toml +++ b/packages/catcher-ws/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "catcher-ws" -version = "0.3.18" +version = "0.3.19" edition = "2021" description = "WebSocket client for catcher — resilient reconnect, heartbeat, multi-endpoint racing" license = "MIT" @@ -12,8 +12,8 @@ rustls-tls = ["yawc/rustls-ring", "reqwest/rustls"] system-proxy = ["catcher-dns/system-proxy"] [dependencies] -catcher-core = { path = "../catcher-core", version = "0.3.18" } -catcher-dns = { path = "../catcher-dns", version = "0.3.18", features = ["reqwest-resolver"] } +catcher-core = { path = "../catcher-core", version = "0.3.19" } +catcher-dns = { path = "../catcher-dns", version = "0.3.19", features = ["reqwest-resolver"] } tokio = { version = "1", features = ["rt-multi-thread", "sync", "time", "net", "io-util", "macros"] } futures-util = "0.3" backon = "1" diff --git a/packages/catcher_core/ios/catcher_core.podspec b/packages/catcher_core/ios/catcher_core.podspec index 5b6fb9f..cb69deb 100644 --- a/packages/catcher_core/ios/catcher_core.podspec +++ b/packages/catcher_core/ios/catcher_core.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'catcher_core' - s.version = '0.3.18' + s.version = '0.3.19' s.summary = 'Resilient HTTP/WebSocket client backed by Rust core for Flutter.' s.description = <<-DESC Resilient HTTP/WebSocket client backed by Rust core for Flutter. diff --git a/packages/catcher_core/macos/catcher_core.podspec b/packages/catcher_core/macos/catcher_core.podspec index 7820a04..f0eab82 100644 --- a/packages/catcher_core/macos/catcher_core.podspec +++ b/packages/catcher_core/macos/catcher_core.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'catcher_core' - s.version = '0.3.18' + s.version = '0.3.19' s.summary = 'Resilient HTTP/WebSocket client backed by Rust core for Flutter.' s.description = <<-DESC Resilient HTTP/WebSocket client backed by Rust core for Flutter. diff --git a/packages/catcher_core/pubspec.yaml b/packages/catcher_core/pubspec.yaml index 31c975f..2044af9 100644 --- a/packages/catcher_core/pubspec.yaml +++ b/packages/catcher_core/pubspec.yaml @@ -1,5 +1,5 @@ name: catcher_core -version: 0.3.18 +version: 0.3.19 description: Resilient HTTP/WebSocket client backed by Rust core for Flutter repository: https://github.com/eric8810/catcher issue_tracker: https://github.com/eric8810/catcher/issues diff --git a/packages/catcher_core/rust/Cargo.lock b/packages/catcher_core/rust/Cargo.lock index 40e0aee..7ed3d86 100644 --- a/packages/catcher_core/rust/Cargo.lock +++ b/packages/catcher_core/rust/Cargo.lock @@ -164,7 +164,7 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "catcher-core" -version = "0.3.18" +version = "0.3.19" dependencies = [ "serde", "serde_json", @@ -173,7 +173,7 @@ dependencies = [ [[package]] name = "catcher-dns" -version = "0.3.18" +version = "0.3.19" dependencies = [ "catcher-core", "hickory-proto", @@ -187,7 +187,7 @@ dependencies = [ [[package]] name = "catcher-http" -version = "0.3.18" +version = "0.3.19" dependencies = [ "async-trait", "backon", @@ -215,7 +215,7 @@ dependencies = [ [[package]] name = "catcher-ws" -version = "0.3.18" +version = "0.3.19" dependencies = [ "backon", "base64", @@ -237,7 +237,7 @@ dependencies = [ [[package]] name = "catcher_core_ffi" -version = "0.3.18" +version = "0.3.19" dependencies = [ "catcher-http", "catcher-ws", diff --git a/packages/catcher_core/rust/Cargo.toml b/packages/catcher_core/rust/Cargo.toml index 0258e6d..6d364e9 100644 --- a/packages/catcher_core/rust/Cargo.toml +++ b/packages/catcher_core/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "catcher_core_ffi" -version = "0.3.18" +version = "0.3.19" edition = "2021" [workspace]