@torthu/jacketui-bring 0.0.1

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.
Files changed (132) hide show
  1. package/README.md +11 -0
  2. package/dist/bring.d.ts +38 -0
  3. package/dist/bring.js +142 -0
  4. package/dist/errors/AbortError.d.ts +11 -0
  5. package/dist/errors/AbortError.js +14 -0
  6. package/dist/errors/BringError.d.ts +15 -0
  7. package/dist/errors/BringError.js +14 -0
  8. package/dist/errors/ClientError.d.ts +11 -0
  9. package/dist/errors/ClientError.js +19 -0
  10. package/dist/errors/NetworkError.d.ts +11 -0
  11. package/dist/errors/NetworkError.js +18 -0
  12. package/dist/errors/ServerError.d.ts +11 -0
  13. package/dist/errors/ServerError.js +19 -0
  14. package/dist/errors/TimeoutError.d.ts +11 -0
  15. package/dist/errors/TimeoutError.js +14 -0
  16. package/dist/errors/index.d.ts +6 -0
  17. package/dist/errors/index.js +6 -0
  18. package/dist/fetch.d.ts +30 -0
  19. package/dist/fetch.js +32 -0
  20. package/dist/helpers/exponentialBackoff.d.ts +21 -0
  21. package/dist/helpers/exponentialBackoff.js +18 -0
  22. package/dist/helpers/extractCallbacks.d.ts +2 -0
  23. package/dist/helpers/extractCallbacks.js +12 -0
  24. package/dist/helpers/extractRequestInit.d.ts +2 -0
  25. package/dist/helpers/extractRequestInit.js +16 -0
  26. package/dist/helpers/randomJitter.d.ts +15 -0
  27. package/dist/helpers/randomJitter.js +14 -0
  28. package/dist/helpers/shouldRetry.d.ts +22 -0
  29. package/dist/helpers/shouldRetry.js +28 -0
  30. package/dist/index.d.ts +4 -0
  31. package/dist/index.js +4 -0
  32. package/dist/pipe-helpers/backoff.d.ts +14 -0
  33. package/dist/pipe-helpers/backoff.js +16 -0
  34. package/dist/pipe-helpers/body.d.ts +14 -0
  35. package/dist/pipe-helpers/body.js +12 -0
  36. package/dist/pipe-helpers/cache.d.ts +17 -0
  37. package/dist/pipe-helpers/cache.js +18 -0
  38. package/dist/pipe-helpers/cors.d.ts +33 -0
  39. package/dist/pipe-helpers/cors.js +34 -0
  40. package/dist/pipe-helpers/credentials.d.ts +25 -0
  41. package/dist/pipe-helpers/credentials.js +26 -0
  42. package/dist/pipe-helpers/header.d.ts +13 -0
  43. package/dist/pipe-helpers/header.js +25 -0
  44. package/dist/pipe-helpers/index.d.ts +17 -0
  45. package/dist/pipe-helpers/index.js +17 -0
  46. package/dist/pipe-helpers/integrity.d.ts +17 -0
  47. package/dist/pipe-helpers/integrity.js +18 -0
  48. package/dist/pipe-helpers/jitter.d.ts +13 -0
  49. package/dist/pipe-helpers/jitter.js +15 -0
  50. package/dist/pipe-helpers/jsonBody.d.ts +10 -0
  51. package/dist/pipe-helpers/jsonBody.js +8 -0
  52. package/dist/pipe-helpers/keepalive.d.ts +15 -0
  53. package/dist/pipe-helpers/keepalive.js +16 -0
  54. package/dist/pipe-helpers/method.d.ts +12 -0
  55. package/dist/pipe-helpers/method.js +13 -0
  56. package/dist/pipe-helpers/priority.d.ts +15 -0
  57. package/dist/pipe-helpers/priority.js +15 -0
  58. package/dist/pipe-helpers/redirect.d.ts +17 -0
  59. package/dist/pipe-helpers/redirect.js +18 -0
  60. package/dist/pipe-helpers/referrer.d.ts +15 -0
  61. package/dist/pipe-helpers/referrer.js +16 -0
  62. package/dist/pipe-helpers/referrerPolicy.d.ts +25 -0
  63. package/dist/pipe-helpers/referrerPolicy.js +26 -0
  64. package/dist/pipe-helpers/retry.d.ts +10 -0
  65. package/dist/pipe-helpers/retry.js +8 -0
  66. package/dist/pipe-helpers/shouldRetry.d.ts +8 -0
  67. package/dist/pipe-helpers/shouldRetry.js +6 -0
  68. package/dist/pipe-helpers/timeout.d.ts +16 -0
  69. package/dist/pipe-helpers/timeout.js +14 -0
  70. package/dist/tryFetch.d.ts +3 -0
  71. package/dist/tryFetch.js +19 -0
  72. package/dist/types/BringDecorator.d.ts +4 -0
  73. package/dist/types/BringDecorator.js +1 -0
  74. package/dist/types/BringInit.d.ts +37 -0
  75. package/dist/types/BringInit.js +1 -0
  76. package/dist/types/ClientErrorResponse.d.ts +6 -0
  77. package/dist/types/ClientErrorResponse.js +32 -0
  78. package/dist/types/InformationalResponse.d.ts +6 -0
  79. package/dist/types/InformationalResponse.js +8 -0
  80. package/dist/types/RedirectResponse.d.ts +6 -0
  81. package/dist/types/RedirectResponse.js +9 -0
  82. package/dist/types/ServerErrorResponse.d.ts +6 -0
  83. package/dist/types/ServerErrorResponse.js +24 -0
  84. package/dist/types/SuccessResponse.d.ts +13 -0
  85. package/dist/types/SuccessResponse.js +20 -0
  86. package/dist/types/statusCodes.d.ts +69 -0
  87. package/dist/types/statusCodes.js +69 -0
  88. package/package.json +23 -0
  89. package/src/bring.ts +198 -0
  90. package/src/errors/AbortError.ts +18 -0
  91. package/src/errors/BringError.ts +24 -0
  92. package/src/errors/ClientError.ts +24 -0
  93. package/src/errors/NetworkError.ts +23 -0
  94. package/src/errors/ServerError.ts +24 -0
  95. package/src/errors/TimeoutError.ts +18 -0
  96. package/src/errors/index.ts +6 -0
  97. package/src/fetch.ts +34 -0
  98. package/src/helpers/exponentialBackoff.ts +28 -0
  99. package/src/helpers/extractCallbacks.ts +23 -0
  100. package/src/helpers/extractRequestInit.ts +30 -0
  101. package/src/helpers/randomJitter.ts +22 -0
  102. package/src/helpers/shouldRetry.ts +40 -0
  103. package/src/index.ts +4 -0
  104. package/src/pipe-helpers/backoff.ts +22 -0
  105. package/src/pipe-helpers/body.ts +17 -0
  106. package/src/pipe-helpers/cache.ts +23 -0
  107. package/src/pipe-helpers/cors.ts +39 -0
  108. package/src/pipe-helpers/credentials.ts +33 -0
  109. package/src/pipe-helpers/header.ts +28 -0
  110. package/src/pipe-helpers/index.ts +18 -0
  111. package/src/pipe-helpers/integrity.ts +23 -0
  112. package/src/pipe-helpers/jitter.ts +17 -0
  113. package/src/pipe-helpers/jsonBody.ts +13 -0
  114. package/src/pipe-helpers/keepalive.ts +21 -0
  115. package/src/pipe-helpers/method.ts +18 -0
  116. package/src/pipe-helpers/priority.ts +20 -0
  117. package/src/pipe-helpers/redirect.ts +23 -0
  118. package/src/pipe-helpers/referrer.ts +21 -0
  119. package/src/pipe-helpers/referrerPolicy.ts +33 -0
  120. package/src/pipe-helpers/retry.ts +13 -0
  121. package/src/pipe-helpers/shouldRetry.ts +11 -0
  122. package/src/pipe-helpers/timeout.ts +19 -0
  123. package/src/tryFetch.ts +32 -0
  124. package/src/types/BringDecorator.ts +5 -0
  125. package/src/types/BringInit.ts +78 -0
  126. package/src/types/ClientErrorResponse.ts +72 -0
  127. package/src/types/InformationalResponse.ts +20 -0
  128. package/src/types/RedirectResponse.ts +20 -0
  129. package/src/types/ServerErrorResponse.ts +56 -0
  130. package/src/types/SuccessResponse.ts +57 -0
  131. package/src/types/statusCodes.ts +69 -0
  132. package/tsconfig.json +11 -0
@@ -0,0 +1,16 @@
1
+ import { BringInitDecorator } from "../types/BringDecorator";
2
+ import { BringInit } from "../types/BringInit";
3
+ /** timeout(number): BringInitDecorator
4
+ *
5
+ * Sets in-flight timeout for request.
6
+ * Used to configure AbortSignal.timeout().
7
+ *
8
+ * If retry is set, timeout will be applied to each retry.
9
+ * Total time for request will be (timeout + jitter + backoff) * retry.
10
+ *
11
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static
12
+ *
13
+ * @param timeout The time to wait before aborting the request.
14
+ * @returns BringInitDecorator
15
+ */
16
+ export declare const timeout: (timeout: BringInit["timeout"]) => BringInitDecorator;
@@ -0,0 +1,14 @@
1
+ /** timeout(number): BringInitDecorator
2
+ *
3
+ * Sets in-flight timeout for request.
4
+ * Used to configure AbortSignal.timeout().
5
+ *
6
+ * If retry is set, timeout will be applied to each retry.
7
+ * Total time for request will be (timeout + jitter + backoff) * retry.
8
+ *
9
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static
10
+ *
11
+ * @param timeout The time to wait before aborting the request.
12
+ * @returns BringInitDecorator
13
+ */
14
+ export const timeout = (timeout) => (init) => ({ ...init, timeout });
@@ -0,0 +1,3 @@
1
+ import { JuiError, Result } from "@torthu/jacketui-core";
2
+ import { BringInit } from "./types/BringInit";
3
+ export declare const tryFetch: (url: string | URL, requestInit: RequestInit, init: BringInit, retryCount?: number) => Promise<Result<Response, JuiError>>;
@@ -0,0 +1,19 @@
1
+ import { failure, sleep, success, tryAwait, } from "@torthu/jacketui-core";
2
+ export const tryFetch = async (url, requestInit, init, retryCount = 0) => {
3
+ const { error, value } = await tryAwait(fetch(url, requestInit));
4
+ if (error && retryCount < (init.retry ?? 0)) {
5
+ init.onRetry?.(retryCount);
6
+ const backoff = init.backoff?.(retryCount) ?? 0;
7
+ const jitter = init.jitter?.() ?? 0;
8
+ if (backoff + jitter > 0) {
9
+ await sleep(backoff + jitter);
10
+ }
11
+ return await tryFetch(url, requestInit, init, retryCount + 1);
12
+ }
13
+ else if (error) {
14
+ return failure(error);
15
+ }
16
+ else {
17
+ return success(value);
18
+ }
19
+ };
@@ -0,0 +1,4 @@
1
+ import { BringInit } from "./BringInit";
2
+ export interface BringInitDecorator {
3
+ (init: BringInit): BringInit;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import { AbortError, BringError, TimeoutError } from "../errors";
2
+ type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array;
3
+ export interface BringCallbacks {
4
+ onAbort?: (error: AbortError) => void;
5
+ onError?: (error: BringError) => void;
6
+ onRetry?: (retryNum: number) => void;
7
+ onSuccess?: (response: Response) => void;
8
+ onTimeout?: (error: TimeoutError) => void;
9
+ onClientError?: (response: Response) => void;
10
+ onServerError?: (response: Response) => void;
11
+ }
12
+ export interface RequestInit {
13
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
14
+ body?: string | ArrayBuffer | Blob | DataView | File | FormData | TypedArray | URLSearchParams | ReadableStream;
15
+ cache?: "default" | "no-store" | "reload" | "no-cache" | "force-cache" | "only-if-cached";
16
+ credentials?: "omit" | "same-origin" | "include";
17
+ headers?: Record<string, string> | Headers;
18
+ integrity?: string;
19
+ keepalive?: boolean;
20
+ mode?: "cors" | "no-cors" | "same-origin";
21
+ priority?: "low" | "high" | "auto";
22
+ redirect?: "follow" | "error" | "manual";
23
+ referrer?: string | "" | "about:client";
24
+ referrerPolicy?: "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "same-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url";
25
+ }
26
+ export interface RetryBackoff {
27
+ retry?: number;
28
+ timeout?: number;
29
+ shouldRetry?: (response: Response, retryNum: number) => boolean;
30
+ backoff?: (retryNum: number) => number;
31
+ jitter?: () => number;
32
+ }
33
+ export type BringInit = RequestInit & RetryBackoff & BringCallbacks & {
34
+ url: string | URL;
35
+ abortController?: AbortController;
36
+ };
37
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ type ClientErrorStatus = 400 | 401 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451;
2
+ export interface ClientErrorResponse extends Response {
3
+ status: ClientErrorStatus;
4
+ }
5
+ export declare const isClientErrorResponse: (res: Response) => res is ClientErrorResponse;
6
+ export {};
@@ -0,0 +1,32 @@
1
+ const clientStatusErrorMap = {
2
+ 400: "Bad Request",
3
+ 401: "Unauthorized",
4
+ 403: "Forbidden",
5
+ 404: "Not Found",
6
+ 405: "Method Not Allowed",
7
+ 406: "Not Acceptable",
8
+ 407: "Proxy Authentication Required",
9
+ 408: "Request Timeout",
10
+ 409: "Conflict",
11
+ 410: "Gone",
12
+ 411: "Length Required",
13
+ 412: "Precondition Failed",
14
+ 413: "Payload Too Large",
15
+ 414: "URI Too Long",
16
+ 415: "Unsupported Media Type",
17
+ 416: "Range Not Satisfiable",
18
+ 417: "Expectation Failed",
19
+ 418: "I'm a teapot",
20
+ 421: "Misdirected Request",
21
+ 422: "Unprocessable Entity",
22
+ 423: "Locked",
23
+ 424: "Failed Dependency",
24
+ 425: "Too Early",
25
+ 426: "Upgrade Required",
26
+ 428: "Precondition Required",
27
+ 429: "Too Many Requests",
28
+ 431: "Request Header Fields Too Large",
29
+ 451: "Unavailable For Legal Reasons",
30
+ };
31
+ const clientStatusErrorSet = new Set(Object.keys(clientStatusErrorMap).map((s) => parseInt(s, 10)));
32
+ export const isClientErrorResponse = (res) => clientStatusErrorSet.has(res.status);
@@ -0,0 +1,6 @@
1
+ type InformationalResponseStatus = 100 | 101 | 102 | 103;
2
+ export interface InformationalResponse extends Response {
3
+ status: InformationalResponseStatus;
4
+ }
5
+ export declare const isInformationalResponse: (res: Response) => res is InformationalResponse;
6
+ export {};
@@ -0,0 +1,8 @@
1
+ const informationalResponseMap = {
2
+ 100: "Continue",
3
+ 101: "Switching Protocols",
4
+ 102: "Processing",
5
+ 103: "Early Hints",
6
+ };
7
+ const informationalResponseSet = new Set(Object.keys(informationalResponseMap).map((s) => parseInt(s, 10)));
8
+ export const isInformationalResponse = (res) => informationalResponseSet.has(res.status);
@@ -0,0 +1,6 @@
1
+ type RedirectStatus = 301 | 302 | 303 | 307 | 308;
2
+ export interface RedirectResponse extends Response {
3
+ status: RedirectStatus;
4
+ }
5
+ export declare const isRedirectResponse: (res: Response) => res is RedirectResponse;
6
+ export {};
@@ -0,0 +1,9 @@
1
+ const redirectMap = {
2
+ 301: "Moved Permanently",
3
+ 302: "Found",
4
+ 303: "See Other",
5
+ 307: "Temporary Redirect",
6
+ 308: "Permanent Redirect",
7
+ };
8
+ const redirectSet = new Set(Object.keys(redirectMap).map((s) => parseInt(s, 10)));
9
+ export const isRedirectResponse = (res) => redirectSet.has(res.status);
@@ -0,0 +1,6 @@
1
+ type ServerErrorStatus = 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 530;
2
+ export interface ServerErrorResponse extends Response {
3
+ status: ServerErrorStatus;
4
+ }
5
+ export declare const isServerErrorResponse: (res: Response) => res is ServerErrorResponse;
6
+ export {};
@@ -0,0 +1,24 @@
1
+ const serverStatusErrorMap = {
2
+ 500: "Internal Server Error",
3
+ 501: "Not Implemented",
4
+ 502: "Bad Gateway",
5
+ 503: "Service Unavailable",
6
+ 504: "Gateway Timeout",
7
+ 505: "HTTP Version Not Supported",
8
+ 506: "Variant Also Negotiates",
9
+ 507: "Insufficient Storage",
10
+ 508: "Loop Detected",
11
+ 510: "Not Extended",
12
+ 511: "Network Authentication Required",
13
+ 520: "Unknown Error",
14
+ 521: "Web Server Is Down",
15
+ 522: "Connection Timed Out",
16
+ 523: "Origin Is Unreachable",
17
+ 524: "A Timeout Occurred",
18
+ 525: "SSL Handshake Failed",
19
+ 526: "Invalid SSL Certificate",
20
+ 527: "Railgun Error",
21
+ 530: "Site Is Frozen",
22
+ };
23
+ const serverStatusErrorSet = new Set(Object.keys(serverStatusErrorMap).map((s) => parseInt(s, 10)));
24
+ export const isServerErrorResponse = (res) => serverStatusErrorSet.has(res.status);
@@ -0,0 +1,13 @@
1
+ type KnownSuccessStatus = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226;
2
+ export interface KnownSuccessResponse extends Response {
3
+ status: KnownSuccessStatus;
4
+ }
5
+ export declare const isKnownSuccessResponse: (res: Response) => res is KnownSuccessResponse;
6
+ export interface UnknownSuccessResponse extends Response {
7
+ status: number;
8
+ }
9
+ export declare const isUnknownSuccessResponse: (res: Response) => res is UnknownSuccessResponse;
10
+ export type SuccessResponse = KnownSuccessResponse | UnknownSuccessResponse;
11
+ export declare const isSuccessResponse: (res: Response) => res is SuccessResponse;
12
+ export declare const getSuccessStatusText: (res: SuccessResponse) => string;
13
+ export {};
@@ -0,0 +1,20 @@
1
+ const knownSuccessStatusMap = {
2
+ 200: "OK",
3
+ 201: "Created",
4
+ 202: "Accepted",
5
+ 203: "Non-Authoritative Information",
6
+ 204: "No Content",
7
+ 205: "Reset Content",
8
+ 206: "Partial Content",
9
+ 207: "Multi-Status",
10
+ 208: "Already Reported",
11
+ 226: "IM Used",
12
+ };
13
+ const unknownSuccessStatusText = "Unknown Success";
14
+ const knownSuccessStatusSet = new Set(Object.keys(knownSuccessStatusMap).map((s) => parseInt(s, 10)));
15
+ export const isKnownSuccessResponse = (res) => knownSuccessStatusSet.has(res.status);
16
+ export const isUnknownSuccessResponse = (res) => res.status >= 200 && res.status < 300 && !isKnownSuccessResponse(res);
17
+ export const isSuccessResponse = (res) => res.status >= 200 && res.status < 300;
18
+ export const getSuccessStatusText = (res) => isKnownSuccessResponse(res)
19
+ ? knownSuccessStatusMap[res.status]
20
+ : unknownSuccessStatusText;
@@ -0,0 +1,69 @@
1
+ export declare const statusCodes: {
2
+ 100: string;
3
+ 101: string;
4
+ 102: string;
5
+ 103: string;
6
+ 200: string;
7
+ 201: string;
8
+ 202: string;
9
+ 203: string;
10
+ 204: string;
11
+ 205: string;
12
+ 206: string;
13
+ 207: string;
14
+ 208: string;
15
+ 226: string;
16
+ 301: string;
17
+ 302: string;
18
+ 303: string;
19
+ 307: string;
20
+ 308: string;
21
+ 400: string;
22
+ 401: string;
23
+ 403: string;
24
+ 404: string;
25
+ 405: string;
26
+ 406: string;
27
+ 407: string;
28
+ 408: string;
29
+ 409: string;
30
+ 410: string;
31
+ 411: string;
32
+ 412: string;
33
+ 413: string;
34
+ 414: string;
35
+ 415: string;
36
+ 416: string;
37
+ 417: string;
38
+ 418: string;
39
+ 421: string;
40
+ 422: string;
41
+ 423: string;
42
+ 424: string;
43
+ 425: string;
44
+ 426: string;
45
+ 428: string;
46
+ 429: string;
47
+ 431: string;
48
+ 451: string;
49
+ 500: string;
50
+ 501: string;
51
+ 502: string;
52
+ 503: string;
53
+ 504: string;
54
+ 505: string;
55
+ 506: string;
56
+ 507: string;
57
+ 508: string;
58
+ 510: string;
59
+ 511: string;
60
+ 520: string;
61
+ 521: string;
62
+ 522: string;
63
+ 523: string;
64
+ 524: string;
65
+ 525: string;
66
+ 526: string;
67
+ 527: string;
68
+ 530: string;
69
+ };
@@ -0,0 +1,69 @@
1
+ export const statusCodes = {
2
+ 100: "Continue",
3
+ 101: "Switching Protocols",
4
+ 102: "Processing",
5
+ 103: "Early Hints",
6
+ 200: "OK",
7
+ 201: "Created",
8
+ 202: "Accepted",
9
+ 203: "Non-Authoritative Information",
10
+ 204: "No Content",
11
+ 205: "Reset Content",
12
+ 206: "Partial Content",
13
+ 207: "Multi-Status",
14
+ 208: "Already Reported",
15
+ 226: "IM Used",
16
+ 301: "Moved Permanently",
17
+ 302: "Found",
18
+ 303: "See Other",
19
+ 307: "Temporary Redirect",
20
+ 308: "Permanent Redirect",
21
+ 400: "Bad Request",
22
+ 401: "Unauthorized",
23
+ 403: "Forbidden",
24
+ 404: "Not Found",
25
+ 405: "Method Not Allowed",
26
+ 406: "Not Acceptable",
27
+ 407: "Proxy Authentication Required",
28
+ 408: "Request Timeout",
29
+ 409: "Conflict",
30
+ 410: "Gone",
31
+ 411: "Length Required",
32
+ 412: "Precondition Failed",
33
+ 413: "Payload Too Large",
34
+ 414: "URI Too Long",
35
+ 415: "Unsupported Media Type",
36
+ 416: "Range Not Satisfiable",
37
+ 417: "Expectation Failed",
38
+ 418: "I'm a teapot",
39
+ 421: "Misdirected Request",
40
+ 422: "Unprocessable Entity",
41
+ 423: "Locked",
42
+ 424: "Failed Dependency",
43
+ 425: "Too Early",
44
+ 426: "Upgrade Required",
45
+ 428: "Precondition Required",
46
+ 429: "Too Many Requests",
47
+ 431: "Request Header Fields Too Large",
48
+ 451: "Unavailable For Legal Reasons",
49
+ 500: "Internal Server Error",
50
+ 501: "Not Implemented",
51
+ 502: "Bad Gateway",
52
+ 503: "Service Unavailable",
53
+ 504: "Gateway Timeout",
54
+ 505: "HTTP Version Not Supported",
55
+ 506: "Variant Also Negotiates",
56
+ 507: "Insufficient Storage",
57
+ 508: "Loop Detected",
58
+ 510: "Not Extended",
59
+ 511: "Network Authentication Required",
60
+ 520: "Unknown Error",
61
+ 521: "Web Server Is Down",
62
+ 522: "Connection Timed Out",
63
+ 523: "Origin Is Unreachable",
64
+ 524: "A Timeout Occurred",
65
+ 525: "SSL Handshake Failed",
66
+ 526: "Invalid SSL Certificate",
67
+ 527: "Railgun Error",
68
+ 530: "Site Is Frozen",
69
+ };
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@torthu/jacketui-bring",
3
+ "version": "0.0.1",
4
+ "description": "Improved fetch API with retry, timeout, and more.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "author": {
8
+ "name": "Torstein Thune",
9
+ "email": "torstein@thune.io",
10
+ "url": "https://thune.io"
11
+ },
12
+ "peerDependencies": {},
13
+ "devDependencies": {},
14
+ "dependencies": {
15
+ "@torthu/jacketui-core": "latest"
16
+ },
17
+ "scripts": {
18
+ "prepublish": "npm run build",
19
+ "build": "rimraf ./dist && tsc",
20
+ "test": "vitest run"
21
+ },
22
+ "license": "GPL-3.0-only"
23
+ }
package/src/bring.ts ADDED
@@ -0,0 +1,198 @@
1
+ import {
2
+ Result,
3
+ success,
4
+ failure,
5
+ AbortablePromise,
6
+ withResolvers,
7
+ } from "@torthu/jacketui-core";
8
+ import { isClientErrorResponse } from "./types/ClientErrorResponse";
9
+ import { BringInit } from "./types/BringInit";
10
+ import { isInformationalResponse } from "./types/InformationalResponse";
11
+ import { isRedirectResponse } from "./types/RedirectResponse";
12
+ import { isServerErrorResponse } from "./types/ServerErrorResponse";
13
+ import {
14
+ AbortError,
15
+ ClientError,
16
+ BringError,
17
+ NetworkError,
18
+ ServerError,
19
+ TimeoutError,
20
+ } from "./errors";
21
+ import { tryFetch } from "./tryFetch";
22
+
23
+ /** bring({url: string | URL})
24
+ *
25
+ * A wrapper around the fetch API that adds support for timeouts, retries, and aborts.
26
+ * It also adds support for custom error handling and success handling.
27
+ * It returns a promise that resolves to a Result object.
28
+ * The returned promise is extended with an abort method that can be used to abort the fetch request.
29
+ *
30
+ * The Result object contains either a Response object or a BringError.
31
+ *
32
+ * @param init {BringInit | BringError} RequestInit + retries, timeout, onSuccess, onError, onAbort, onRetry, onTimeout, onClientError, onServerError
33
+ * @param init.url The URL to fetch.
34
+ * @param init.abortController (optional) An AbortController object to abort the fetch request. (default: new AbortController())
35
+ * @param init.timeout (optional) The timeout in milliseconds. (default: 0)
36
+ * @param init.retry (optional) The number of times to retry the fetch request. (default: 0)
37
+ * @param init.onSuccess (optional) A callback function that is called when the fetch request is successful.
38
+ * @param init.onError (optional) A callback function that is called when the fetch request fails.
39
+ * @param init.onAbort (optional) A callback function that is called when the fetch request is aborted.
40
+ * @param init.onRetry (optional) A callback function that is called when the fetch request is retried.
41
+ * @param init.onTimeout (optional) A callback function that is called when the fetch request times out.
42
+ * @param init.onClientError (optional) A callback function that is called when the fetch request fails with a client error.
43
+ * @param init.onServerError (optional) A callback function that is called when the fetch request fails with a server error.
44
+ * @param init.body (optional) The body of the fetch request.
45
+ * @param init.headers (optional) The headers of the fetch request.
46
+ * @param init.method (optional) The method of the fetch request.
47
+ * @param init.mode (optional) The mode of the fetch request.
48
+ * @param init.credentials (optional) The credentials of the fetch request.
49
+ * @param init.cache (optional) The cache of the fetch request.
50
+ * @param init.redirect (optional) The redirect of the fetch request.
51
+ * @param init.referrer (optional) The referrer of the fetch request.
52
+ * @param init.referrerPolicy (optional) The referrerPolicy of the fetch request.
53
+ * @param init.integrity (optional) The integrity of the fetch request.
54
+ * @param init.keepalive (optional) The keepalive of the fetch request.
55
+ * @returns AbortablePromise<Result<Response, BringError>>
56
+ */
57
+ export const bring = (init: BringInit | BringError) => {
58
+ if (init instanceof BringError) init = init.requestInit;
59
+
60
+ const {
61
+ url,
62
+ abortController = new AbortController(),
63
+ timeout = 0,
64
+ retry = 0,
65
+ onSuccess,
66
+ onError,
67
+ onAbort,
68
+ onRetry,
69
+ onTimeout,
70
+ onClientError,
71
+ onServerError,
72
+ ...requestInit
73
+ } = init;
74
+
75
+ // Configure promise
76
+ const { promise, resolve } = withResolvers();
77
+ const abortablePromise = promise as AbortablePromise<
78
+ Result<Response, BringError>
79
+ >;
80
+ abortablePromise.abort = abortController.abort;
81
+
82
+ // Configure timeout signal
83
+ const timeoutSignal = timeout > 0 ? AbortSignal.timeout(timeout) : undefined;
84
+ const signal = AbortSignal.any(
85
+ [abortController.signal, timeoutSignal].filter((s) => !!s)
86
+ );
87
+
88
+ tryFetch(
89
+ url,
90
+ {
91
+ ...requestInit,
92
+ signal,
93
+ },
94
+ init,
95
+ 0
96
+ ).then(({ error, value }) => {
97
+ if (error) {
98
+ if (abortController.signal.aborted) {
99
+ const abortError = new AbortError(abortController.signal.reason, {
100
+ context: {
101
+ url: url,
102
+ method: requestInit.method ?? "GET",
103
+ reason: abortController.signal.reason,
104
+ },
105
+ url,
106
+ requestInit: init,
107
+ });
108
+ resolve(failure(abortError));
109
+ onAbort?.(abortError);
110
+ onError?.(abortError);
111
+ return;
112
+ }
113
+ if (timeoutSignal?.aborted) {
114
+ const error = new TimeoutError("Timeout", {
115
+ context: { reason: timeoutSignal.reason, timeout },
116
+ url,
117
+ requestInit: init,
118
+ });
119
+ resolve(failure(error));
120
+ onTimeout?.(error);
121
+ onError?.(error);
122
+ return;
123
+ }
124
+
125
+ if (error instanceof Error) {
126
+ resolve(
127
+ failure(
128
+ new BringError(error.message, {
129
+ cause: error,
130
+ context: { url, method: requestInit.method ?? "GET" },
131
+ url,
132
+ requestInit: init,
133
+ })
134
+ )
135
+ );
136
+ return;
137
+ }
138
+
139
+ if (typeof error === "string") {
140
+ resolve(failure(new BringError(error, { url, requestInit: init })));
141
+ return;
142
+ }
143
+
144
+ resolve(
145
+ failure(new BringError("Unknown error", { url, requestInit: init }))
146
+ );
147
+ } else {
148
+ const response = value!;
149
+
150
+ if (response.ok) {
151
+ resolve(success(response));
152
+ onSuccess?.(response);
153
+ } else {
154
+ if (isClientErrorResponse(response)) {
155
+ return failure(
156
+ new ClientError(response.statusText, {
157
+ context: {
158
+ status: response.status,
159
+ statusText: response.statusText,
160
+ },
161
+ response,
162
+ requestInit: init,
163
+ url,
164
+ })
165
+ );
166
+ }
167
+ if (isServerErrorResponse(response)) {
168
+ return failure(
169
+ new ServerError(response.statusText, {
170
+ response,
171
+ requestInit: init,
172
+ url,
173
+ context: {
174
+ status: response.status,
175
+ statusText: response.statusText,
176
+ },
177
+ })
178
+ );
179
+ }
180
+
181
+ const error = new BringError("Fetch failed", {
182
+ context: {
183
+ url,
184
+ status: response.status,
185
+ statusText: response.statusText,
186
+ },
187
+ url: url,
188
+ requestInit: init,
189
+ response,
190
+ });
191
+ resolve(failure(error));
192
+ onError?.(error);
193
+ }
194
+ }
195
+ });
196
+
197
+ return abortablePromise;
198
+ };
@@ -0,0 +1,18 @@
1
+ import { BringError } from "./BringError";
2
+
3
+ export class AbortError extends BringError {
4
+ tag = "AbortError";
5
+ }
6
+
7
+ /** isAbortError
8
+ *
9
+ * Checks if error is AbortError.
10
+ * An AbortError is either an instance of AbortError or a BringError with tag "AbortError".
11
+ *
12
+ */
13
+ export const isAbortError = (error: unknown): error is AbortError => {
14
+ return (
15
+ error instanceof AbortError ||
16
+ (error instanceof BringError && error.tag === "AbortError")
17
+ );
18
+ };
@@ -0,0 +1,24 @@
1
+ import { BringInit } from "../types/BringInit";
2
+ import { JuiError, JuiErrorOptions } from "@torthu/jacketui-core";
3
+
4
+ export interface BringErrorOptions extends JuiErrorOptions {
5
+ response?: Response;
6
+ request?: Request;
7
+ requestInit: BringInit;
8
+ url: string | URL;
9
+ }
10
+
11
+ export class BringError extends JuiError {
12
+ public readonly response?: BringErrorOptions["response"];
13
+ public readonly request?: BringErrorOptions["request"];
14
+ public readonly requestInit: BringErrorOptions["requestInit"];
15
+ public readonly url: BringErrorOptions["url"];
16
+
17
+ constructor(message: string, options: BringErrorOptions) {
18
+ super(message, options);
19
+ this.response = options.response;
20
+ this.request = options.request;
21
+ this.requestInit = options.requestInit;
22
+ this.url = options.url;
23
+ }
24
+ }