snapreq 0.0.1 → 0.0.3

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
@@ -15,6 +15,19 @@ The same code runs everywhere. Where a platform cannot provide a feature (Unix s
15
15
  npm install snapreq
16
16
  ```
17
17
 
18
+ ## Imports
19
+
20
+ snapreq has **no barrel entry point** — you import each piece from its own subpath so a bundler (Metro/Expo, webpack, …) only pulls in what you use, and the HTTP client never drags the WebSocket client into your bundle:
21
+
22
+ ```js
23
+ import SnapReq from "snapreq" // the HTTP client
24
+ import SnapReqWebSocketClient from "snapreq/websocket"
25
+ import {SnapReqHttpError, SnapReqUnsupportedFeatureError} from "snapreq/errors"
26
+ import {defaultRetryableError} from "snapreq/retry"
27
+ import SnapReqResponse from "snapreq/response"
28
+ import SnapReqHeaders from "snapreq/headers"
29
+ ```
30
+
18
31
  ## HTTP
19
32
 
20
33
  ```js
@@ -69,7 +82,7 @@ await client.get("/flaky", {retry: true})
69
82
  await client.get("/flaky", {retry: {tries: 5, waitMs: 200, retryableStatuses: [503]}})
70
83
 
71
84
  // Compose extra rules on top of the default network classifier:
72
- import {defaultRetryableError} from "snapreq"
85
+ import {defaultRetryableError} from "snapreq/retry"
73
86
 
74
87
  await client.get("/x", {retry: {shouldRetry: (error) => defaultRetryableError(error) || isMyCase(error)}})
75
88
  ```
@@ -118,7 +131,7 @@ const caps = await client.capabilities()
118
131
  A WebSocket client over `globalThis.WebSocket` with auto-reconnect, session resumption, request/response calls, channel subscriptions and 1:1 connections.
119
132
 
120
133
  ```js
121
- import {SnapReqWebSocketClient} from "snapreq"
134
+ import SnapReqWebSocketClient from "snapreq/websocket"
122
135
 
123
136
  const client = new SnapReqWebSocketClient({url: "wss://example.com/websocket"})
124
137
 
@@ -137,7 +150,7 @@ await connection.ready
137
150
  connection.sendMessage({text: "hi"})
138
151
  ```
139
152
 
140
- Optional adapters: `networkMonitor` (gate reconnects on online state) and `sessionStore` (persist the session id across reloads).
153
+ Optional adapters: `networkMonitor` (gate reconnects on online state), `sessionStore` (persist the session id across reloads) and `deserialize` (a `(value) => value` transform applied inside `response.json()` so an app can re-hydrate its own wire format).
141
154
 
142
155
  ## License
143
156
 
package/package.json CHANGED
@@ -1,17 +1,33 @@
1
1
  {
2
2
  "name": "snapreq",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Cross-platform HTTP and WebSocket client with one API across Node, web, Expo and React Native",
5
5
  "type": "module",
6
- "main": "src/index.js",
6
+ "main": "./src/snap-req.js",
7
+ "types": "./types/snap-req.d.ts",
7
8
  "exports": {
8
- ".": "./src/index.js"
9
+ ".": {"types": "./types/snap-req.d.ts", "default": "./src/snap-req.js"},
10
+ "./websocket": {"types": "./types/websocket/websocket-client.d.ts", "default": "./src/websocket/websocket-client.js"},
11
+ "./errors": {"types": "./types/errors.d.ts", "default": "./src/errors.js"},
12
+ "./headers": {"types": "./types/headers.d.ts", "default": "./src/headers.js"},
13
+ "./response": {"types": "./types/response.d.ts", "default": "./src/response.js"},
14
+ "./retry": {"types": "./types/retry.d.ts", "default": "./src/retry.js"},
15
+ "./capabilities": {"types": "./types/capabilities.d.ts", "default": "./src/capabilities.js"},
16
+ "./transports/select": {"types": "./types/transports/select.d.ts", "default": "./src/transports/select.js"},
17
+ "./transports/fetch": {"types": "./types/transports/fetch-transport.d.ts", "default": "./src/transports/fetch-transport.js"},
18
+ "./transports/xhr": {"types": "./types/transports/xhr-transport.d.ts", "default": "./src/transports/xhr-transport.js"},
19
+ "./*": {"types": "./types/*.d.ts", "default": "./src/*.js"}
9
20
  },
10
21
  "files": [
11
- "src/**"
22
+ "src/**",
23
+ "types/**"
12
24
  ],
13
25
  "scripts": {
26
+ "all-checks": "npm run typecheck && npm run lint && npm run test",
27
+ "build:types": "tsc -p tsconfig.build.json",
14
28
  "lint": "eslint src spec",
29
+ "prepublishOnly": "npm run build:types",
30
+ "release:patch": "release-patch",
15
31
  "test": "node --test \"spec/**/*-spec.js\"",
16
32
  "typecheck": "tsc --noEmit"
17
33
  },
@@ -33,6 +49,7 @@
33
49
  "eslint": "^10.3.0",
34
50
  "eslint-plugin-jsdoc": "^62.9.0",
35
51
  "globals": "^16.0.0",
52
+ "release-patch": "^1.0.0",
36
53
  "typescript": "^6.0.3",
37
54
  "ws": "^8.21.0"
38
55
  }
@@ -31,11 +31,14 @@ export default class SnapReqWebSocketClient {
31
31
  * @param {{getIsOnline?: () => boolean | Promise<boolean>, subscribe?: (callback: (isOnline: boolean) => void) => (() => void) | {remove: () => void}}} [args.networkMonitor] - Optional online-state adapter. When provided, auto-reconnect can wait for the network to report online before reconnecting, and open sockets are closed when the monitor reports offline.
32
32
  * @param {number[]} [args.reconnectDelays] - Backoff delays in ms (default: [1000, 2000, 4000, 8000, 15000]).
33
33
  * @param {{get: () => string | null | undefined | Promise<string | null | undefined>, set: (sessionId: string) => void | Promise<void>, clear: () => void | Promise<void>}} [args.sessionStore] - Optional sessionId persistence hook surviving reloads (localStorage, a cookie, SQLite, etc.).
34
+ * @param {(value: any) => any} [args.deserialize] - Optional transform applied to a response body inside `response.json()`. Lets an app re-hydrate its own wire format. Defaults to identity.
34
35
  */
35
- constructor({autoReconnect = true, debug = false, networkMonitor, reconnectDelays, sessionStore, url} = /** @type {any} */ ({})) {
36
+ constructor({autoReconnect = true, debug = false, deserialize, networkMonitor, reconnectDelays, sessionStore, url} = /** @type {any} */ ({})) {
36
37
  if (!globalThis.WebSocket) throw new Error("WebSocket global is not available")
37
38
  if (!url) throw new Error("SnapReqWebSocketClient requires a url")
38
39
 
40
+ /** @type {(value: any) => any} */
41
+ this._deserialize = deserialize || ((value) => value)
39
42
  /** @type {boolean} */
40
43
  this.autoReconnect = autoReconnect
41
44
  this.debug = debug
@@ -637,7 +640,7 @@ export default class SnapReqWebSocketClient {
637
640
 
638
641
  if (pending) {
639
642
  this.pendingRequests.delete(id)
640
- pending.resolve(new SnapReqWebSocketResponse(message))
643
+ pending.resolve(new SnapReqWebSocketResponse(message, this._deserialize))
641
644
  } else {
642
645
  this._debug(`No pending request for response id ${id}`)
643
646
  }
@@ -1006,8 +1009,9 @@ export default class SnapReqWebSocketClient {
1006
1009
  export class SnapReqWebSocketResponse {
1007
1010
  /**
1008
1011
  * @param {object} message - The response message.
1012
+ * @param {(value: any) => any} [deserialize] - Transform applied to the parsed body in `json()`. Defaults to identity.
1009
1013
  */
1010
- constructor(message) {
1014
+ constructor(message, deserialize) {
1011
1015
  const responseMessage = /** @type {{body?: any, headers?: Record<string, any>, id?: string | number | null, statusCode?: number, statusMessage?: string, type?: string}} */ (message)
1012
1016
 
1013
1017
  this.body = responseMessage.body
@@ -1016,14 +1020,15 @@ export class SnapReqWebSocketResponse {
1016
1020
  this.statusCode = responseMessage.statusCode || 200
1017
1021
  this.statusMessage = responseMessage.statusMessage || "OK"
1018
1022
  this.type = responseMessage.type
1023
+ this._deserialize = deserialize || ((value) => value)
1019
1024
  }
1020
1025
 
1021
- /** @returns {any} - The parsed JSON body. */
1026
+ /** @returns {any} - The parsed (and optionally deserialized) JSON body. */
1022
1027
  json() {
1023
1028
  if (typeof this.body !== "string") {
1024
1029
  throw new Error("Response body is not a string")
1025
1030
  }
1026
1031
 
1027
- return JSON.parse(this.body)
1032
+ return this._deserialize(JSON.parse(this.body))
1028
1033
  }
1029
1034
  }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @typedef {object} TransportCapabilities
3
+ * @property {boolean} unixSocket - Connect over a Unix domain socket.
4
+ * @property {boolean} tlsClientCert - Present a client certificate / custom CA for TLS.
5
+ * @property {boolean} requestCompression - Compress the request body (gzip/deflate/br/zstd).
6
+ * @property {boolean} responseStreaming - Expose the response body as a stream before it is fully read.
7
+ * @property {boolean} requestStreaming - Send a streamed (async-iterable) request body.
8
+ * @property {boolean} keepAlive - Reuse connections across requests (HTTP keep-alive).
9
+ * @property {boolean} abort - Cancel an in-flight request via an `AbortSignal`.
10
+ */
11
+ /**
12
+ * Builds a fully-populated capability object so callers can read every flag
13
+ * without undefined checks.
14
+ * @param {Partial<TransportCapabilities>} overrides - Capabilities the transport supports.
15
+ * @returns {TransportCapabilities} - Complete capability flags.
16
+ */
17
+ export function buildCapabilities(overrides: Partial<TransportCapabilities>): TransportCapabilities;
18
+ export type TransportCapabilities = {
19
+ /**
20
+ * - Connect over a Unix domain socket.
21
+ */
22
+ unixSocket: boolean;
23
+ /**
24
+ * - Present a client certificate / custom CA for TLS.
25
+ */
26
+ tlsClientCert: boolean;
27
+ /**
28
+ * - Compress the request body (gzip/deflate/br/zstd).
29
+ */
30
+ requestCompression: boolean;
31
+ /**
32
+ * - Expose the response body as a stream before it is fully read.
33
+ */
34
+ responseStreaming: boolean;
35
+ /**
36
+ * - Send a streamed (async-iterable) request body.
37
+ */
38
+ requestStreaming: boolean;
39
+ /**
40
+ * - Reuse connections across requests (HTTP keep-alive).
41
+ */
42
+ keepAlive: boolean;
43
+ /**
44
+ * - Cancel an in-flight request via an `AbortSignal`.
45
+ */
46
+ abort: boolean;
47
+ };
@@ -0,0 +1,64 @@
1
+ /** Base class for every error thrown by snapreq. */
2
+ export class SnapReqError extends Error {
3
+ /** @param {string} message - Human readable description. */
4
+ constructor(message: string);
5
+ }
6
+ /**
7
+ * Thrown when a request completes with a non-2xx status and the caller asked
8
+ * snapreq to treat error statuses as failures (`throwOnError`, the default for
9
+ * the high-level helpers). Carries enough metadata to build a friendly message
10
+ * without re-reading the response.
11
+ */
12
+ export class SnapReqHttpError extends SnapReqError {
13
+ /**
14
+ * @param {object} options - Error metadata.
15
+ * @param {string} options.message - Human readable description.
16
+ * @param {string} options.method - HTTP method used for the request.
17
+ * @param {string} options.url - Fully resolved request URL.
18
+ * @param {number} options.status - HTTP status code returned by the server.
19
+ * @param {string} [options.statusText] - HTTP status text returned by the server.
20
+ * @param {string} [options.responseText] - Decoded response body, when available.
21
+ * @param {import("./response.js").default} [options.response] - The response that failed.
22
+ */
23
+ constructor({ message, method, url, status, statusText, responseText, response }: {
24
+ message: string;
25
+ method: string;
26
+ url: string;
27
+ status: number;
28
+ statusText?: string;
29
+ responseText?: string;
30
+ response?: import("./response.js").default;
31
+ });
32
+ method: string;
33
+ url: string;
34
+ status: number;
35
+ statusText: string;
36
+ responseText: string;
37
+ response: import("./response.js").default;
38
+ }
39
+ /**
40
+ * Thrown when a request asks for a capability the active transport cannot
41
+ * provide on the current platform (for example a Unix socket or request-body
42
+ * compression in a browser). The API stays identical across platforms; this
43
+ * error is how snapreq tells you a specific feature had to be left out.
44
+ */
45
+ export class SnapReqUnsupportedFeatureError extends SnapReqError {
46
+ /**
47
+ * @param {object} options - Error metadata.
48
+ * @param {string} options.feature - The capability that is not supported.
49
+ * @param {string} options.transport - Name of the active transport.
50
+ * @param {string} [options.detail] - Optional extra context.
51
+ */
52
+ constructor({ feature, transport, detail }: {
53
+ feature: string;
54
+ transport: string;
55
+ detail?: string;
56
+ });
57
+ feature: string;
58
+ transport: string;
59
+ }
60
+ /** Thrown when a request is aborted via an `AbortSignal`. */
61
+ export class SnapReqAbortError extends SnapReqError {
62
+ /** @param {string} [message] - Human readable description. */
63
+ constructor(message?: string);
64
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * A tiny case-insensitive header bag. Works the same on every platform and
3
+ * avoids depending on the DOM `Headers` global (absent in some runtimes) or on
4
+ * Node's header handling. Header values are always stored as strings.
5
+ */
6
+ export default class SnapReqHeaders {
7
+ /**
8
+ * @param {Record<string, string | number | string[]> | SnapReqHeaders | Iterable<[string, string]>} [init] - Initial headers.
9
+ */
10
+ constructor(init?: Record<string, string | number | string[]> | SnapReqHeaders | Iterable<[string, string]>);
11
+ /** @type {Map<string, {name: string, value: string}>} - Keyed by lower-cased name. */
12
+ _map: Map<string, {
13
+ name: string;
14
+ value: string;
15
+ }>;
16
+ /**
17
+ * @param {string} name - Header name (case-insensitive).
18
+ * @param {string | number} value - Header value.
19
+ * @returns {void}
20
+ */
21
+ set(name: string, value: string | number): void;
22
+ /**
23
+ * @param {string} name - Header name (case-insensitive).
24
+ * @returns {string | null} - The header value or null when absent.
25
+ */
26
+ get(name: string): string | null;
27
+ /**
28
+ * @param {string} name - Header name (case-insensitive).
29
+ * @returns {boolean} - Whether the header is present.
30
+ */
31
+ has(name: string): boolean;
32
+ /**
33
+ * @param {string} name - Header name (case-insensitive).
34
+ * @returns {void}
35
+ */
36
+ delete(name: string): void;
37
+ /**
38
+ * @yields {[string, string]} - Name/value pair preserving original casing.
39
+ * @returns {IterableIterator<[string, string]>} - Name/value pairs preserving original casing.
40
+ */
41
+ entries(): IterableIterator<[string, string]>;
42
+ /** @returns {[string, string][]} - Name/value pairs preserving original casing. */
43
+ toArray(): [string, string][];
44
+ /**
45
+ * Plain object form keyed by the original header casing. Suitable for
46
+ * passing straight to `http.request` or `fetch`.
47
+ * @returns {Record<string, string>} - Header object.
48
+ */
49
+ toObject(): Record<string, string>;
50
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Joins a base URL and a path and appends query parameters. `path` may be a
3
+ * fully-qualified URL, in which case the base is ignored.
4
+ * @param {string | undefined} baseUrl - Origin (and optional base path).
5
+ * @param {string} path - Path or absolute URL.
6
+ * @param {Record<string, string | number | boolean | null | undefined> | undefined} query - Query parameters.
7
+ * @returns {string} - Resolved absolute URL.
8
+ */
9
+ export function buildUrl(baseUrl: string | undefined, path: string, query: Record<string, string | number | boolean | null | undefined> | undefined): string;
10
+ /**
11
+ * Determines whether a value should be sent as a streamed request body.
12
+ * @param {unknown} body - Candidate body value.
13
+ * @returns {boolean} - Whether the body is a stream.
14
+ */
15
+ export function isStreamBody(body: unknown): boolean;
16
+ /**
17
+ * Normalizes a user-supplied body into one of a small set of shapes and applies
18
+ * a default `Content-Type` header when the caller did not set one.
19
+ * @param {unknown} body - Raw body value.
20
+ * @param {SnapReqHeaders} headers - Headers to receive a default `Content-Type`.
21
+ * @returns {NormalizedBody} - Normalized body descriptor.
22
+ */
23
+ export function normalizeBody(body: unknown, headers: SnapReqHeaders): NormalizedBody;
24
+ export type CompressionEncoding = "identity" | "gzip" | "deflate" | "br" | "zstd";
25
+ export type NormalizedBody = {
26
+ /**
27
+ * - Shape of the body payload.
28
+ */
29
+ kind: "none" | "text" | "bytes" | "stream";
30
+ /**
31
+ * - The payload itself.
32
+ */
33
+ value: string | Uint8Array | AsyncIterable<Uint8Array> | null;
34
+ };
35
+ import SnapReqHeaders from "./headers.js";
@@ -0,0 +1,74 @@
1
+ /**
2
+ * A platform-agnostic response. Transports build it with either a fully-read
3
+ * body (`bytes`) or a `stream` (an async iterable of `Uint8Array`) that the
4
+ * read helpers buffer on first use. The body can be read exactly once as a
5
+ * stream; the buffering helpers may be called repeatedly because they cache.
6
+ */
7
+ export default class SnapReqResponse {
8
+ /**
9
+ * @param {object} options - Response data.
10
+ * @param {string} options.url - Fully resolved request URL.
11
+ * @param {string} options.method - HTTP method used for the request.
12
+ * @param {number} options.status - HTTP status code.
13
+ * @param {string} [options.statusText] - HTTP status text.
14
+ * @param {SnapReqHeaders} [options.headers] - Response headers.
15
+ * @param {Uint8Array} [options.bytes] - Fully-read body, when the transport already buffered it.
16
+ * @param {AsyncIterable<Uint8Array>} [options.stream] - Streamed body, when the transport supports streaming.
17
+ * @param {import("node:stream").Readable} [options.nodeStream] - Raw Node stream, when available, for advanced consumers.
18
+ */
19
+ constructor({ url, method, status, statusText, headers, bytes, stream, nodeStream }: {
20
+ url: string;
21
+ method: string;
22
+ status: number;
23
+ statusText?: string;
24
+ headers?: SnapReqHeaders;
25
+ bytes?: Uint8Array;
26
+ stream?: AsyncIterable<Uint8Array>;
27
+ nodeStream?: import("node:stream").Readable;
28
+ });
29
+ url: string;
30
+ method: string;
31
+ status: number;
32
+ statusText: string;
33
+ headers: SnapReqHeaders;
34
+ /** @type {Uint8Array | null} */
35
+ _bytes: Uint8Array | null;
36
+ /** @type {AsyncIterable<Uint8Array> | null} */
37
+ _stream: AsyncIterable<Uint8Array> | null;
38
+ /** @type {import("node:stream").Readable | undefined} */
39
+ nodeStream: import("node:stream").Readable | undefined;
40
+ _streamConsumed: boolean;
41
+ /** @returns {boolean} - Whether the status is in the 2xx range. */
42
+ get ok(): boolean;
43
+ /**
44
+ * Returns the response body as an async iterable of byte chunks. Can only be
45
+ * called once and only when the transport provided a stream.
46
+ * @returns {AsyncIterable<Uint8Array>} - The streamed body.
47
+ */
48
+ stream(): AsyncIterable<Uint8Array>;
49
+ /** @returns {boolean} - Whether the body is available as a stream that has not been read yet. */
50
+ get streamable(): boolean;
51
+ /**
52
+ * Reads the whole body into a `Uint8Array`, buffering the stream if needed.
53
+ * @returns {Promise<Uint8Array>} - The full response body.
54
+ */
55
+ bytes(): Promise<Uint8Array>;
56
+ /**
57
+ * Reads the whole body as a Node `Buffer`. Node-only convenience; throws when
58
+ * the `Buffer` global is unavailable.
59
+ * @returns {Promise<Buffer>} - The full response body as a Buffer.
60
+ */
61
+ buffer(): Promise<Buffer>;
62
+ /**
63
+ * Reads the whole body and decodes it as a UTF-8 string.
64
+ * @returns {Promise<string>} - The decoded response body.
65
+ */
66
+ text(): Promise<string>;
67
+ /**
68
+ * Reads the whole body and parses it as JSON. Returns `null` for an empty
69
+ * body.
70
+ * @returns {Promise<any>} - The parsed JSON body.
71
+ */
72
+ json(): Promise<any>;
73
+ }
74
+ import SnapReqHeaders from "./headers.js";
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @typedef {object} RetryOptions
3
+ * @property {number} [tries] - Maximum number of attempts. Defaults to 3.
4
+ * @property {number} [waitMs] - Delay between attempts in milliseconds. Defaults to 500.
5
+ * @property {number[]} [retryableStatuses] - HTTP status codes that should be retried. Defaults to 502/503/504.
6
+ * @property {(error: unknown, attempt: number) => boolean} [shouldRetry] - Override the retryable-error classifier.
7
+ */
8
+ /**
9
+ * @typedef {object} NormalizedRetryOptions
10
+ * @property {number} tries - Maximum number of attempts.
11
+ * @property {number} waitMs - Delay between attempts in milliseconds.
12
+ * @property {number[]} retryableStatuses - HTTP status codes that should be retried.
13
+ * @property {(error: unknown, attempt: number) => boolean} shouldRetry - Retryable-error classifier.
14
+ */
15
+ /**
16
+ * The default network-error classifier. Exposed so callers can compose extra
17
+ * rules on top of it (for example matching server-specific 500 messages).
18
+ * @param {unknown} error - Error thrown by a transport.
19
+ * @returns {boolean} - Whether the error is a transient network failure.
20
+ */
21
+ export function defaultRetryableError(error: unknown): boolean;
22
+ /**
23
+ * Normalizes the `retry` option into a complete set of retry settings, or
24
+ * `null` when retries are disabled.
25
+ * @param {boolean | RetryOptions | undefined} retry - Retry configuration.
26
+ * @returns {NormalizedRetryOptions | null} - Normalized retry settings.
27
+ */
28
+ export function normalizeRetryOptions(retry: boolean | RetryOptions | undefined): NormalizedRetryOptions | null;
29
+ /**
30
+ * Runs a request attempt, retrying transient network errors and retryable HTTP
31
+ * statuses. Retries are only used for buffered requests — the caller must not
32
+ * apply this to streamed responses.
33
+ * @param {() => Promise<import("./response.js").default>} attempt - Performs one request attempt.
34
+ * @param {NormalizedRetryOptions} retry - Normalized retry settings.
35
+ * @returns {Promise<import("./response.js").default>} - The successful (or final) response.
36
+ */
37
+ export function runWithRetry(attempt: () => Promise<import("./response.js").default>, retry: NormalizedRetryOptions): Promise<import("./response.js").default>;
38
+ export type RetryOptions = {
39
+ /**
40
+ * - Maximum number of attempts. Defaults to 3.
41
+ */
42
+ tries?: number;
43
+ /**
44
+ * - Delay between attempts in milliseconds. Defaults to 500.
45
+ */
46
+ waitMs?: number;
47
+ /**
48
+ * - HTTP status codes that should be retried. Defaults to 502/503/504.
49
+ */
50
+ retryableStatuses?: number[];
51
+ /**
52
+ * - Override the retryable-error classifier.
53
+ */
54
+ shouldRetry?: (error: unknown, attempt: number) => boolean;
55
+ };
56
+ export type NormalizedRetryOptions = {
57
+ /**
58
+ * - Maximum number of attempts.
59
+ */
60
+ tries: number;
61
+ /**
62
+ * - Delay between attempts in milliseconds.
63
+ */
64
+ waitMs: number;
65
+ /**
66
+ * - HTTP status codes that should be retried.
67
+ */
68
+ retryableStatuses: number[];
69
+ /**
70
+ * - Retryable-error classifier.
71
+ */
72
+ shouldRetry: (error: unknown, attempt: number) => boolean;
73
+ };