@izak0s/spacebring-api 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,7 +17,8 @@ A fully-typed TypeScript client for the [Spacebring](https://www.spacebring.com)
17
17
  - **Nested, discoverable API** — `sb.billing.invoices.pay(id)`, `sb.visitors.visits.checkIn(body)`
18
18
  - **Auto-pagination** — every paginated list endpoint has an `iterate()` async generator that walks `nextPageToken` for you
19
19
  - **Ergonomic returns** — single-property response envelopes are unwrapped: entities and plain arrays come back directly
20
- - **Rich error handling** — non-2xx responses throw a typed `SpacebringError`; malformed 2xx bodies and stuck pagination tokens throw instead of failing silently
20
+ - **Rich error handling** — non-2xx responses throw a typed `SpacebringError` carrying the status, parsed body, and the operation that failed; malformed 2xx bodies and stuck pagination tokens throw instead of failing silently
21
+ - **Resilient by default** — automatic retries for rate limits, gateway errors, and network failures (never replaying non-idempotent requests); optional per-attempt timeouts and `AbortSignal` cancellation on every method
21
22
  - **Zero runtime dependencies** — Node ≥ 20, `fetch`-based
22
23
  - **Dual module** — ships both ESM and CommonJS builds with type declarations for each
23
24
 
@@ -101,15 +102,30 @@ try {
101
102
  } catch (error) {
102
103
  if (error instanceof SpacebringError) {
103
104
  console.error(error.status, error.body?.message);
105
+ console.error(error.operation); // "GET /benefits/v1/{benefitId}"
106
+ console.error(error.url); // the full request URL
104
107
  }
105
108
  }
106
109
  ```
107
110
 
108
111
  Malformed successes are covered too: a 2xx with an empty or incomplete body throws a `SpacebringError` (never a bare `TypeError`), and `iterate()` throws instead of looping forever if the API repeats a page token.
109
112
 
110
- ### Rate limits
113
+ ### Rate limits & retries
111
114
 
112
- The API allows **10 requests per second**. Rate-limited requests (429) are retried automatically — up to 3 times, honoring `Retry-After` or backing off exponentially — so `iterate()` survives the limit out of the box. Tune or disable via `maxRetries` in the config (`maxRetries: 0` turns it off); a 429 that persists past the retries is thrown as a normal `SpacebringError`.
115
+ The API allows **10 requests per second**. Rate-limited requests (429) are retried automatically — up to 3 times, honoring `Retry-After` (seconds or HTTP-date) or backing off exponentially — so `iterate()` survives the limit out of the box. Gateway errors (502/503/504), network failures, and timeouts are retried the same way, but only for idempotent methods (`GET`/`PUT`/`DELETE`) — a `POST` is never replayed, since the request may have reached the API. Tune or disable via `maxRetries` in the config (`maxRetries: 0` turns it off); an error that persists past the retries is thrown as-is.
116
+
117
+ ### Timeouts & cancellation
118
+
119
+ Every method accepts a trailing options argument with an `AbortSignal`; aborting cancels the in-flight request and any pending retry wait. A client-wide per-attempt timeout is available via `timeoutMs`:
120
+
121
+ ```ts
122
+ const sb = new Spacebring({ clientId, clientSecret, timeoutMs: 15_000 });
123
+
124
+ const controller = new AbortController();
125
+ const benefits = await sb.benefits.list({ locationRef }, { signal: controller.signal });
126
+ ```
127
+
128
+ `timeoutMs` uses `AbortSignal.timeout`; combining it with your own signal relies on `AbortSignal.any` (Node ≥ 20.3, all modern browsers/workers/edge runtimes).
113
129
 
114
130
  ---
115
131