@jamx/http 0.1.0 → 0.1.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.
- package/README.md +75 -4
- package/build/internal/core.d.ts +57 -12
- package/build/internal/core.d.ts.map +1 -1
- package/build/internal/core.js +49 -14
- package/build/internal/decoders.d.ts +2 -1
- package/build/internal/decoders.d.ts.map +1 -1
- package/build/internal/decoders.js +12 -10
- package/build/internal/interceptors.d.ts +38 -34
- package/build/internal/interceptors.d.ts.map +1 -1
- package/build/internal/interceptors.js +122 -90
- package/build/internal/statuses/validators.d.ts +15 -15
- package/build/internal/statuses/validators.d.ts.map +1 -1
- package/build/internal/statuses/validators.js +40 -16
- package/build/internal/types.d.ts +1 -1
- package/build/internal/types.d.ts.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineInterceptor
|
|
1
|
+
import { defineInterceptor } from "./core.js";
|
|
2
2
|
import { isErr, isOk, ok } from "./either.js";
|
|
3
3
|
/**
|
|
4
4
|
* Rebases a request URL onto a configured base URL while preserving the
|
|
@@ -6,79 +6,102 @@ import { isErr, isOk, ok } from "./either.js";
|
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
8
|
* ```ts
|
|
9
|
-
* import {
|
|
9
|
+
* import { compose, defaultFetch, withBaseUrl } from "@jamx/http";
|
|
10
10
|
*
|
|
11
|
-
* const fetcher =
|
|
12
|
-
* withBaseUrl("https://api.example.com/v1"),
|
|
13
|
-
* )(defaultFetch);
|
|
11
|
+
* const fetcher = compose(withBaseUrl("https://api.example.com/v1"))(defaultFetch);
|
|
14
12
|
* ```
|
|
15
13
|
*/
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
});
|
|
14
|
+
export function withBaseUrl(base_url) {
|
|
15
|
+
return defineInterceptor(async ({ request, next }) => {
|
|
16
|
+
const { input, init } = request;
|
|
17
|
+
const current_url = getUrl(input, base_url);
|
|
18
|
+
const url = new URL(`${trimTrailingSlash(base_url.toString())}/${trimLeadingSlash(`${current_url.pathname}${current_url.search}${current_url.hash}`)}`);
|
|
19
|
+
return next({
|
|
20
|
+
input: input instanceof Request ? new Request(url, input) : url,
|
|
21
|
+
init,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
21
25
|
/**
|
|
22
26
|
* Merges additional headers into the outgoing request.
|
|
23
27
|
*
|
|
24
28
|
* @example
|
|
25
29
|
* ```ts
|
|
26
|
-
* import {
|
|
30
|
+
* import { compose, defaultFetch, withHeaders } from "@jamx/http";
|
|
27
31
|
*
|
|
28
|
-
* const fetcher =
|
|
32
|
+
* const fetcher = compose(
|
|
29
33
|
* withHeaders({ accept: "application/json" }),
|
|
30
34
|
* )(defaultFetch);
|
|
31
35
|
* ```
|
|
32
36
|
*/
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
export function withHeaders(headers) {
|
|
38
|
+
return defineInterceptor(async ({ request, next }) => {
|
|
39
|
+
const { input, init } = request;
|
|
40
|
+
const next_headers = getHeaders(input, init);
|
|
41
|
+
const extra_headers = typeof headers === "function"
|
|
42
|
+
? headers(new Request(input, init))
|
|
43
|
+
: headers;
|
|
44
|
+
new Headers(extra_headers).forEach((value, key) => {
|
|
45
|
+
next_headers.set(key, value);
|
|
46
|
+
});
|
|
47
|
+
return next({
|
|
48
|
+
input,
|
|
49
|
+
init: { ...init, headers: next_headers },
|
|
50
|
+
});
|
|
38
51
|
});
|
|
39
|
-
|
|
40
|
-
});
|
|
52
|
+
}
|
|
41
53
|
/**
|
|
42
54
|
* Adds an `Authorization` header using the given token and scheme.
|
|
43
55
|
*
|
|
44
56
|
* @example
|
|
45
57
|
* ```ts
|
|
46
|
-
* import {
|
|
58
|
+
* import { compose, defaultFetch, withAuth } from "@jamx/http";
|
|
47
59
|
*
|
|
48
|
-
* const fetcher =
|
|
60
|
+
* const fetcher = compose(withAuth("demo-token"))(defaultFetch);
|
|
49
61
|
* ```
|
|
50
62
|
*/
|
|
51
|
-
export
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
63
|
+
export function withAuth(token, scheme = "Bearer") {
|
|
64
|
+
return defineInterceptor(async ({ request, next }) => {
|
|
65
|
+
const { input, init } = request;
|
|
66
|
+
const resolvedToken = typeof token === "function" ? token(new Request(input, init)) : token;
|
|
67
|
+
const headers = getHeaders(input, init);
|
|
68
|
+
headers.set("authorization", `${scheme} ${resolvedToken}`);
|
|
69
|
+
return next({ input, init: { ...init, headers } });
|
|
70
|
+
});
|
|
71
|
+
}
|
|
57
72
|
/**
|
|
58
73
|
* Aborts requests that take longer than the given timeout.
|
|
59
74
|
*
|
|
60
75
|
* @example
|
|
61
76
|
* ```ts
|
|
62
|
-
* import {
|
|
77
|
+
* import { compose, defaultFetch, withTimeout } from "@jamx/http";
|
|
63
78
|
*
|
|
64
|
-
* const fetcher =
|
|
79
|
+
* const fetcher = compose(withTimeout(500))(defaultFetch);
|
|
65
80
|
* ```
|
|
66
81
|
*/
|
|
82
|
+
export class TimeoutError extends Error {
|
|
83
|
+
constructor(timeoutMs, cause) {
|
|
84
|
+
super(`Fetch request timed out after ${timeoutMs}ms.`, { cause });
|
|
85
|
+
this.name = "TimeoutError";
|
|
86
|
+
this._tag = "FetchError";
|
|
87
|
+
this.timeoutMs = timeoutMs;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
67
90
|
export const withTimeout = (timeoutMs) => defineInterceptor(async ({ request, next }) => {
|
|
91
|
+
const { input, init } = request;
|
|
68
92
|
const controller = new AbortController();
|
|
69
93
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
const signal = mergeSignals(getSignal(input, init), controller.signal);
|
|
95
|
+
const timed_request = {
|
|
96
|
+
input,
|
|
97
|
+
init: { ...init, signal },
|
|
98
|
+
};
|
|
73
99
|
try {
|
|
74
100
|
return await Promise.race([
|
|
75
|
-
next(
|
|
101
|
+
next(timed_request),
|
|
76
102
|
new Promise((resolve) => {
|
|
77
|
-
|
|
78
|
-
resolve({
|
|
79
|
-
ok: false,
|
|
80
|
-
error: new TimeoutError(timeoutMs),
|
|
81
|
-
});
|
|
103
|
+
signal.addEventListener("abort", () => {
|
|
104
|
+
resolve({ ok: false, error: new TimeoutError(timeoutMs) });
|
|
82
105
|
}, { once: true });
|
|
83
106
|
}),
|
|
84
107
|
]);
|
|
@@ -87,10 +110,9 @@ export const withTimeout = (timeoutMs) => defineInterceptor(async ({ request, ne
|
|
|
87
110
|
clearTimeout(timeout);
|
|
88
111
|
}
|
|
89
112
|
});
|
|
90
|
-
|
|
91
|
-
if (!left)
|
|
113
|
+
function mergeSignals(left, right) {
|
|
114
|
+
if (!left)
|
|
92
115
|
return right;
|
|
93
|
-
}
|
|
94
116
|
const anySignal = AbortSignal;
|
|
95
117
|
if (typeof anySignal.any === "function") {
|
|
96
118
|
return anySignal.any([left, right]);
|
|
@@ -104,71 +126,50 @@ const mergeSignals = (left, right) => {
|
|
|
104
126
|
left.addEventListener("abort", abort, { once: true });
|
|
105
127
|
right.addEventListener("abort", abort, { once: true });
|
|
106
128
|
return controller.signal;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
* const fetcher = composeInterceptors(withRetry({ retries: 2 }))(defaultFetch);
|
|
117
|
-
* ```
|
|
118
|
-
*/
|
|
119
|
-
export const withRetry = (options) => defineInterceptor(async ({ request, next }) => {
|
|
120
|
-
for (let attempt = 0; attempt <= options.retries; attempt += 1) {
|
|
121
|
-
const attemptRequest = new Request(request);
|
|
122
|
-
const result = await next(attemptRequest);
|
|
123
|
-
const shouldRetry = await shouldRetryResult(result, attempt, request, options);
|
|
124
|
-
if (!shouldRetry) {
|
|
125
|
-
return result;
|
|
129
|
+
}
|
|
130
|
+
export function withRetry(options) {
|
|
131
|
+
return defineInterceptor(async ({ request, next }) => {
|
|
132
|
+
for (let attempt = 0; attempt <= options.retries; attempt += 1) {
|
|
133
|
+
const attempt_request = cloneInput(request);
|
|
134
|
+
const result = await next(attempt_request);
|
|
135
|
+
const should_retry = await shouldRetryResult(result, attempt, request, options);
|
|
136
|
+
if (!should_retry)
|
|
137
|
+
return result;
|
|
126
138
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
139
|
+
return next(cloneInput(request));
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
async function shouldRetryResult(result, attempt, request, options) {
|
|
131
143
|
if (attempt >= options.retries) {
|
|
132
144
|
return false;
|
|
133
145
|
}
|
|
134
|
-
if (!isRetryableMethod(request.
|
|
146
|
+
if (!isRetryableMethod(getMethod(request.input, request.init), options.methods)) {
|
|
135
147
|
return false;
|
|
136
148
|
}
|
|
137
149
|
if (options.shouldRetry) {
|
|
138
150
|
return options.shouldRetry(result, attempt, request);
|
|
139
151
|
}
|
|
140
152
|
return isErr(result) || (isOk(result) && result.value.status >= 500);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
* Caches successful `GET` responses in a store.
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* ```ts
|
|
147
|
-
* import { composeInterceptors, createMemoryCacheStore, defaultFetch, withCache } from "@jamx/http";
|
|
148
|
-
*
|
|
149
|
-
* const store = createMemoryCacheStore();
|
|
150
|
-
* const fetcher = composeInterceptors(withCache({ store }))(defaultFetch);
|
|
151
|
-
* ```
|
|
152
|
-
*/
|
|
153
|
-
export const withCache = (options = {}) => {
|
|
153
|
+
}
|
|
154
|
+
export function withCache(options = {}) {
|
|
154
155
|
const store = options.store ?? createMemoryCacheStore();
|
|
155
156
|
return defineInterceptor(async ({ request, next }) => {
|
|
157
|
+
const { input, init } = request;
|
|
156
158
|
const key = (options.key ?? defaultCacheKey)(request);
|
|
157
|
-
if (
|
|
159
|
+
if (getMethod(input, init) === "GET") {
|
|
158
160
|
const cached = await store.get(key);
|
|
159
|
-
if (cached)
|
|
161
|
+
if (cached)
|
|
160
162
|
return ok(cached.clone());
|
|
161
|
-
}
|
|
162
163
|
}
|
|
163
|
-
const result = await next(
|
|
164
|
-
if (
|
|
164
|
+
const result = await next(cloneInput(request));
|
|
165
|
+
if (getMethod(input, init) === "GET" &&
|
|
165
166
|
isOk(result) &&
|
|
166
167
|
(options.shouldCache ?? defaultShouldCache)(result, request)) {
|
|
167
168
|
await store.set(key, result.value.clone());
|
|
168
169
|
}
|
|
169
170
|
return result;
|
|
170
171
|
});
|
|
171
|
-
}
|
|
172
|
+
}
|
|
172
173
|
/**
|
|
173
174
|
* Creates an in-memory cache store compatible with `withCache`.
|
|
174
175
|
*
|
|
@@ -179,7 +180,7 @@ export const withCache = (options = {}) => {
|
|
|
179
180
|
* const store = createMemoryCacheStore();
|
|
180
181
|
* ```
|
|
181
182
|
*/
|
|
182
|
-
export
|
|
183
|
+
export function createMemoryCacheStore() {
|
|
183
184
|
const store = new Map();
|
|
184
185
|
return {
|
|
185
186
|
get(key) {
|
|
@@ -190,16 +191,20 @@ export const createMemoryCacheStore = () => {
|
|
|
190
191
|
store.set(key, response.clone());
|
|
191
192
|
},
|
|
192
193
|
};
|
|
194
|
+
}
|
|
195
|
+
const defaultShouldCache = (result) => {
|
|
196
|
+
return isOk(result) && result.value.ok;
|
|
197
|
+
};
|
|
198
|
+
const defaultCacheKey = (request) => {
|
|
199
|
+
return `${getMethod(request.input, request.init)}:${getUrl(request.input).href}:${serializeHeaders(getHeaders(request.input, request.init))}`;
|
|
193
200
|
};
|
|
194
|
-
const defaultShouldCache = (result) => isOk(result) && result.value.ok;
|
|
195
|
-
const defaultCacheKey = (request) => `${request.method}:${request.url}:${serializeHeaders(request.headers)}`;
|
|
196
201
|
const DEFAULT_RETRY_METHODS = new Set([
|
|
197
|
-
"DELETE",
|
|
198
202
|
"GET",
|
|
199
|
-
"HEAD",
|
|
200
|
-
"OPTIONS",
|
|
201
203
|
"PUT",
|
|
204
|
+
"HEAD",
|
|
202
205
|
"TRACE",
|
|
206
|
+
"DELETE",
|
|
207
|
+
"OPTIONS",
|
|
203
208
|
]);
|
|
204
209
|
const isRetryableMethod = (method, methods) => {
|
|
205
210
|
const normalizedMethod = method.toUpperCase();
|
|
@@ -217,3 +222,30 @@ const serializeHeaders = (headers) => {
|
|
|
217
222
|
};
|
|
218
223
|
const trimLeadingSlash = (value) => value.replace(/^\/+/, "");
|
|
219
224
|
const trimTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
225
|
+
const getUrl = (input, base) => {
|
|
226
|
+
if (input instanceof Request) {
|
|
227
|
+
return new URL(input.url);
|
|
228
|
+
}
|
|
229
|
+
return new URL(input, base);
|
|
230
|
+
};
|
|
231
|
+
const getHeaders = (input, init) => {
|
|
232
|
+
const headers = new Headers(input instanceof Request ? input.headers : init?.headers);
|
|
233
|
+
if (input instanceof Request && init?.headers) {
|
|
234
|
+
new Headers(init.headers).forEach((value, key) => {
|
|
235
|
+
headers.set(key, value);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return headers;
|
|
239
|
+
};
|
|
240
|
+
const getSignal = (input, init) => {
|
|
241
|
+
return init?.signal ?? (input instanceof Request ? input.signal : null);
|
|
242
|
+
};
|
|
243
|
+
const getMethod = (input, init) => {
|
|
244
|
+
return (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
245
|
+
};
|
|
246
|
+
const cloneInput = (request) => {
|
|
247
|
+
return {
|
|
248
|
+
input: request.input instanceof Request ? request.input.clone() : request.input,
|
|
249
|
+
init: request.init,
|
|
250
|
+
};
|
|
251
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Either } from "../either.js";
|
|
1
|
+
import { type Either } from "../either.js";
|
|
2
2
|
export declare class StatusError extends Error {
|
|
3
3
|
name: string;
|
|
4
4
|
readonly status: number;
|
|
@@ -18,7 +18,7 @@ export declare class StatusError extends Error {
|
|
|
18
18
|
* const result = expectStatus(ok(new Response(null, { status: 200 })), 200);
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
|
-
export declare
|
|
21
|
+
export declare function expectStatus<TError>(result: Either<TError, Response>, expected: number | readonly number[]): Either<TError | StatusError, Response>;
|
|
22
22
|
/**
|
|
23
23
|
* Asserts that a response status satisfies a custom predicate.
|
|
24
24
|
*
|
|
@@ -33,14 +33,14 @@ export declare const expectStatus: <TError>(result: Either<TError, Response>, ex
|
|
|
33
33
|
* );
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
|
-
export declare
|
|
37
|
-
export declare
|
|
38
|
-
export declare
|
|
39
|
-
export declare
|
|
40
|
-
export declare
|
|
41
|
-
export declare
|
|
42
|
-
export declare
|
|
43
|
-
export declare
|
|
36
|
+
export declare function expectStatusRange<TError>(result: Either<TError, Response>, predicate: (status: number) => boolean, description?: string): Either<TError | StatusError, Response>;
|
|
37
|
+
export declare function isInformationalStatus(status: number): boolean;
|
|
38
|
+
export declare function isOkStatus(status: number): boolean;
|
|
39
|
+
export declare function isRedirectStatus(status: number): boolean;
|
|
40
|
+
export declare function isClientErrorStatus(status: number): boolean;
|
|
41
|
+
export declare function isServerErrorStatus(status: number): boolean;
|
|
42
|
+
export declare function isErrorStatus(status: number): boolean;
|
|
43
|
+
export declare function expectInformationalStatus<TError>(result: Either<TError, Response>): Either<TError | StatusError, Response>;
|
|
44
44
|
/**
|
|
45
45
|
* Asserts that a response has a successful `2xx` status.
|
|
46
46
|
*
|
|
@@ -51,9 +51,9 @@ export declare const expectInformationalStatus: <TError>(result: Either<TError,
|
|
|
51
51
|
* const result = expectOKStatus(ok(new Response(null, { status: 204 })));
|
|
52
52
|
* ```
|
|
53
53
|
*/
|
|
54
|
-
export declare
|
|
55
|
-
export declare
|
|
56
|
-
export declare
|
|
57
|
-
export declare
|
|
58
|
-
export declare
|
|
54
|
+
export declare function expectOKStatus<TError>(result: Either<TError, Response>): Either<TError | StatusError, Response>;
|
|
55
|
+
export declare function expectRedirectStatus<TError>(result: Either<TError, Response>): Either<TError | StatusError, Response>;
|
|
56
|
+
export declare function expectClientErrorStatus<TError>(result: Either<TError, Response>): Either<TError | StatusError, Response>;
|
|
57
|
+
export declare function expectServerErrorStatus<TError>(result: Either<TError, Response>): Either<TError | StatusError, Response>;
|
|
58
|
+
export declare function expectErrorStatus<TError>(result: Either<TError, Response>): Either<TError | StatusError, Response>;
|
|
59
59
|
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../src/internal/statuses/validators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../src/internal/statuses/validators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAc,MAAM,cAAc,CAAC;AAEvD,qBAAa,WAAY,SAAQ,KAAK;IACpC,IAAI,SAAiB;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC;gBAGrD,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,MAAM,EAC7C,OAAO,SAA+C;CAQzD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,MAAM,EACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GACnC,MAAM,CAAC,MAAM,GAAG,WAAW,EAAE,QAAQ,CAAC,CAgBxC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EAChC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,EACtC,WAAW,SAAsB,GAChC,MAAM,CAAC,MAAM,GAAG,WAAW,EAAE,QAAQ,CAAC,CAYxC;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,WAEnD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,WAExC;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,WAE9C;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,WAEjD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,WAEjD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,WAE3C;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAC9C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAC/B,MAAM,CAAC,MAAM,GAAG,WAAW,EAAE,QAAQ,CAAC,CAMxC;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,MAAM,EACnC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAC/B,MAAM,CAAC,MAAM,GAAG,WAAW,EAAE,QAAQ,CAAC,CAExC;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAC/B,MAAM,CAAC,MAAM,GAAG,WAAW,EAAE,QAAQ,CAAC,CAExC;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAC/B,MAAM,CAAC,MAAM,GAAG,WAAW,EAAE,QAAQ,CAAC,CAMxC;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAC/B,MAAM,CAAC,MAAM,GAAG,WAAW,EAAE,QAAQ,CAAC,CAMxC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAC/B,MAAM,CAAC,MAAM,GAAG,WAAW,EAAE,QAAQ,CAAC,CAMxC"}
|
|
@@ -20,7 +20,7 @@ export class StatusError extends Error {
|
|
|
20
20
|
* const result = expectStatus(ok(new Response(null, { status: 200 })), 200);
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
export
|
|
23
|
+
export function expectStatus(result, expected) {
|
|
24
24
|
if (isErr(result))
|
|
25
25
|
return result;
|
|
26
26
|
const matches = Array.isArray(expected)
|
|
@@ -29,7 +29,7 @@ export const expectStatus = (result, expected) => {
|
|
|
29
29
|
return matches
|
|
30
30
|
? result
|
|
31
31
|
: err(new StatusError(result.value, expected, `Expected status ${formatExpectedStatus(expected)} but received ${result.value.status}.`));
|
|
32
|
-
}
|
|
32
|
+
}
|
|
33
33
|
/**
|
|
34
34
|
* Asserts that a response status satisfies a custom predicate.
|
|
35
35
|
*
|
|
@@ -44,20 +44,34 @@ export const expectStatus = (result, expected) => {
|
|
|
44
44
|
* );
|
|
45
45
|
* ```
|
|
46
46
|
*/
|
|
47
|
-
export
|
|
47
|
+
export function expectStatusRange(result, predicate, description = "a matching status") {
|
|
48
48
|
if (isErr(result))
|
|
49
49
|
return result;
|
|
50
50
|
return predicate(result.value.status)
|
|
51
51
|
? result
|
|
52
52
|
: err(new StatusError(result.value, description, `Expected ${description} but received ${result.value.status}.`));
|
|
53
|
-
}
|
|
54
|
-
export
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
export
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
export
|
|
53
|
+
}
|
|
54
|
+
export function isInformationalStatus(status) {
|
|
55
|
+
return status >= 100 && status < 200;
|
|
56
|
+
}
|
|
57
|
+
export function isOkStatus(status) {
|
|
58
|
+
return status >= 200 && status < 300;
|
|
59
|
+
}
|
|
60
|
+
export function isRedirectStatus(status) {
|
|
61
|
+
return status >= 300 && status < 400;
|
|
62
|
+
}
|
|
63
|
+
export function isClientErrorStatus(status) {
|
|
64
|
+
return status >= 400 && status < 500;
|
|
65
|
+
}
|
|
66
|
+
export function isServerErrorStatus(status) {
|
|
67
|
+
return status >= 500 && status < 600;
|
|
68
|
+
}
|
|
69
|
+
export function isErrorStatus(status) {
|
|
70
|
+
return isClientErrorStatus(status) || isServerErrorStatus(status);
|
|
71
|
+
}
|
|
72
|
+
export function expectInformationalStatus(result) {
|
|
73
|
+
return expectStatusRange(result, isInformationalStatus, "an informational status (1xx)");
|
|
74
|
+
}
|
|
61
75
|
/**
|
|
62
76
|
* Asserts that a response has a successful `2xx` status.
|
|
63
77
|
*
|
|
@@ -68,11 +82,21 @@ export const expectInformationalStatus = (result) => expectStatusRange(result, i
|
|
|
68
82
|
* const result = expectOKStatus(ok(new Response(null, { status: 204 })));
|
|
69
83
|
* ```
|
|
70
84
|
*/
|
|
71
|
-
export
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
export
|
|
75
|
-
|
|
85
|
+
export function expectOKStatus(result) {
|
|
86
|
+
return expectStatusRange(result, isOkStatus, "a successful status (2xx)");
|
|
87
|
+
}
|
|
88
|
+
export function expectRedirectStatus(result) {
|
|
89
|
+
return expectStatusRange(result, isRedirectStatus, "a redirect status (3xx)");
|
|
90
|
+
}
|
|
91
|
+
export function expectClientErrorStatus(result) {
|
|
92
|
+
return expectStatusRange(result, isClientErrorStatus, "a client error status (4xx)");
|
|
93
|
+
}
|
|
94
|
+
export function expectServerErrorStatus(result) {
|
|
95
|
+
return expectStatusRange(result, isServerErrorStatus, "a server error status (5xx)");
|
|
96
|
+
}
|
|
97
|
+
export function expectErrorStatus(result) {
|
|
98
|
+
return expectStatusRange(result, isErrorStatus, "an error status (4xx or 5xx)");
|
|
99
|
+
}
|
|
76
100
|
function formatExpectedStatus(expected) {
|
|
77
101
|
return Array.isArray(expected) ? expected.join(", ") : String(expected);
|
|
78
102
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/internal/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/internal/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC"}
|