@adaptic/utils 0.0.977 → 0.0.978
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/dist/index.cjs +99 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +99 -0
- package/dist/index.mjs.map +1 -1
- package/dist/types/__tests__/retry.test.d.ts +2 -0
- package/dist/types/__tests__/retry.test.d.ts.map +1 -0
- package/dist/types/utils/retry.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5604,6 +5604,92 @@ const DEFAULT_RETRY_CONFIG = {
|
|
|
5604
5604
|
retryableStatusCodes: [429, 500, 502, 503, 504],
|
|
5605
5605
|
retryOnNetworkError: true,
|
|
5606
5606
|
};
|
|
5607
|
+
/**
|
|
5608
|
+
* Node.js / undici / system error codes that represent transient network
|
|
5609
|
+
* conditions. Present on `error.code` for net/http/dns/undici errors.
|
|
5610
|
+
*/
|
|
5611
|
+
const RETRYABLE_ERROR_CODES = new Set([
|
|
5612
|
+
"ETIMEDOUT",
|
|
5613
|
+
"ESOCKETTIMEDOUT",
|
|
5614
|
+
"ECONNRESET",
|
|
5615
|
+
"ECONNREFUSED",
|
|
5616
|
+
"EHOSTUNREACH",
|
|
5617
|
+
"ENETUNREACH",
|
|
5618
|
+
"EAI_AGAIN",
|
|
5619
|
+
"EPIPE",
|
|
5620
|
+
"ECONNABORTED",
|
|
5621
|
+
"ENOTFOUND",
|
|
5622
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
5623
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
5624
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
5625
|
+
"UND_ERR_SOCKET",
|
|
5626
|
+
"UND_ERR_CLOSED",
|
|
5627
|
+
"UND_ERR_REQ_CONTENT_LENGTH_MISMATCH",
|
|
5628
|
+
]);
|
|
5629
|
+
/**
|
|
5630
|
+
* Error constructor names / `error.name` values that indicate transient
|
|
5631
|
+
* abort / timeout conditions.
|
|
5632
|
+
*/
|
|
5633
|
+
const RETRYABLE_ERROR_NAMES = new Set([
|
|
5634
|
+
"AbortError",
|
|
5635
|
+
"TimeoutError",
|
|
5636
|
+
"FetchError",
|
|
5637
|
+
"RequestTimeoutError",
|
|
5638
|
+
"ConnectTimeoutError",
|
|
5639
|
+
"HeadersTimeoutError",
|
|
5640
|
+
"BodyTimeoutError",
|
|
5641
|
+
]);
|
|
5642
|
+
/**
|
|
5643
|
+
* Message-pattern fallback for libraries that discard error codes/names but
|
|
5644
|
+
* preserve text (e.g., some Apollo/axios wrappers).
|
|
5645
|
+
*/
|
|
5646
|
+
const RETRYABLE_MESSAGE_PATTERNS = [
|
|
5647
|
+
/aborted/i,
|
|
5648
|
+
/timeout/i,
|
|
5649
|
+
/timed out/i,
|
|
5650
|
+
/network error/i,
|
|
5651
|
+
/socket hang up/i,
|
|
5652
|
+
/connection (reset|refused|closed)/i,
|
|
5653
|
+
/ECONNRESET/,
|
|
5654
|
+
/ETIMEDOUT/,
|
|
5655
|
+
/ECONNREFUSED/,
|
|
5656
|
+
/EAI_AGAIN/,
|
|
5657
|
+
/UND_ERR_/,
|
|
5658
|
+
];
|
|
5659
|
+
/**
|
|
5660
|
+
* Walks the `error.cause` chain (capped to avoid cycles) and tests whether
|
|
5661
|
+
* any link along the chain looks like a transient network error. Modern APIs
|
|
5662
|
+
* (undici, fetch, Apollo Client 3.8+) wrap the root network failure as a
|
|
5663
|
+
* `.cause`, so the surface `Error` may report a generic message while the
|
|
5664
|
+
* actionable signal lives one or more levels deeper.
|
|
5665
|
+
*/
|
|
5666
|
+
function isTransientNetworkError(error) {
|
|
5667
|
+
const MAX_CAUSE_DEPTH = 6;
|
|
5668
|
+
let current = error;
|
|
5669
|
+
for (let depth = 0; depth < MAX_CAUSE_DEPTH && current; depth++) {
|
|
5670
|
+
if (current instanceof Error || typeof current === "object") {
|
|
5671
|
+
const err = current;
|
|
5672
|
+
if (typeof err.name === "string" && RETRYABLE_ERROR_NAMES.has(err.name)) {
|
|
5673
|
+
return true;
|
|
5674
|
+
}
|
|
5675
|
+
if (typeof err.code === "string" && RETRYABLE_ERROR_CODES.has(err.code)) {
|
|
5676
|
+
return true;
|
|
5677
|
+
}
|
|
5678
|
+
if (typeof err.message === "string") {
|
|
5679
|
+
for (const pattern of RETRYABLE_MESSAGE_PATTERNS) {
|
|
5680
|
+
if (pattern.test(err.message)) {
|
|
5681
|
+
return true;
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5685
|
+
current = err.cause;
|
|
5686
|
+
}
|
|
5687
|
+
else {
|
|
5688
|
+
break;
|
|
5689
|
+
}
|
|
5690
|
+
}
|
|
5691
|
+
return false;
|
|
5692
|
+
}
|
|
5607
5693
|
/**
|
|
5608
5694
|
* Analyzes an error and determines if it's retryable.
|
|
5609
5695
|
* @param error - The error to analyze
|
|
@@ -5668,6 +5754,19 @@ function analyzeError(error, response, config) {
|
|
|
5668
5754
|
isRetryable: config.retryOnNetworkError,
|
|
5669
5755
|
};
|
|
5670
5756
|
}
|
|
5757
|
+
// Handle transient network conditions: AbortError, TimeoutError,
|
|
5758
|
+
// Node/undici error codes (ETIMEDOUT, ECONNRESET, UND_ERR_*), and
|
|
5759
|
+
// wrapped failures exposed via error.cause. This catches the broad class
|
|
5760
|
+
// of infrastructure flakes that the TypeError-only check above misses.
|
|
5761
|
+
if (isTransientNetworkError(error)) {
|
|
5762
|
+
const reason = error instanceof Error ? error.message : "Transient network error";
|
|
5763
|
+
return {
|
|
5764
|
+
type: "NETWORK_ERROR",
|
|
5765
|
+
reason,
|
|
5766
|
+
status: null,
|
|
5767
|
+
isRetryable: config.retryOnNetworkError,
|
|
5768
|
+
};
|
|
5769
|
+
}
|
|
5671
5770
|
// Handle error objects with messages
|
|
5672
5771
|
if (error instanceof Error) {
|
|
5673
5772
|
// Parse error messages that might contain status information
|