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