@brianbuie/node-kit 0.2.0 → 0.2.2
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/Fetcher.d.ts +3 -1
- package/.dist/Fetcher.js +18 -5
- package/package.json +1 -1
- package/src/Fetcher.test.ts +6 -0
- package/src/Fetcher.ts +18 -7
package/.dist/Fetcher.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type Route = string | URL;
|
|
2
|
-
|
|
2
|
+
type QueryVal = string | number | boolean | null | undefined;
|
|
3
|
+
export type Query = Record<string, QueryVal | QueryVal[]>;
|
|
3
4
|
export type FetchOptions = RequestInit & {
|
|
4
5
|
base?: string;
|
|
5
6
|
query?: Query;
|
|
@@ -48,3 +49,4 @@ export declare class Fetcher {
|
|
|
48
49
|
fetchText(route: Route, opts?: FetchOptions): Promise<[string, Response, Request]>;
|
|
49
50
|
fetchJson<T>(route: Route, opts?: FetchOptions): Promise<[T, Response, Request]>;
|
|
50
51
|
}
|
|
52
|
+
export {};
|
package/.dist/Fetcher.js
CHANGED
|
@@ -21,9 +21,19 @@ export class Fetcher {
|
|
|
21
21
|
*/
|
|
22
22
|
buildUrl(route, opts = {}) {
|
|
23
23
|
const mergedOptions = merge({}, this.defaultOptions, opts);
|
|
24
|
-
const params =
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const params = [];
|
|
25
|
+
Object.entries(mergedOptions.query || {}).forEach(([key, val]) => {
|
|
26
|
+
if (val === undefined)
|
|
27
|
+
return;
|
|
28
|
+
if (Array.isArray(val)) {
|
|
29
|
+
val.forEach((v) => {
|
|
30
|
+
params.push([key, `${v}`]);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
params.push([key, `${val}`]);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
27
37
|
const search = params.length > 0 ? '?' + new URLSearchParams(params).toString() : '';
|
|
28
38
|
const url = new URL(route + search, this.defaultOptions.base);
|
|
29
39
|
const domain = extractDomain(url.href);
|
|
@@ -63,7 +73,7 @@ export class Fetcher {
|
|
|
63
73
|
// Rebuild request on every attempt to reset AbortSignal.timeout
|
|
64
74
|
const [req] = this.buildRequest(route, opts);
|
|
65
75
|
const res = await fetch(req)
|
|
66
|
-
.then(r => {
|
|
76
|
+
.then((r) => {
|
|
67
77
|
if (!r.ok)
|
|
68
78
|
throw new Error(r.statusText);
|
|
69
79
|
return r;
|
|
@@ -72,7 +82,10 @@ export class Fetcher {
|
|
|
72
82
|
if (attempt < maxAttempts) {
|
|
73
83
|
const wait = attempt * 3000;
|
|
74
84
|
console.warn(`${req.method} ${req.url} (attempt ${attempt} of ${maxAttempts})`, error);
|
|
75
|
-
await new Promise(resolve => setTimeout(resolve, wait));
|
|
85
|
+
await new Promise((resolve) => setTimeout(resolve, wait));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
throw new Error(error);
|
|
76
89
|
}
|
|
77
90
|
});
|
|
78
91
|
if (res)
|
package/package.json
CHANGED
package/src/Fetcher.test.ts
CHANGED
|
@@ -50,6 +50,12 @@ describe('Fetcher', () => {
|
|
|
50
50
|
assert(url.href === statusApi.defaultOptions.base + route);
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
+
it('should handle array query param', () => {
|
|
54
|
+
const [url] = statusApi.buildUrl('/', { query: { multiple: [1, 2] } });
|
|
55
|
+
assert(url.href.includes('multiple=1'));
|
|
56
|
+
assert(url.href.includes('multiple=2'));
|
|
57
|
+
});
|
|
58
|
+
|
|
53
59
|
it('should throw on bad request', async () => {
|
|
54
60
|
try {
|
|
55
61
|
await statusApi.fetch('/404', { retries: 0 });
|
package/src/Fetcher.ts
CHANGED
|
@@ -3,7 +3,8 @@ import extractDomain from 'extract-domain';
|
|
|
3
3
|
|
|
4
4
|
export type Route = string | URL;
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type QueryVal = string | number | boolean | null | undefined;
|
|
7
|
+
export type Query = Record<string, QueryVal | QueryVal[]>;
|
|
7
8
|
|
|
8
9
|
export type FetchOptions = RequestInit & {
|
|
9
10
|
base?: string;
|
|
@@ -38,9 +39,17 @@ export class Fetcher {
|
|
|
38
39
|
*/
|
|
39
40
|
buildUrl(route: Route, opts: FetchOptions = {}): [URL, string] {
|
|
40
41
|
const mergedOptions = merge({}, this.defaultOptions, opts);
|
|
41
|
-
const params
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const params: [string, string][] = [];
|
|
43
|
+
Object.entries(mergedOptions.query || {}).forEach(([key, val]) => {
|
|
44
|
+
if (val === undefined) return;
|
|
45
|
+
if (Array.isArray(val)) {
|
|
46
|
+
val.forEach((v) => {
|
|
47
|
+
params.push([key, `${v}`]);
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
params.push([key, `${val}`]);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
44
53
|
const search = params.length > 0 ? '?' + new URLSearchParams(params).toString() : '';
|
|
45
54
|
const url = new URL(route + search, this.defaultOptions.base);
|
|
46
55
|
const domain = extractDomain(url.href) as string;
|
|
@@ -82,15 +91,17 @@ export class Fetcher {
|
|
|
82
91
|
// Rebuild request on every attempt to reset AbortSignal.timeout
|
|
83
92
|
const [req] = this.buildRequest(route, opts);
|
|
84
93
|
const res = await fetch(req)
|
|
85
|
-
.then(r => {
|
|
94
|
+
.then((r) => {
|
|
86
95
|
if (!r.ok) throw new Error(r.statusText);
|
|
87
96
|
return r;
|
|
88
97
|
})
|
|
89
|
-
.catch(async error => {
|
|
98
|
+
.catch(async (error) => {
|
|
90
99
|
if (attempt < maxAttempts) {
|
|
91
100
|
const wait = attempt * 3000;
|
|
92
101
|
console.warn(`${req.method} ${req.url} (attempt ${attempt} of ${maxAttempts})`, error);
|
|
93
|
-
await new Promise(resolve => setTimeout(resolve, wait));
|
|
102
|
+
await new Promise((resolve) => setTimeout(resolve, wait));
|
|
103
|
+
} else {
|
|
104
|
+
throw new Error(error);
|
|
94
105
|
}
|
|
95
106
|
});
|
|
96
107
|
if (res) return [res, req];
|