@codingaryan/smoothapi 0.1.0 → 0.2.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 ADDED
@@ -0,0 +1,102 @@
1
+ # @codingaryan/smoothapi
2
+
3
+ API resilience library for TypeScript/JavaScript. It wraps the native `fetch` API with **exponential backoff, full jitter, and a finite-state machine circuit breaker** to protect against cascading failures.
4
+
5
+ Zero dependencies. Small bundle size. Built for modern ESM.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @codingaryan/smoothapi
11
+ ```
12
+
13
+ ## Features
14
+
15
+ - **Exponential Backoff with Full Jitter:** Prevents the "thundering herd" problem by randomizing retry delays.
16
+ - **Circuit Breaker (FSM):** Isolated per-domain state machine (`CLOSED` → `OPEN` → `HALF_OPEN`).
17
+ - **Smart Retries:** Automatically retries on specific HTTP status codes (e.g., 429, 500, 502, 503, 504) while throwing immediately on client errors (400, 401, 404).
18
+ - **Graceful Fallbacks:** Optionally serve cached or default data instantly when the circuit is `OPEN`.
19
+
20
+ ## Usage
21
+
22
+ ### Basic Usage (Defaults)
23
+
24
+ If you don't need custom configurations, you can instantiate the resilient fetch with its defaults by simply passing an empty object.
25
+
26
+ ```typescript
27
+ import { createResilientFetch } from '@codingaryan/smoothapi';
28
+
29
+ // Create it with default settings
30
+ const fetchWithRetry = createResilientFetch({});
31
+
32
+ async function main() {
33
+ try {
34
+ // Drop-in replacement for native fetch
35
+ const response = await fetchWithRetry('https://api.example.com/data');
36
+ const data = await response.json();
37
+ console.log(data);
38
+ } catch (err) {
39
+ console.error("Request failed completely:", err);
40
+ }
41
+ }
42
+ ```
43
+
44
+ **Default Settings provided automatically:**
45
+ - **Retries**: 3 attempts
46
+ - **Backoff Base Delay**: 100 milliseconds
47
+ - **Circuit Failure Threshold**: Trips after 3 consecutive failures
48
+ - **Circuit Cooldown**: Stays open for 10 seconds before probing
49
+ - **Status Codes to Retry**: `429`, `500`, `502`, `503`, and `504`
50
+
51
+ ### Advanced Usage (Custom Settings)
52
+
53
+ You can override any of the defaults to suit your application's needs, such as adding a fallback object.
54
+
55
+ ```typescript
56
+ import { createResilientFetch } from '@codingaryan/smoothapi';
57
+
58
+ const fetchWithRetry = createResilientFetch({
59
+ backoff: {
60
+ baseDelay: 100, // ms to wait before first retry
61
+ maxDelay: 30000, // cap on exponential growth
62
+ maxRetries: 3 // max number of retry attempts
63
+ },
64
+ circuitBreaker: {
65
+ failureThreshold: 3, // trip OPEN after 3 consecutive failures
66
+ cooldownMs: 10000 // stay OPEN for 10 seconds before probing
67
+ },
68
+ // Optional: Return this instead of throwing when the circuit is OPEN
69
+ fallback: { error: "Service degraded, returning stale data." },
70
+ // Optional: Custom status codes to retry on
71
+ retryOn: [429, 500, 502, 503, 504]
72
+ });
73
+
74
+ async function main() {
75
+ try {
76
+ const response = await fetchWithRetry('https://api.example.com/data');
77
+
78
+ // If fallback triggered, it returns your fallback object directly
79
+ if ('error' in response) {
80
+ console.log("Fallback triggered:", response.error);
81
+ return;
82
+ }
83
+
84
+ // Otherwise it's a standard Response object
85
+ const data = await response.json();
86
+ console.log(data);
87
+ } catch (err) {
88
+ console.error("Request failed completely:", err);
89
+ }
90
+ }
91
+ ```
92
+
93
+ ## How It Works
94
+
95
+ 1. **Host Extraction:** The domain is automatically extracted from the URL. The circuit breaker state is isolated per host (e.g., `api.github.com` failing won't trip the circuit for `api.stripe.com`).
96
+ 2. **Circuit Check:** Before making a network request, the breaker checks the state. If it's `OPEN`, the request is blocked instantly (returning your fallback, or throwing a `CircuitOpenError`).
97
+ 3. **Execution & Retries:** If the response status is in your `retryOn` list, it's counted as a failure and retried with backoff.
98
+ 4. **Recovery:** After `cooldownMs`, the breaker enters `HALF_OPEN` state. The next request acts as a probe. If it succeeds, the circuit closes. If it fails, it snaps back to `OPEN` immediately.
99
+
100
+ ## License
101
+
102
+ MIT
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAUpE,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,YAAY,EAAE,oBAAoB,CAAC,CAAC,CAAC,IAMzE,KAAK,MAAM,GAAG,GAAG,EACjB,UAAU,WAAW,KACpB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,CAsCzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAUpE,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,YAAY,EAAE,oBAAoB,CAAC,CAAC,CAAC,IAMzE,KAAK,MAAM,GAAG,GAAG,EACjB,UAAU,WAAW,KACpB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,CA2CzB"}
package/dist/index.js CHANGED
@@ -25,9 +25,14 @@ export function createResilientFetch(globalConfig) {
25
25
  try {
26
26
  const response = await fetch(url, options);
27
27
  // fetch() resolves for any HTTP status. Retryable codes need to be
28
- // treated as failures manually so they dont throw on their own.
28
+ // treated as failures manually.
29
29
  if (retryOn.includes(response.status)) {
30
- throw new Error(`HTTP ${response.status}`);
30
+ breaker.recordFailure(domain);
31
+ if (attempt < backoffConfig.maxRetries) {
32
+ await sleep(calculateBackoff(attempt, backoffConfig));
33
+ continue;
34
+ }
35
+ return response;
31
36
  }
32
37
  breaker.recordSuccess(domain);
33
38
  return response;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAwB,MAAM,YAAY,CAAC;AAEpE,MAAM,gBAAgB,GAAG;IACvB,SAAS,EAAE,GAAG;IACd,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,CAAC;CACd,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEnD,MAAM,UAAU,oBAAoB,CAAI,YAAqC;IAC3E,MAAM,aAAa,GAAG,EAAE,GAAG,gBAAgB,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,CAAC;IACvE,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACzD,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;IAErE,OAAO,KAAK,UAAU,cAAc,CAClC,GAAiB,EACjB,OAAqB;QAErB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QAErC,sDAAsD;QACtD,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,IAAI,YAAY,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACxC,OAAO,YAAY,CAAC,QAAa,CAAC;YACpC,CAAC;YACD,MAAM,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,SAAkB,CAAC;QAEvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;YACrE,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBAE3C,mEAAmE;gBACnE,gEAAgE;gBAChE,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBACtC,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC7C,CAAC;gBAED,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAC9B,OAAO,QAAQ,CAAC;YAClB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,SAAS,GAAG,GAAG,CAAC;gBAChB,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAE9B,sCAAsC;gBACtC,IAAI,OAAO,GAAG,aAAa,CAAC,UAAU,EAAE,CAAC;oBACvC,MAAM,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;gBACxD,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,SAAS,CAAC;IAClB,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAwB,MAAM,YAAY,CAAC;AAEpE,MAAM,gBAAgB,GAAG;IACvB,SAAS,EAAE,GAAG;IACd,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,CAAC;CACd,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEnD,MAAM,UAAU,oBAAoB,CAAI,YAAqC;IAC3E,MAAM,aAAa,GAAG,EAAE,GAAG,gBAAgB,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE,CAAC;IACvE,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACzD,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;IAErE,OAAO,KAAK,UAAU,cAAc,CAClC,GAAiB,EACjB,OAAqB;QAErB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QAErC,sDAAsD;QACtD,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,IAAI,YAAY,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACxC,OAAO,YAAY,CAAC,QAAa,CAAC;YACpC,CAAC;YACD,MAAM,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,SAAkB,CAAC;QAEvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;YACrE,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBAE3C,mEAAmE;gBACnE,gCAAgC;gBAChC,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBACtC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;oBAC9B,IAAI,OAAO,GAAG,aAAa,CAAC,UAAU,EAAE,CAAC;wBACvC,MAAM,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;wBACtD,SAAS;oBACX,CAAC;oBACD,OAAO,QAAQ,CAAC;gBAClB,CAAC;gBAED,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAC9B,OAAO,QAAQ,CAAC;YAClB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,SAAS,GAAG,GAAG,CAAC;gBAChB,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAE9B,sCAAsC;gBACtC,IAAI,OAAO,GAAG,aAAa,CAAC,UAAU,EAAE,CAAC;oBACvC,MAAM,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;gBACxD,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,SAAS,CAAC;IAClB,CAAC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codingaryan/smoothapi",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "API resilience library — exponential backoff and circuit breaker for fetch",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,7 +11,9 @@
11
11
  "types": "./dist/index.d.ts"
12
12
  }
13
13
  },
14
- "files": ["dist"],
14
+ "files": [
15
+ "dist"
16
+ ],
15
17
  "scripts": {
16
18
  "build": "tsc",
17
19
  "build:watch": "tsc --watch",
@@ -25,6 +27,12 @@
25
27
  "engines": {
26
28
  "node": ">=18.0.0"
27
29
  },
28
- "keywords": ["resilience", "circuit-breaker", "retry", "backoff", "fetch"],
30
+ "keywords": [
31
+ "resilience",
32
+ "circuit-breaker",
33
+ "retry",
34
+ "backoff",
35
+ "fetch"
36
+ ],
29
37
  "license": "MIT"
30
38
  }