keq 2.6.8 → 2.6.9
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/CHANGELOG.md +7 -0
- package/__tests__/node/abort.spec.ts +110 -0
- package/__tests__/node/request.ts +51 -0
- package/__tests__/node/resolve-with-mode.spec.ts +48 -0
- package/__tests__/node/retry.spec.ts +35 -0
- package/__tests__/node/send-request-with-body.spec.ts +107 -0
- package/__tests__/node/send-simple-request.spec.ts +121 -0
- package/__tests__/node/timeout.spec.ts +48 -0
- package/__tests__/setup.ts +32 -0
- package/dist/esm/src/is/is-buffer.d.ts +2 -0
- package/dist/esm/src/is/is-buffer.js +4 -0
- package/dist/esm/src/keq.js +2 -1
- package/dist/esm/src/middlewares/fetch-arguments-middleware.js +2 -2
- package/dist/esm/src/util/clone-body.js +2 -1
- package/dist/umd/src/is/is-buffer.d.ts +2 -0
- package/dist/umd/src/is/is-buffer.js +18 -0
- package/dist/umd/src/keq.js +3 -2
- package/dist/umd/src/middlewares/fetch-arguments-middleware.js +3 -3
- package/dist/umd/src/util/clone-body.js +3 -2
- package/package.json +8 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [2.6.9](https://github.com/keq-request/keq/compare/v2.6.8...v2.6.9) (2024-06-24)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* cannot find buffer in the browser ([49b57d8](https://github.com/keq-request/keq/commit/49b57d8a13c576ab1b8e56a1ddc73ffb7dea1286))
|
|
11
|
+
|
|
5
12
|
## [2.6.8](https://github.com/keq-request/keq/compare/v2.6.7...v2.6.8) (2024-06-04)
|
|
6
13
|
|
|
7
14
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { expect, jest, test } from '@jest/globals'
|
|
2
|
+
import { request } from './request.js'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
test('abort flowController request', async () => {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
7
|
+
const mockedFetch = jest.fn((input: RequestInfo | URL, init?: RequestInit) => new Promise((resolve, reject) => {
|
|
8
|
+
let finished = false
|
|
9
|
+
|
|
10
|
+
if (init?.signal) {
|
|
11
|
+
const signal = init.signal
|
|
12
|
+
signal.onabort = () => {
|
|
13
|
+
if (finished) return
|
|
14
|
+
finished = true
|
|
15
|
+
if (signal.reason) reject(signal.reason)
|
|
16
|
+
reject(new DOMException('AbortError', 'AbortError'))
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// sleet 500ms
|
|
21
|
+
setTimeout(
|
|
22
|
+
() => {
|
|
23
|
+
if (finished) return
|
|
24
|
+
finished = true
|
|
25
|
+
|
|
26
|
+
resolve(new Response(
|
|
27
|
+
JSON.stringify({ code: '200' }),
|
|
28
|
+
{
|
|
29
|
+
headers: {
|
|
30
|
+
'content-type': 'application/json',
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
))
|
|
34
|
+
},
|
|
35
|
+
100,
|
|
36
|
+
)
|
|
37
|
+
}))
|
|
38
|
+
|
|
39
|
+
async function abortRequest(): Promise<void> {
|
|
40
|
+
try {
|
|
41
|
+
await request
|
|
42
|
+
.get('http://test.com')
|
|
43
|
+
.option('fetchAPI', mockedFetch)
|
|
44
|
+
.flowControl('abort', 'test')
|
|
45
|
+
} catch (e) {
|
|
46
|
+
expect(e).toBeInstanceOf(DOMException)
|
|
47
|
+
expect((e as DOMException).name).toBe('AbortError')
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
void abortRequest()
|
|
52
|
+
|
|
53
|
+
await request
|
|
54
|
+
.get('http://test.com')
|
|
55
|
+
.option('fetchAPI', mockedFetch)
|
|
56
|
+
.flowControl('abort', 'test')
|
|
57
|
+
|
|
58
|
+
expect(mockedFetch).toBeCalledTimes(2)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('serial flowController request', async () => {
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
63
|
+
const mockedFetch = jest.fn((input: RequestInfo | URL, init?: RequestInit) => new Promise((resolve, reject) => {
|
|
64
|
+
let finished = false
|
|
65
|
+
|
|
66
|
+
if (init?.signal) {
|
|
67
|
+
const signal = init.signal
|
|
68
|
+
signal.onabort = () => {
|
|
69
|
+
if (finished) return
|
|
70
|
+
finished = true
|
|
71
|
+
if (signal.reason) reject(signal.reason)
|
|
72
|
+
reject(new DOMException('AbortError', 'AbortError'))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// sleet 500ms
|
|
77
|
+
setTimeout(
|
|
78
|
+
() => {
|
|
79
|
+
if (finished) return
|
|
80
|
+
finished = true
|
|
81
|
+
|
|
82
|
+
resolve(new Response(
|
|
83
|
+
JSON.stringify({ code: '200' }),
|
|
84
|
+
{
|
|
85
|
+
headers: {
|
|
86
|
+
'content-type': 'application/json',
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
))
|
|
90
|
+
},
|
|
91
|
+
100,
|
|
92
|
+
)
|
|
93
|
+
}))
|
|
94
|
+
|
|
95
|
+
async function serialRequest(): Promise<void> {
|
|
96
|
+
await request
|
|
97
|
+
.get('http://test.com')
|
|
98
|
+
.option('fetchAPI', mockedFetch)
|
|
99
|
+
.flowControl('serial', 'test')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
void serialRequest()
|
|
103
|
+
|
|
104
|
+
await request
|
|
105
|
+
.get('http://test.com')
|
|
106
|
+
.option('fetchAPI', mockedFetch)
|
|
107
|
+
.flowControl('abort', 'test')
|
|
108
|
+
|
|
109
|
+
expect(mockedFetch).toBeCalledTimes(2)
|
|
110
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
2
|
+
import { createRequest } from '~/index.js'
|
|
3
|
+
|
|
4
|
+
import type { KeqBaseOperation, KeqOperations } from '~/types/keq-operation.js'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
interface TestModule extends KeqOperations {
|
|
8
|
+
'http://test.com': {
|
|
9
|
+
get: {
|
|
10
|
+
requestParams: {}
|
|
11
|
+
requestQuery: {
|
|
12
|
+
q: string
|
|
13
|
+
}
|
|
14
|
+
requestHeaders: KeqBaseOperation['requestHeaders']
|
|
15
|
+
requestBody: {}
|
|
16
|
+
responseBody: {
|
|
17
|
+
data: 'test get'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
post: {
|
|
21
|
+
requestParams: {}
|
|
22
|
+
requestQuery: {
|
|
23
|
+
q: string
|
|
24
|
+
}
|
|
25
|
+
requestHeaders: {
|
|
26
|
+
'x-test': 'test'
|
|
27
|
+
}
|
|
28
|
+
requestBody: {
|
|
29
|
+
id: number
|
|
30
|
+
name: string
|
|
31
|
+
file: Buffer
|
|
32
|
+
}
|
|
33
|
+
responseBody: {
|
|
34
|
+
data: 'test post'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
'http://example.com': {
|
|
39
|
+
get: {
|
|
40
|
+
requestParams: {}
|
|
41
|
+
requestQuery: {}
|
|
42
|
+
requestHeaders: KeqBaseOperation['requestHeaders']
|
|
43
|
+
requestBody: {}
|
|
44
|
+
responseBody: {
|
|
45
|
+
data: 'example get'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const request = createRequest<TestModule>()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { expect, test } from '@jest/globals'
|
|
2
|
+
import { request } from './request.js'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
test('resolve response body by text', async () => {
|
|
6
|
+
const result = await request
|
|
7
|
+
.get('http://test.com')
|
|
8
|
+
.auth('username', 'password')
|
|
9
|
+
.resolveWith('text')
|
|
10
|
+
|
|
11
|
+
expect(result).toEqual('{"code":"200"}')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('resolve response body by json', async () => {
|
|
15
|
+
const result = await request
|
|
16
|
+
.get('http://test.com')
|
|
17
|
+
.auth('username', 'password')
|
|
18
|
+
.resolveWith<{ code: string }>('json')
|
|
19
|
+
|
|
20
|
+
expect(result).toEqual({ code: '200' })
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('resolve response body by blob', async () => {
|
|
24
|
+
const result = await request
|
|
25
|
+
.get('http://test.com')
|
|
26
|
+
.auth('username', 'password')
|
|
27
|
+
.resolveWith('blob')
|
|
28
|
+
|
|
29
|
+
expect(result).toBeInstanceOf(Blob)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('resolve response body by arrayBuffer', async () => {
|
|
33
|
+
const result = await request
|
|
34
|
+
.get('http://test.com')
|
|
35
|
+
.auth('username', 'password')
|
|
36
|
+
.resolveWith('array-buffer')
|
|
37
|
+
|
|
38
|
+
expect(result).toBeInstanceOf(ArrayBuffer)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('resolve response body', async () => {
|
|
42
|
+
const result = await request
|
|
43
|
+
.get('http://test.com')
|
|
44
|
+
.auth('username', 'password')
|
|
45
|
+
.resolveWith('response')
|
|
46
|
+
|
|
47
|
+
expect(result).toBeInstanceOf(Response)
|
|
48
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { expect, jest, test } from '@jest/globals'
|
|
2
|
+
import { KeqRetryOn } from '~/index.js'
|
|
3
|
+
import { request } from './request.js'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
test('send request retry twice', async () => {
|
|
7
|
+
const mockedFetch = jest.fn()
|
|
8
|
+
const retryOn = jest.fn<KeqRetryOn>(() => true)
|
|
9
|
+
const mockedListener = jest.fn()
|
|
10
|
+
|
|
11
|
+
await request
|
|
12
|
+
.get('http://test.com')
|
|
13
|
+
.retry(2, 10, retryOn)
|
|
14
|
+
.option('fetchAPI', mockedFetch)
|
|
15
|
+
.on('retry', mockedListener)
|
|
16
|
+
|
|
17
|
+
expect(mockedFetch.mock.calls).toHaveLength(3)
|
|
18
|
+
expect(mockedListener.mock.calls).toHaveLength(2)
|
|
19
|
+
|
|
20
|
+
expect(retryOn.mock.calls.length).toBe(2)
|
|
21
|
+
expect(retryOn.mock.calls[0][0]).toBe(0)
|
|
22
|
+
expect(retryOn.mock.calls[1][0]).toBe(1)
|
|
23
|
+
expect(retryOn.mock.calls[1][2].retry?.delay).toBe(10)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('send request retry once', async () => {
|
|
27
|
+
const mockedFetch = jest.fn()
|
|
28
|
+
|
|
29
|
+
await request
|
|
30
|
+
.get('http://test.com')
|
|
31
|
+
.retry(2, 0)
|
|
32
|
+
.option('fetchAPI', mockedFetch)
|
|
33
|
+
|
|
34
|
+
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
35
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals'
|
|
2
|
+
import { Mock } from 'jest-mock'
|
|
3
|
+
import { request } from './request.js'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
describe('send multipart/form-data request', () => {
|
|
7
|
+
test('use FormData Class', async () => {
|
|
8
|
+
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
9
|
+
|
|
10
|
+
const formData = new FormData()
|
|
11
|
+
|
|
12
|
+
const resumeFile = new Blob(['test'], { type: 'text/plain' })
|
|
13
|
+
formData.append('name', 'John')
|
|
14
|
+
formData.append('resume', resumeFile, 'test.txt')
|
|
15
|
+
formData.append('friends', 'Tom')
|
|
16
|
+
formData.append('friends', 'Bob')
|
|
17
|
+
|
|
18
|
+
await request
|
|
19
|
+
.post('http://test.com')
|
|
20
|
+
.send(formData)
|
|
21
|
+
|
|
22
|
+
const init = mockedFetch.mock.calls[0][1]
|
|
23
|
+
expect(init).toBeDefined()
|
|
24
|
+
|
|
25
|
+
const headers = init?.headers as Headers
|
|
26
|
+
|
|
27
|
+
// FormData 需要自动删除 Content-Type
|
|
28
|
+
expect(headers.get('Content-Type')).toBeNull()
|
|
29
|
+
|
|
30
|
+
const body = init?.body as FormData
|
|
31
|
+
|
|
32
|
+
expect(body.getAll('name')).toEqual(['John'])
|
|
33
|
+
expect(body.getAll('friends')).toEqual(['Tom', 'Bob'])
|
|
34
|
+
expect((body.get('resume') as File).name).toBe('test.txt')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('use keq API', async () => {
|
|
38
|
+
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
39
|
+
|
|
40
|
+
await request
|
|
41
|
+
.post('http://test.com')
|
|
42
|
+
.field('name', 'John')
|
|
43
|
+
.field('friends', ['Tom', 'Bob'])
|
|
44
|
+
.field({ age: '12' })
|
|
45
|
+
.attach('file1', new Blob(['test'], { type: 'text/plain' }), 'file1.txt')
|
|
46
|
+
|
|
47
|
+
const init = mockedFetch.mock.calls[0][1]
|
|
48
|
+
expect(init).toBeDefined()
|
|
49
|
+
|
|
50
|
+
const headers = init?.headers as Headers
|
|
51
|
+
|
|
52
|
+
// FormData 需要自动删除 Content-Type
|
|
53
|
+
expect(headers.get('Content-Type')).toBeNull()
|
|
54
|
+
|
|
55
|
+
const body = init?.body as FormData
|
|
56
|
+
|
|
57
|
+
expect(body.getAll('name')).toEqual(['John'])
|
|
58
|
+
expect(body.getAll('friends')).toEqual(['Tom', 'Bob'])
|
|
59
|
+
expect(body.getAll('age')).toEqual(['12'])
|
|
60
|
+
expect((body.get('file1') as File).name).toBe('file1.txt')
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
test('send application/json request', async () => {
|
|
66
|
+
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
67
|
+
|
|
68
|
+
const requestBody = {
|
|
69
|
+
name: 'John',
|
|
70
|
+
friends: ['Tom', 'Bob'],
|
|
71
|
+
}
|
|
72
|
+
await request
|
|
73
|
+
.post('http://test.com')
|
|
74
|
+
.send(requestBody)
|
|
75
|
+
|
|
76
|
+
const init = mockedFetch.mock.calls[0][1]
|
|
77
|
+
expect(init).toBeDefined()
|
|
78
|
+
|
|
79
|
+
const headers = init?.headers as Headers
|
|
80
|
+
expect(headers.get('Content-Type')).toBe('application/json')
|
|
81
|
+
|
|
82
|
+
const body = init?.body as string
|
|
83
|
+
expect(body).toBe(JSON.stringify(requestBody))
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
test('send application/x-www-form-urlencoded request', async () => {
|
|
88
|
+
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
89
|
+
|
|
90
|
+
await request
|
|
91
|
+
.post('http://test.com')
|
|
92
|
+
.type('form')
|
|
93
|
+
.send({
|
|
94
|
+
name: 'John',
|
|
95
|
+
friends: ['Tom', 'Bob'],
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const init = mockedFetch.mock.calls[0][1]
|
|
99
|
+
expect(init).toBeDefined()
|
|
100
|
+
|
|
101
|
+
const headers = init?.headers as Headers
|
|
102
|
+
expect(headers.get('Content-Type')).toBe('application/x-www-form-urlencoded')
|
|
103
|
+
|
|
104
|
+
const body = init?.body as URLSearchParams
|
|
105
|
+
expect(body).toBeInstanceOf(URLSearchParams)
|
|
106
|
+
expect(body.toString()).toBe('name=John&friends=Tom&friends=Bob')
|
|
107
|
+
})
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { expect, test } from '@jest/globals'
|
|
2
|
+
import { Mock } from 'jest-mock'
|
|
3
|
+
import { request } from './request.js'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
test('send get request', async () => {
|
|
7
|
+
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
8
|
+
|
|
9
|
+
await request
|
|
10
|
+
.get('http://test.com')
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
14
|
+
|
|
15
|
+
const url = mockedFetch.mock.calls[0][0]
|
|
16
|
+
const init = mockedFetch.mock.calls[0][1]
|
|
17
|
+
|
|
18
|
+
expect(url).toBe('http://test.com/')
|
|
19
|
+
expect(init?.method).toBe('GET')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
test('send post request', async () => {
|
|
24
|
+
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
25
|
+
|
|
26
|
+
await request
|
|
27
|
+
.post('http://test.com')
|
|
28
|
+
|
|
29
|
+
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
30
|
+
|
|
31
|
+
const url = mockedFetch.mock.calls[0][0]
|
|
32
|
+
const init = mockedFetch.mock.calls[0][1]
|
|
33
|
+
|
|
34
|
+
expect(url).toBe('http://test.com/')
|
|
35
|
+
expect(init?.method).toBe('POST')
|
|
36
|
+
|
|
37
|
+
expect(init?.headers).toBeInstanceOf(Headers)
|
|
38
|
+
|
|
39
|
+
const headers = init?.headers as Headers
|
|
40
|
+
expect(headers.get('content-type')).toBeNull()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('send put request', async () => {
|
|
44
|
+
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
45
|
+
|
|
46
|
+
await request
|
|
47
|
+
.put('http://test.com')
|
|
48
|
+
|
|
49
|
+
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
50
|
+
|
|
51
|
+
const url = mockedFetch.mock.calls[0][0]
|
|
52
|
+
const init = mockedFetch.mock.calls[0][1]
|
|
53
|
+
|
|
54
|
+
expect(url).toBe('http://test.com/')
|
|
55
|
+
expect(init?.method).toBe('PUT')
|
|
56
|
+
|
|
57
|
+
expect(init?.headers).toBeInstanceOf(Headers)
|
|
58
|
+
|
|
59
|
+
const headers = init?.headers as Headers
|
|
60
|
+
expect(headers.get('content-type')).toBeNull()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('send patch request', async () => {
|
|
64
|
+
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
65
|
+
|
|
66
|
+
await request
|
|
67
|
+
.patch('http://test.com')
|
|
68
|
+
|
|
69
|
+
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
70
|
+
|
|
71
|
+
const url = mockedFetch.mock.calls[0][0]
|
|
72
|
+
const init = mockedFetch.mock.calls[0][1]
|
|
73
|
+
|
|
74
|
+
expect(url).toBe('http://test.com/')
|
|
75
|
+
expect(init?.method).toBe('PATCH')
|
|
76
|
+
|
|
77
|
+
expect(init?.headers).toBeInstanceOf(Headers)
|
|
78
|
+
|
|
79
|
+
const headers = init?.headers as Headers
|
|
80
|
+
expect(headers.get('content-type')).toBeNull()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('send head request', async () => {
|
|
84
|
+
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
85
|
+
|
|
86
|
+
await request
|
|
87
|
+
.head('http://test.com')
|
|
88
|
+
|
|
89
|
+
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
90
|
+
|
|
91
|
+
const url = mockedFetch.mock.calls[0][0]
|
|
92
|
+
const init = mockedFetch.mock.calls[0][1]
|
|
93
|
+
|
|
94
|
+
expect(url).toBe('http://test.com/')
|
|
95
|
+
expect(init?.method).toBe('HEAD')
|
|
96
|
+
|
|
97
|
+
expect(init?.headers).toBeInstanceOf(Headers)
|
|
98
|
+
|
|
99
|
+
const headers = init?.headers as Headers
|
|
100
|
+
expect(headers.get('content-type')).toBeNull()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('send DELETE request', async () => {
|
|
104
|
+
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
105
|
+
|
|
106
|
+
await request
|
|
107
|
+
.del('http://test.com')
|
|
108
|
+
|
|
109
|
+
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
110
|
+
|
|
111
|
+
const url = mockedFetch.mock.calls[0][0]
|
|
112
|
+
const init = mockedFetch.mock.calls[0][1]
|
|
113
|
+
|
|
114
|
+
expect(url).toBe('http://test.com/')
|
|
115
|
+
expect(init?.method).toBe('DELETE')
|
|
116
|
+
|
|
117
|
+
expect(init?.headers).toBeInstanceOf(Headers)
|
|
118
|
+
|
|
119
|
+
const headers = init?.headers as Headers
|
|
120
|
+
expect(headers.get('content-type')).toBeNull()
|
|
121
|
+
})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { expect, jest, test } from '@jest/globals'
|
|
2
|
+
import { request } from './request.js'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
test('timeout request', async () => {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
7
|
+
const mockedFetch = jest.fn((input: RequestInfo | URL, init?: RequestInit) => new Promise((resolve, reject) => {
|
|
8
|
+
let finished = false
|
|
9
|
+
|
|
10
|
+
if (init?.signal) {
|
|
11
|
+
const signal = init.signal
|
|
12
|
+
signal.onabort = () => {
|
|
13
|
+
if (finished) return
|
|
14
|
+
finished = true
|
|
15
|
+
if (signal.reason) reject(signal.reason)
|
|
16
|
+
reject(new DOMException('AbortError', 'AbortError'))
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// sleet 500ms
|
|
21
|
+
setTimeout(
|
|
22
|
+
() => {
|
|
23
|
+
if (finished) return
|
|
24
|
+
finished = true
|
|
25
|
+
|
|
26
|
+
resolve(new Response(
|
|
27
|
+
JSON.stringify({ code: '200' }),
|
|
28
|
+
{
|
|
29
|
+
headers: {
|
|
30
|
+
'content-type': 'application/json',
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
))
|
|
34
|
+
},
|
|
35
|
+
500,
|
|
36
|
+
)
|
|
37
|
+
}))
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await request
|
|
41
|
+
.get('http://test.com')
|
|
42
|
+
.option('fetchAPI', mockedFetch)
|
|
43
|
+
.timeout(100)
|
|
44
|
+
} catch (e) {
|
|
45
|
+
expect(e).toBeInstanceOf(DOMException)
|
|
46
|
+
expect((e as DOMException).name).toBe('AbortError')
|
|
47
|
+
}
|
|
48
|
+
})
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, jest } from '@jest/globals'
|
|
2
|
+
// import { TextEncoder, TextDecoder } from 'util'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// https://github.com/inrupt/solid-client-authn-js/issues/1676
|
|
6
|
+
// global.TextEncoder = TextEncoder
|
|
7
|
+
// global.TextDecoder = TextDecoder as any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
11
|
+
const mockedFetch = jest.fn((input: RequestInfo | URL, init?: RequestInit) => Promise.resolve(new Response(
|
|
12
|
+
JSON.stringify({ code: '200' }),
|
|
13
|
+
{
|
|
14
|
+
headers: {
|
|
15
|
+
'content-type': 'application/json',
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
)))
|
|
19
|
+
|
|
20
|
+
const unMockedFetch = global.fetch
|
|
21
|
+
|
|
22
|
+
beforeAll(() => {
|
|
23
|
+
global.fetch = mockedFetch as unknown as typeof fetch
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
afterAll(() => {
|
|
27
|
+
global.fetch = unMockedFetch
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
mockedFetch.mockClear()
|
|
32
|
+
})
|
package/dist/esm/src/keq.js
CHANGED
|
@@ -5,6 +5,7 @@ import { isBlob } from './is/is-blob.js';
|
|
|
5
5
|
import { isFile } from './is/is-file.js';
|
|
6
6
|
import { isFormData } from './is/is-form-data.js';
|
|
7
7
|
import { isHeaders } from './is/is-headers.js';
|
|
8
|
+
import { isBuffer } from './is/is-buffer.js';
|
|
8
9
|
import { isUrlSearchParams } from './is/is-url-search-params.js';
|
|
9
10
|
import { assignKeqRequestBody } from './util/assign-keq-request-body.js';
|
|
10
11
|
import { base64Encode } from './util/base64.js';
|
|
@@ -144,7 +145,7 @@ export class Keq extends Core {
|
|
|
144
145
|
else if (isFile(value)) {
|
|
145
146
|
file = value;
|
|
146
147
|
}
|
|
147
|
-
else if (value
|
|
148
|
+
else if (isBuffer(value)) {
|
|
148
149
|
const formData = new FormData();
|
|
149
150
|
formData.set(key, new Blob([value]), arg3);
|
|
150
151
|
file = formData.get(key);
|
|
@@ -2,7 +2,7 @@ import { URL } from 'whatwg-url';
|
|
|
2
2
|
import { Exception } from '../exception/exception.js';
|
|
3
3
|
import { compilePathnameTemplate } from '../util/compile-pathname-template.js';
|
|
4
4
|
import { ABORT_PROPERTY } from '../constant.js';
|
|
5
|
-
import {
|
|
5
|
+
import { isBuffer } from "../is/is-buffer.js";
|
|
6
6
|
function compileUrl(obj, routeParams) {
|
|
7
7
|
const url = new URL(typeof obj === 'string' ? obj : obj.href);
|
|
8
8
|
try {
|
|
@@ -64,7 +64,7 @@ function compileBody(ctx) {
|
|
|
64
64
|
request.headers.delete('content-type');
|
|
65
65
|
return form;
|
|
66
66
|
}
|
|
67
|
-
if (
|
|
67
|
+
if (isBuffer(body))
|
|
68
68
|
return body;
|
|
69
69
|
if (body === undefined)
|
|
70
70
|
return body;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isBuffer } from "../is/is-buffer.js";
|
|
1
2
|
import { isBlob } from '../is/is-blob.js';
|
|
2
3
|
import { isFile } from '../is/is-file.js';
|
|
3
4
|
import { isFormData } from '../is/is-form-data.js';
|
|
@@ -24,7 +25,7 @@ export function cloneBody(obj) {
|
|
|
24
25
|
if (isBlob(obj)) {
|
|
25
26
|
return obj;
|
|
26
27
|
}
|
|
27
|
-
if (obj
|
|
28
|
+
if (isBuffer(obj)) {
|
|
28
29
|
return obj;
|
|
29
30
|
}
|
|
30
31
|
if (obj === null) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
(function (factory) {
|
|
2
|
+
if (typeof module === "object" && typeof module.exports === "object") {
|
|
3
|
+
var v = factory(require, exports);
|
|
4
|
+
if (v !== undefined) module.exports = v;
|
|
5
|
+
}
|
|
6
|
+
else if (typeof define === "function" && define.amd) {
|
|
7
|
+
define(["require", "exports", "./is-browser"], factory);
|
|
8
|
+
}
|
|
9
|
+
})(function (require, exports) {
|
|
10
|
+
"use strict";
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.isBuffer = void 0;
|
|
13
|
+
const is_browser_1 = require("./is-browser");
|
|
14
|
+
function isBuffer(obj) {
|
|
15
|
+
return (0, is_browser_1.isBrowser)() ? false : Buffer.isBuffer(obj);
|
|
16
|
+
}
|
|
17
|
+
exports.isBuffer = isBuffer;
|
|
18
|
+
});
|
package/dist/umd/src/keq.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
if (v !== undefined) module.exports = v;
|
|
5
5
|
}
|
|
6
6
|
else if (typeof define === "function" && define.amd) {
|
|
7
|
-
define(["require", "exports", "./core.js", "./exception/exception.js", "./exception/invalid-arguments.exception.js", "./is/is-blob.js", "./is/is-file.js", "./is/is-form-data.js", "./is/is-headers.js", "./is/is-url-search-params.js", "./util/assign-keq-request-body.js", "./util/base64.js", "./util/fix-content-type.js", "./util/get-unique-code-identifier.js"], factory);
|
|
7
|
+
define(["require", "exports", "./core.js", "./exception/exception.js", "./exception/invalid-arguments.exception.js", "./is/is-blob.js", "./is/is-file.js", "./is/is-form-data.js", "./is/is-headers.js", "./is/is-buffer.js", "./is/is-url-search-params.js", "./util/assign-keq-request-body.js", "./util/base64.js", "./util/fix-content-type.js", "./util/get-unique-code-identifier.js"], factory);
|
|
8
8
|
}
|
|
9
9
|
})(function (require, exports) {
|
|
10
10
|
"use strict";
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
const is_file_js_1 = require("./is/is-file.js");
|
|
18
18
|
const is_form_data_js_1 = require("./is/is-form-data.js");
|
|
19
19
|
const is_headers_js_1 = require("./is/is-headers.js");
|
|
20
|
+
const is_buffer_js_1 = require("./is/is-buffer.js");
|
|
20
21
|
const is_url_search_params_js_1 = require("./is/is-url-search-params.js");
|
|
21
22
|
const assign_keq_request_body_js_1 = require("./util/assign-keq-request-body.js");
|
|
22
23
|
const base64_js_1 = require("./util/base64.js");
|
|
@@ -156,7 +157,7 @@
|
|
|
156
157
|
else if ((0, is_file_js_1.isFile)(value)) {
|
|
157
158
|
file = value;
|
|
158
159
|
}
|
|
159
|
-
else if (
|
|
160
|
+
else if ((0, is_buffer_js_1.isBuffer)(value)) {
|
|
160
161
|
const formData = new FormData();
|
|
161
162
|
formData.set(key, new Blob([value]), arg3);
|
|
162
163
|
file = formData.get(key);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
if (v !== undefined) module.exports = v;
|
|
5
5
|
}
|
|
6
6
|
else if (typeof define === "function" && define.amd) {
|
|
7
|
-
define(["require", "exports", "whatwg-url", "../exception/exception.js", "../util/compile-pathname-template.js", "../constant.js", "../is/is-
|
|
7
|
+
define(["require", "exports", "whatwg-url", "../exception/exception.js", "../util/compile-pathname-template.js", "../constant.js", "../is/is-buffer.js"], factory);
|
|
8
8
|
}
|
|
9
9
|
})(function (require, exports) {
|
|
10
10
|
"use strict";
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
const exception_js_1 = require("../exception/exception.js");
|
|
15
15
|
const compile_pathname_template_js_1 = require("../util/compile-pathname-template.js");
|
|
16
16
|
const constant_js_1 = require("../constant.js");
|
|
17
|
-
const
|
|
17
|
+
const is_buffer_js_1 = require("../is/is-buffer.js");
|
|
18
18
|
function compileUrl(obj, routeParams) {
|
|
19
19
|
const url = new whatwg_url_1.URL(typeof obj === 'string' ? obj : obj.href);
|
|
20
20
|
try {
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
request.headers.delete('content-type');
|
|
77
77
|
return form;
|
|
78
78
|
}
|
|
79
|
-
if (
|
|
79
|
+
if ((0, is_buffer_js_1.isBuffer)(body))
|
|
80
80
|
return body;
|
|
81
81
|
if (body === undefined)
|
|
82
82
|
return body;
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
if (v !== undefined) module.exports = v;
|
|
5
5
|
}
|
|
6
6
|
else if (typeof define === "function" && define.amd) {
|
|
7
|
-
define(["require", "exports", "../is/is-blob.js", "../is/is-file.js", "../is/is-form-data.js", "../is/is-object.js", "../is/is-url-search-params.js"], factory);
|
|
7
|
+
define(["require", "exports", "../is/is-buffer.js", "../is/is-blob.js", "../is/is-file.js", "../is/is-form-data.js", "../is/is-object.js", "../is/is-url-search-params.js"], factory);
|
|
8
8
|
}
|
|
9
9
|
})(function (require, exports) {
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.cloneBody = void 0;
|
|
13
|
+
const is_buffer_js_1 = require("../is/is-buffer.js");
|
|
13
14
|
const is_blob_js_1 = require("../is/is-blob.js");
|
|
14
15
|
const is_file_js_1 = require("../is/is-file.js");
|
|
15
16
|
const is_form_data_js_1 = require("../is/is-form-data.js");
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
if ((0, is_blob_js_1.isBlob)(obj)) {
|
|
37
38
|
return obj;
|
|
38
39
|
}
|
|
39
|
-
if (
|
|
40
|
+
if ((0, is_buffer_js_1.isBuffer)(obj)) {
|
|
40
41
|
return obj;
|
|
41
42
|
}
|
|
42
43
|
if (obj === null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keq",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.9",
|
|
4
4
|
"description": "Request API write by Typescript for flexibility, readability, and a low learning curve.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"request",
|
|
@@ -55,19 +55,20 @@
|
|
|
55
55
|
"@rushstack/eslint-patch": "^1.10.3",
|
|
56
56
|
"@types/clone": "^2.1.4",
|
|
57
57
|
"@types/minimatch": "^5.1.2",
|
|
58
|
-
"@types/node": "^20.
|
|
59
|
-
"@typescript-eslint/eslint-plugin": "^7.
|
|
60
|
-
"@typescript-eslint/parser": "^7.
|
|
58
|
+
"@types/node": "^20.14.1",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^7.12.0",
|
|
60
|
+
"@typescript-eslint/parser": "^7.12.0",
|
|
61
61
|
"eslint": "^8.57.0",
|
|
62
62
|
"husky": "^9.0.11",
|
|
63
63
|
"is-ci": "^3.0.1",
|
|
64
64
|
"jest": "^29.7.0",
|
|
65
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
65
66
|
"jest-mock": "^29.7.0",
|
|
66
67
|
"standard-version": "^9.5.0",
|
|
67
|
-
"ts-jest": "^29.1.
|
|
68
|
+
"ts-jest": "^29.1.5",
|
|
68
69
|
"ts-node": "^10.9.2",
|
|
69
|
-
"ts-patch": "^3.
|
|
70
|
-
"typescript": "
|
|
70
|
+
"ts-patch": "^3.2.0",
|
|
71
|
+
"typescript": "5.4.5",
|
|
71
72
|
"typescript-transform-paths": "^3.4.7"
|
|
72
73
|
},
|
|
73
74
|
"packageManager": "pnpm@9.1.4",
|