fetchbasequery 0.2.1 → 1.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/LICENSE +1 -1
- package/README.md +262 -36
- package/jsconfig.json +12 -0
- package/package.json +26 -6
- package/src/decorators/index.d.ts +10 -0
- package/src/decorators/index.js +1 -0
- package/src/decorators/json.js +107 -0
- package/src/fetchBaseQuery.js +101 -14
- package/src/index.d.ts +19 -0
- package/src/index.js +4 -0
- package/src/middlewares/index.d.ts +14 -0
- package/src/middlewares/index.js +3 -4
- package/src/middlewares/muteErrors.js +17 -0
- package/src/middlewares/retryOn429.js +37 -0
- package/src/middlewares/retryOnError.js +32 -0
- package/src/utils/sleep.js +17 -0
- package/tests/index.html +7 -0
- package/tests/index.js +546 -0
- package/src/baseQuery.js +0 -80
- package/src/helpers/abortable.js +0 -13
- package/src/helpers/encodeJson.js +0 -8
- package/src/helpers/mergeHeaders.js +0 -9
- package/src/helpers/pipe.js +0 -1
- package/src/helpers/purge.js +0 -16
- package/src/helpers/responseFromError.js +0 -5
- package/src/middlewares/accessToken.js +0 -62
- package/src/middlewares/commonReply.js +0 -23
- package/src/middlewares/encodeBody.js +0 -85
- package/src/middlewares/jsonReply.js +0 -35
- package/types/index.d.ts +0 -64
package/src/baseQuery.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import responseFromError from './helpers/responseFromError.js'
|
|
2
|
-
import purge from './helpers/purge.js'
|
|
3
|
-
import mergeHeaders from './helpers/mergeHeaders.js'
|
|
4
|
-
|
|
5
|
-
const moduleName = 'fetchBaseQuery/baseQuery'
|
|
6
|
-
|
|
7
|
-
const baseQuery = async ({ url: endpoint, request: subject, ...options } = {}) => {
|
|
8
|
-
const {
|
|
9
|
-
baseUrl,
|
|
10
|
-
logErrors,
|
|
11
|
-
debug,
|
|
12
|
-
...restOptions
|
|
13
|
-
} = {
|
|
14
|
-
logErrors: false,
|
|
15
|
-
baseUrl: globalThis.location
|
|
16
|
-
? globalThis.location.origin
|
|
17
|
-
: undefined,
|
|
18
|
-
...options
|
|
19
|
-
}
|
|
20
|
-
// extract options which are known by `fetch()`
|
|
21
|
-
const { method, headers, body, mode, credentials, cache, redirect,
|
|
22
|
-
referrer, referrerPolicy, integrity, keepalive, signal } = restOptions
|
|
23
|
-
// combine extracted options and remove undefined properties
|
|
24
|
-
const fetchOptions = purge({ method, headers, body, mode, credentials, cache, redirect,
|
|
25
|
-
referrer, referrerPolicy, integrity, keepalive, signal })
|
|
26
|
-
// do request
|
|
27
|
-
let request
|
|
28
|
-
let response
|
|
29
|
-
try {
|
|
30
|
-
if (endpoint !== undefined) {
|
|
31
|
-
let url, origin, prefix = '/'
|
|
32
|
-
if (baseUrl) {
|
|
33
|
-
const url = new URL(baseUrl)
|
|
34
|
-
origin = url.origin
|
|
35
|
-
prefix = url.pathname
|
|
36
|
-
}
|
|
37
|
-
try {
|
|
38
|
-
url = new URL(endpoint)
|
|
39
|
-
} catch (error) {
|
|
40
|
-
url = new URL(
|
|
41
|
-
/\/$/.test(prefix) && /^\//.test(endpoint)
|
|
42
|
-
? prefix + endpoint.substring(1)
|
|
43
|
-
: prefix + endpoint,
|
|
44
|
-
origin
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
subject = url
|
|
48
|
-
} else if (fetchOptions.headers) {
|
|
49
|
-
fetchOptions.headers = mergeHeaders(subject.headers, fetchOptions.headers)
|
|
50
|
-
}
|
|
51
|
-
request = new Request(subject, fetchOptions)
|
|
52
|
-
if (debug?.baseQuery) {
|
|
53
|
-
console.log(moduleName, { request })
|
|
54
|
-
}
|
|
55
|
-
response = await fetch(request)
|
|
56
|
-
} catch (error) {
|
|
57
|
-
if (logErrors) {
|
|
58
|
-
console.error(moduleName, error)
|
|
59
|
-
}
|
|
60
|
-
response = responseFromError(error)
|
|
61
|
-
}
|
|
62
|
-
if (debug?.baseQuery) {
|
|
63
|
-
console.log(moduleName, { response })
|
|
64
|
-
}
|
|
65
|
-
return response
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export default ({ timeout, signal, ...options } = {}) => {
|
|
69
|
-
// construct abort signal
|
|
70
|
-
const abortController = new AbortController()
|
|
71
|
-
const abortSignal = AbortSignal.any([
|
|
72
|
-
abortController.signal,
|
|
73
|
-
...(timeout ? [ AbortSignal.timeout(timeout) ] : []),
|
|
74
|
-
...(signal ? [ signal ] : [])
|
|
75
|
-
])
|
|
76
|
-
// construct abortable promise
|
|
77
|
-
const promise = baseQuery({ ...options, signal: abortSignal })
|
|
78
|
-
promise.abort = () => abortController.abort()
|
|
79
|
-
return promise
|
|
80
|
-
}
|
package/src/helpers/abortable.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export default middleware => query => ({ signal, ...options } = {}) => {
|
|
2
|
-
const abortController = new AbortController()
|
|
3
|
-
const abortableQuery = (options) => query({
|
|
4
|
-
...options,
|
|
5
|
-
signal: AbortSignal.any([
|
|
6
|
-
abortController.signal,
|
|
7
|
-
...(signal ? [ signal ] : [])
|
|
8
|
-
])
|
|
9
|
-
})
|
|
10
|
-
const promise = middleware(abortableQuery)(options)
|
|
11
|
-
promise.abort = () => abortController.abort()
|
|
12
|
-
return promise
|
|
13
|
-
}
|
package/src/helpers/pipe.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default (...fns) => (input) => fns.reduce((chain, f) => f(chain), input)
|
package/src/helpers/purge.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
const purgeObject = object => Object.fromEntries(
|
|
2
|
-
Object.entries(object)
|
|
3
|
-
.filter(entry => entry[1] !== undefined)
|
|
4
|
-
)
|
|
5
|
-
|
|
6
|
-
const purgeHeaders = headers => {
|
|
7
|
-
headers.entities().forEach(([ name, value ]) => {
|
|
8
|
-
if (value === '')
|
|
9
|
-
headers.delete(name)
|
|
10
|
-
})
|
|
11
|
-
return headers
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default object => object instanceof Headers
|
|
15
|
-
? purgeHeaders(object)
|
|
16
|
-
: purgeObject(object)
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import responseFromError from '../helpers/responseFromError.js'
|
|
2
|
-
import purge from '../helpers/purge.js'
|
|
3
|
-
import mergeHeaders from '../helpers/mergeHeaders.js'
|
|
4
|
-
|
|
5
|
-
const moduleName = 'fetchBaseQuery/middlewares/accessToken'
|
|
6
|
-
|
|
7
|
-
export default (settings = {}) => query => async ({ payload, headers, ...options } = {}) => {
|
|
8
|
-
const {
|
|
9
|
-
getAccessToken,
|
|
10
|
-
setAccessToken,
|
|
11
|
-
removeAccessToken,
|
|
12
|
-
isUnauthorized,
|
|
13
|
-
getUpdatedAccessToken,
|
|
14
|
-
getAuthorizationHeaders
|
|
15
|
-
} = {
|
|
16
|
-
getAccessToken: (payload) => undefined,
|
|
17
|
-
setAccessToken: (token) => {},
|
|
18
|
-
removeAccessToken: () => {},
|
|
19
|
-
isUnauthorized: (response) => response.status === 401,
|
|
20
|
-
getUpdatedAccessToken: async (response) => undefined,
|
|
21
|
-
getAuthorizationHeaders: (accessToken) => (
|
|
22
|
-
accessToken
|
|
23
|
-
? { Authorization: `bearer ${accessToken}` }
|
|
24
|
-
: {}
|
|
25
|
-
),
|
|
26
|
-
...settings
|
|
27
|
-
}
|
|
28
|
-
let response
|
|
29
|
-
try {
|
|
30
|
-
response = await query({
|
|
31
|
-
...options,
|
|
32
|
-
headers: mergeHeaders(
|
|
33
|
-
purge(getAuthorizationHeaders(getAccessToken(payload))),
|
|
34
|
-
headers || {}
|
|
35
|
-
)
|
|
36
|
-
})
|
|
37
|
-
} catch (error) {
|
|
38
|
-
if (options.logErrors) {
|
|
39
|
-
console.error(moduleName, error)
|
|
40
|
-
}
|
|
41
|
-
response = responseFromError(error)
|
|
42
|
-
}
|
|
43
|
-
try {
|
|
44
|
-
if (isUnauthorized(response)) {
|
|
45
|
-
removeAccessToken()
|
|
46
|
-
} else {
|
|
47
|
-
const updatedAccessToken = await getUpdatedAccessToken(response)
|
|
48
|
-
if (updatedAccessToken) {
|
|
49
|
-
setAccessToken(updatedAccessToken)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
} catch (error) {
|
|
53
|
-
if (options.logErrors) {
|
|
54
|
-
console.error(moduleName, error)
|
|
55
|
-
}
|
|
56
|
-
response = responseFromError(error)
|
|
57
|
-
}
|
|
58
|
-
if (options.debug?.accessToken) {
|
|
59
|
-
console.log(moduleName, { response })
|
|
60
|
-
}
|
|
61
|
-
return response
|
|
62
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const moduleName = 'fetchBaseQuery/middlewares/commonReply'
|
|
2
|
-
|
|
3
|
-
export default () => query => async (options) => {
|
|
4
|
-
const { status, data, error, headers } = await query(options)
|
|
5
|
-
let reply
|
|
6
|
-
if (error) {
|
|
7
|
-
reply = { status, error: true, message: error.message || error, headers }
|
|
8
|
-
} else if (data) {
|
|
9
|
-
if (data.success) {
|
|
10
|
-
reply = { ...data, status, headers }
|
|
11
|
-
} else if (data.error) {
|
|
12
|
-
reply = { status, error: true, message: data.message, headers }
|
|
13
|
-
} else {
|
|
14
|
-
reply = { status, error: true, message: 'Unexpected format of common response body.', headers }
|
|
15
|
-
}
|
|
16
|
-
} else {
|
|
17
|
-
reply = { status, headers }
|
|
18
|
-
}
|
|
19
|
-
if (options.debug?.commonReply) {
|
|
20
|
-
console.log(moduleName, { reply })
|
|
21
|
-
}
|
|
22
|
-
return reply
|
|
23
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import purge from '../helpers/purge.js'
|
|
2
|
-
import encodeJson from '../helpers/encodeJson.js'
|
|
3
|
-
import responseFromError from '../helpers/responseFromError.js'
|
|
4
|
-
import mergeHeaders from '../helpers/mergeHeaders.js'
|
|
5
|
-
|
|
6
|
-
const moduleName = 'fetchBaseQuery/middlewares/accessToken'
|
|
7
|
-
|
|
8
|
-
const getEncodedBody = body => (
|
|
9
|
-
typeof body === 'string'
|
|
10
|
-
|| body instanceof String
|
|
11
|
-
|| body instanceof FormData
|
|
12
|
-
|| body instanceof Blob
|
|
13
|
-
|| body instanceof ArrayBuffer
|
|
14
|
-
|| body instanceof DataView
|
|
15
|
-
|| body instanceof File
|
|
16
|
-
|| body instanceof ReadableStream
|
|
17
|
-
|| body instanceof Int8Array
|
|
18
|
-
|| body instanceof Uint8Array
|
|
19
|
-
|| body instanceof Int16Array
|
|
20
|
-
|| body instanceof Uint16Array
|
|
21
|
-
|| body instanceof Int32Array
|
|
22
|
-
|| body instanceof Uint32Array
|
|
23
|
-
|| body instanceof BigInt64Array
|
|
24
|
-
|| body instanceof BigUint64Array
|
|
25
|
-
? body // raw
|
|
26
|
-
: (
|
|
27
|
-
body instanceof URLSearchParams
|
|
28
|
-
? body.toString()
|
|
29
|
-
: encodeJson(body) // default
|
|
30
|
-
)
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
const detectContentType = ({ body }) => {
|
|
34
|
-
if (body instanceof ArrayBuffer
|
|
35
|
-
|| body instanceof DataView
|
|
36
|
-
|| body instanceof ReadableStream
|
|
37
|
-
|| body instanceof Int8Array
|
|
38
|
-
|| body instanceof Uint8Array
|
|
39
|
-
|| body instanceof Int16Array
|
|
40
|
-
|| body instanceof Uint16Array
|
|
41
|
-
|| body instanceof Int32Array
|
|
42
|
-
|| body instanceof Uint32Array
|
|
43
|
-
|| body instanceof BigInt64Array
|
|
44
|
-
|| body instanceof BigUint64Array
|
|
45
|
-
) return 'application/octet-stream'
|
|
46
|
-
if (body instanceof URLSearchParams) return 'application/x-www-form-urlencoded'
|
|
47
|
-
if (body instanceof FormData) return 'multipart/form-data'
|
|
48
|
-
if (body instanceof Object) return 'application/json; charset=utf-8'
|
|
49
|
-
// return undefined
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export default (settings = {}) => (query) => async ({ body, headers, ...options } = {}) => {
|
|
53
|
-
const {
|
|
54
|
-
contentType,
|
|
55
|
-
encodeBody
|
|
56
|
-
} = {
|
|
57
|
-
contentType: detectContentType,
|
|
58
|
-
encodeBody: getEncodedBody,
|
|
59
|
-
...settings
|
|
60
|
-
}
|
|
61
|
-
let response
|
|
62
|
-
try {
|
|
63
|
-
response = await query({
|
|
64
|
-
...options,
|
|
65
|
-
headers: mergeHeaders(
|
|
66
|
-
purge({
|
|
67
|
-
'Content-Type': contentType instanceof Function
|
|
68
|
-
? contentType({ body })
|
|
69
|
-
: contentType
|
|
70
|
-
}),
|
|
71
|
-
headers || {}
|
|
72
|
-
),
|
|
73
|
-
body: encodeBody(body)
|
|
74
|
-
})
|
|
75
|
-
} catch (error) {
|
|
76
|
-
if (options.logErrors) {
|
|
77
|
-
console.error(moduleName, error)
|
|
78
|
-
}
|
|
79
|
-
response = responseFromError(error)
|
|
80
|
-
}
|
|
81
|
-
if (options.debug?.encodeBody) {
|
|
82
|
-
console.log(moduleName, { response })
|
|
83
|
-
}
|
|
84
|
-
return response
|
|
85
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
const moduleName = 'fetchBaseQuery/middlewares/jsonReply'
|
|
2
|
-
|
|
3
|
-
export default (settings = {}) => query => async (options = {}) => {
|
|
4
|
-
const {
|
|
5
|
-
isJsonBody
|
|
6
|
-
} = {
|
|
7
|
-
isJsonBody: (response) => /^application\/json/i.test(response.headers.get('Content-Type')),
|
|
8
|
-
...settings
|
|
9
|
-
}
|
|
10
|
-
const response = await query(options)
|
|
11
|
-
const { status, error, headers } = response
|
|
12
|
-
let reply
|
|
13
|
-
if (error) {
|
|
14
|
-
reply = { status, error, headers }
|
|
15
|
-
} else {
|
|
16
|
-
let data
|
|
17
|
-
try {
|
|
18
|
-
if (isJsonBody(response)) {
|
|
19
|
-
data = await response.json()
|
|
20
|
-
reply = { status, data, headers }
|
|
21
|
-
} else {
|
|
22
|
-
reply = { status, headers }
|
|
23
|
-
}
|
|
24
|
-
} catch (error) {
|
|
25
|
-
if (options.logErrors) {
|
|
26
|
-
console.error(moduleName, error)
|
|
27
|
-
}
|
|
28
|
-
reply = { status, error, headers }
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
if (options.debug?.jsonReply) {
|
|
32
|
-
console.log(moduleName, { reply })
|
|
33
|
-
}
|
|
34
|
-
return reply
|
|
35
|
-
}
|
package/types/index.d.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
export type DebuggingFlags = {
|
|
2
|
-
baseQuery?: boolean,
|
|
3
|
-
encodeBody?: boolean,
|
|
4
|
-
accessToken?: boolean,
|
|
5
|
-
jsonReply?: boolean,
|
|
6
|
-
commonReply?: boolean
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export type FetchOptions = {
|
|
10
|
-
logErrors?: boolean,
|
|
11
|
-
url?: URL | string,
|
|
12
|
-
request?: Request,
|
|
13
|
-
timeout?: number,
|
|
14
|
-
method?: string,
|
|
15
|
-
headers?: Headers | object,
|
|
16
|
-
body?: string
|
|
17
|
-
| object
|
|
18
|
-
| FormData
|
|
19
|
-
| Blob
|
|
20
|
-
| ArrayBuffer
|
|
21
|
-
| DataView
|
|
22
|
-
| File
|
|
23
|
-
| ReadableStream
|
|
24
|
-
| Int8Array
|
|
25
|
-
| Uint8Array
|
|
26
|
-
| Int16Array
|
|
27
|
-
| Uint16Array
|
|
28
|
-
| Int32Array
|
|
29
|
-
| Uint32Array
|
|
30
|
-
| BigInt64Array
|
|
31
|
-
| BigUint64Array
|
|
32
|
-
| URLSearchParams,
|
|
33
|
-
mode?: string,
|
|
34
|
-
credentials?: string,
|
|
35
|
-
cache?: string,
|
|
36
|
-
redirect?: string,
|
|
37
|
-
referrer?: string,
|
|
38
|
-
referrerPolicy?: string,
|
|
39
|
-
integrity?: string,
|
|
40
|
-
keepalive?: boolean,
|
|
41
|
-
signal?: AbortSignal,
|
|
42
|
-
debug?: DebuggingFlags
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export type baseQueryFn = (options?: FetchOptions) => Promise<Response>;
|
|
46
|
-
export type queryFn = (options?: FetchOptions) => Promise<any>;
|
|
47
|
-
export type middlewareFn = (settings: any) => (query: queryFn) => queryFn;
|
|
48
|
-
|
|
49
|
-
export type FetchBaseQueryOptions = {
|
|
50
|
-
baseUrl?: URL | string,
|
|
51
|
-
baseQuery?: baseQueryFn,
|
|
52
|
-
middlewares?: Array<middlewareFn>
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export const baseQuery: baseQueryFn;
|
|
56
|
-
|
|
57
|
-
export const encodeBody: middlewareFn;
|
|
58
|
-
export const accessToken: middlewareFn;
|
|
59
|
-
export const jsonReply: middlewareFn;
|
|
60
|
-
export const commonReply: middlewareFn;
|
|
61
|
-
|
|
62
|
-
declare function fetchBaseQuery(options?: FetchBaseQueryOptions & FetchOptions): queryFn;
|
|
63
|
-
|
|
64
|
-
export default fetchBaseQuery;
|