@ci-kit/http 0.0.0-stage → 0.0.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/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.mjs +62 -0
- package/package.json +33 -3
- package/README.md +0 -3
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface RequestOptions {
|
|
2
|
+
timeoutMs?: number;
|
|
3
|
+
retries?: number;
|
|
4
|
+
retryDelayMs?: number;
|
|
5
|
+
}
|
|
6
|
+
declare class HttpError extends Error {
|
|
7
|
+
readonly url: string;
|
|
8
|
+
readonly status: number;
|
|
9
|
+
readonly body: string;
|
|
10
|
+
constructor(url: string, status: number, body: string);
|
|
11
|
+
}
|
|
12
|
+
declare function isRetryable(error: unknown): boolean;
|
|
13
|
+
declare function request(url: string, init?: RequestInit, options?: RequestOptions): Promise<Response>;
|
|
14
|
+
declare function requestJson<T>(url: string, init?: RequestInit, options?: RequestOptions): Promise<T>;
|
|
15
|
+
declare function requestBuffer(url: string, init?: RequestInit, options?: RequestOptions): Promise<Uint8Array>;
|
|
16
|
+
|
|
17
|
+
export { HttpError, isRetryable, request, requestBuffer, requestJson };
|
|
18
|
+
export type { RequestOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface RequestOptions {
|
|
2
|
+
timeoutMs?: number;
|
|
3
|
+
retries?: number;
|
|
4
|
+
retryDelayMs?: number;
|
|
5
|
+
}
|
|
6
|
+
declare class HttpError extends Error {
|
|
7
|
+
readonly url: string;
|
|
8
|
+
readonly status: number;
|
|
9
|
+
readonly body: string;
|
|
10
|
+
constructor(url: string, status: number, body: string);
|
|
11
|
+
}
|
|
12
|
+
declare function isRetryable(error: unknown): boolean;
|
|
13
|
+
declare function request(url: string, init?: RequestInit, options?: RequestOptions): Promise<Response>;
|
|
14
|
+
declare function requestJson<T>(url: string, init?: RequestInit, options?: RequestOptions): Promise<T>;
|
|
15
|
+
declare function requestBuffer(url: string, init?: RequestInit, options?: RequestOptions): Promise<Uint8Array>;
|
|
16
|
+
|
|
17
|
+
export { HttpError, isRetryable, request, requestBuffer, requestJson };
|
|
18
|
+
export type { RequestOptions };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
class HttpError extends Error {
|
|
2
|
+
constructor(url, status, body) {
|
|
3
|
+
super(`HTTP ${status} ${url}`);
|
|
4
|
+
this.url = url;
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.body = body;
|
|
7
|
+
this.name = "HttpError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 6e4;
|
|
11
|
+
const DEFAULT_RETRIES = 2;
|
|
12
|
+
const DEFAULT_RETRY_DELAY_MS = 800;
|
|
13
|
+
function isRetryable(error) {
|
|
14
|
+
if (error instanceof HttpError) {
|
|
15
|
+
return error.status === 408 || error.status === 429 || error.status >= 500;
|
|
16
|
+
}
|
|
17
|
+
if (error instanceof Error) {
|
|
18
|
+
if (error.name === "TimeoutError" || error.name === "AbortError") {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return /fetch failed|ECONNRESET|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN/i.test(error.message);
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
async function request(url, init = {}, options = {}) {
|
|
26
|
+
const {
|
|
27
|
+
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
28
|
+
retries = DEFAULT_RETRIES,
|
|
29
|
+
retryDelayMs = DEFAULT_RETRY_DELAY_MS
|
|
30
|
+
} = options;
|
|
31
|
+
let lastError;
|
|
32
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
33
|
+
try {
|
|
34
|
+
const response = await fetch(url, {
|
|
35
|
+
...init,
|
|
36
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
37
|
+
});
|
|
38
|
+
if (!response.ok) {
|
|
39
|
+
const body = await response.text().catch(() => "");
|
|
40
|
+
throw new HttpError(url, response.status, body.slice(0, 500));
|
|
41
|
+
}
|
|
42
|
+
return response;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
lastError = error;
|
|
45
|
+
if (attempt === retries || !isRetryable(error)) {
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs * 2 ** attempt));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
throw lastError;
|
|
52
|
+
}
|
|
53
|
+
async function requestJson(url, init = {}, options = {}) {
|
|
54
|
+
const response = await request(url, init, options);
|
|
55
|
+
return await response.json();
|
|
56
|
+
}
|
|
57
|
+
async function requestBuffer(url, init = {}, options = {}) {
|
|
58
|
+
const response = await request(url, init, options);
|
|
59
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { HttpError, isRetryable, request, requestBuffer, requestJson };
|
package/package.json
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ci-kit/http",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"description": "Tiny fetch wrapper with timeout, retry and typed errors",
|
|
6
|
+
"author": "imba97",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/cikit-js/ci-kit#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/cikit-js/ci-kit.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/cikit-js/ci-kit/issues"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
19
|
+
"import": "./dist/index.mjs"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/index.mjs",
|
|
23
|
+
"module": "./dist/index.mjs",
|
|
24
|
+
"types": "./dist/index.d.mts",
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "unbuild",
|
|
33
|
+
"typecheck": "tsc --noEmit",
|
|
34
|
+
"stub": "unbuild --stub"
|
|
35
|
+
}
|
|
6
36
|
}
|
package/README.md
DELETED