You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implement SDK network resilience and endpoint diagnostics layer (#434)
* network: classify HTTP failures into typed NET_RATE_LIMITED/NET_UNREACHABLE errors
NetworkClient previously collapsed every non-2xx response into a generic
HTTP_ERROR_<status> code and every connection-level failure (ECONNREFUSED,
ENOTFOUND, ...) into a generic NETWORK_ERROR. NET_RATE_LIMITED and
NET_UNREACHABLE already existed in the published error registry
(src/errors/codes.ts) but nothing ever produced them.
- 429 responses now throw NET_RATE_LIMITED (retryable: true)
- 5xx responses and socket/DNS failures (ECONNREFUSED, ENOTFOUND, EAI_AGAIN,
ENETUNREACH, EHOSTUNREACH) now throw NET_UNREACHABLE (retryable: true)
- Other 4xx statuses keep the existing HTTP_ERROR_<status> code for
backward compatibility
- Adds checkEndpointReachability(url): a lightweight GET probe that reports
{ reachable, latencyMs, errorCode } without ever exposing response bodies
or headers — used by the new endpoint diagnostics probe
Refs #272 (Files: src/network/index.ts, +116/-8 lines)
* wallet: preserve FRIENDBOT_ERROR/FUND_ERROR mapping for new typed codes
fundTestnetAccount() remapped NetworkClient failures by checking
error.code.startsWith('HTTP_ERROR_'), which no longer matches 429/5xx
responses now that those throw NET_RATE_LIMITED/NET_UNREACHABLE. Extends
the check so Friendbot failures still surface as FRIENDBOT_ERROR, and
fixes a pre-existing bug where retryable/category/safeMessage were
silently dropped when the error was reconstructed (now passed through via
the object-form PocketPayError constructor instead of positional args).
External behaviour for existing consumers is unchanged: fund.test.ts
(23 tests, all pre-existing) still passes without modification.
Refs #272 (Files: src/wallet/index.ts, +7/-4 lines)
* diagnostics: add opt-in live endpoint reachability probe
Adds probeConfiguredEndpoints(), which checks the resolved Horizon and
Soroban RPC URLs with checkEndpointReachability() and returns only URLs,
latency, and typed error codes — never response bodies, headers, or
secrets.
Kept separate from buildDiagnosticsReport(), which stays a pure,
side-effect-free config snapshot. This is opt-in: call it explicitly (e.g.
from a support 'test connection' action) since it makes real network calls.
Refs #272 (Files: src/diagnostics/probe.ts [new, 45 lines],
src/diagnostics/index.ts, +3 lines)
* index: export the network client abstraction from the package root
NetworkClient, withTimeout, fetchWithTimeout, executeHorizonOperation,
executeSorobanOperation, and checkEndpointReachability were implemented
and used internally (e.g. by fundTestnetAccount) but never re-exported
from src/index.ts, so consumers had no public entry point to the 'network
client abstraction' required by issue #272. Also exports
probeConfiguredEndpoints and the EndpointDiagnostics/EndpointReachability
types.
Refs #272 (Files: src/index.ts, +11 lines)
* tests: cover timeout, rate-limit, unavailable endpoint, malformed config
- network-client.test.ts (12 tests, new): NetworkClient success path;
429 -> NET_RATE_LIMITED; 503 -> NET_UNREACHABLE; other 4xx keeps
HTTP_ERROR_<status>; ECONNREFUSED and ENOTFOUND -> NET_UNREACHABLE;
unrecognized rejections -> NETWORK_ERROR; REQUEST_TIMEOUT on a slow
response; single fetch call per request (no hidden retries); plus
checkEndpointReachability success/failure/no-body-leak cases.
- endpoint-diagnostics.test.ts (3 tests, new): probeConfiguredEndpoints
reports both endpoints reachable; rejects malformed config (invalid
network) before making any network call; never leaks secrets or
response bodies into the report.
- exports.test.ts: asserts NetworkClient and the rest of the network
resilience layer are exported from the package root; adds
probeConfiguredEndpoints to the diagnostics export list.
All unit tests run offline (tests/setup/offline-guard.ts blocks real
fetch calls). Full suite: 164/164 passing across the 9 touched/added
test files; pre-existing unrelated failures elsewhere are untouched
by this change (see PR description).
Refs #272 (Files: tests/network-client.test.ts [new, 161 lines],
tests/endpoint-diagnostics.test.ts [new, 58 lines],
tests/exports.test.ts, +19 lines)
* docs: add Network Resilience Layer guide, link NET_UNREACHABLE
- docs/network-resilience.md (new): consolidated guide to NetworkClient,
the typed failure codes (REQUEST_TIMEOUT, NET_RATE_LIMITED,
NET_UNREACHABLE, HTTP_ERROR_<status>, NETWORK_ERROR), why read-only
calls are safe to retry but transaction submission is not, and the two
endpoint-diagnostics tools (config snapshot vs. live reachability
probe). Cross-links network-errors.md and retry-policy.md rather than
duplicating their content.
- docs/network-errors.md: adds a NET_UNREACHABLE / NET_RATE_LIMITED row
and links to the new guide.
- README.md: adds the new guide to the docs index.
Refs #272 (Files: docs/network-resilience.md [new, 125 lines],
docs/network-errors.md, +6 lines, README.md, +1 line)
---------
Co-authored-by: PocketPay Contributor <dev@example.com>
-[Local Mobile Consumption](./docs/local-mobile-consumption.md) - Safely test unpublished SDK changes in `pocketpay-mobile`with tarballs, links, local paths, or workspaces
63
63
-[Transaction Date Formatting](./docs/transaction-timestamps.md) - Format of every `createdAt` timestamp returned by the SDK
64
64
-[Network Error Handling](./docs/network-errors.md) - Retry guidance for Horizon, Friendbot, and Soroban RPC failures
65
+
-[Network Resilience Layer](./docs/network-resilience.md) - The `NetworkClient` abstraction, typed timeout/rate-limit/unreachable errors, and endpoint reachability diagnostics
65
66
-[Safe Retry Policy](./docs/retry-policy.md) - Classifying submission outcomes, safe retry rules, and the `withRetryPolicy` API
66
67
-[Account Sequence & Concurrency Safety](./docs/sequence-safety.md) - Account sequence number handling, caching, stale sequence error classification, and in-process concurrency safety with SequenceProvider
67
68
-[Meaningful Change Review Guide](./docs/meaningful-change-review.md) - what counts as real SDK work: behaviour, modules, tests, acceptance criteria + reviewer checks
Copy file name to clipboardExpand all lines: docs/network-errors.md
+6Lines changed: 6 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,10 @@
2
2
3
3
This guide helps PocketPay SDK consumers handle transient failures from Stellar network services correctly.
4
4
5
+
> See [Network Resilience Layer](./network-resilience.md) for the `NetworkClient`
6
+
> abstraction, endpoint diagnostics, and how read-only retries differ from
7
+
> transaction submission.
8
+
5
9
## Overview
6
10
7
11
Stellar network calls can fail for different reasons. Some failures are temporary and should be retried. Others indicate a problem with the request or account state and should be shown to the user.
@@ -20,6 +24,8 @@ These errors are temporary. Retry with exponential backoff (start at 1s, double
`NetworkClient` classifies every failure into one of these codes before
34
+
throwing, so callers can branch on `error.code` / `error.retryable` instead of
35
+
inspecting HTTP status numbers or `error.message`:
36
+
37
+
| Code | When | Retryable |
38
+
|---|---|---|
39
+
|`REQUEST_TIMEOUT`| The SDK's own timeout budget elapsed (see `withTimeout`) | Yes (unless the stage is `submission`/`confirmation` — see below) |
40
+
|`NET_RATE_LIMITED`| HTTP 429 | Yes |
41
+
|`NET_UNREACHABLE`| HTTP 5xx, or the request never reached the endpoint (`ECONNREFUSED`, `ENOTFOUND`, `EAI_AGAIN`, `ENETUNREACH`, `EHOSTUNREACH`) | Yes |
42
+
|`HTTP_ERROR_<status>`| Any other non-2xx response (400, 401, 403, 404, …) | No |
43
+
|`NETWORK_ERROR`| An unrecognized fetch rejection | Depends — inspect `error.cause`|
44
+
45
+
All of the above are published, stable codes in `ERROR_CODES` (see
46
+
`src/errors/codes.ts`); use `describeError(code)` to get a safe user-facing
0 commit comments