rekwest 4.4.4 → 4.5.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/README.md +12 -12
- package/dist/cookies.js +41 -3
- package/dist/defaults.js +2 -0
- package/dist/formdata.js +1 -0
- package/dist/index.js +2 -1
- package/dist/postflight.js +8 -4
- package/dist/preflight.js +9 -5
- package/dist/transfer.js +105 -0
- package/dist/transform.js +77 -0
- package/dist/utils.js +8 -163
- package/package.json +4 -4
- package/src/cookies.mjs +53 -7
- package/src/defaults.mjs +2 -0
- package/src/formdata.mjs +243 -241
- package/src/index.mjs +1 -1
- package/src/postflight.mjs +9 -4
- package/src/preflight.mjs +10 -5
- package/src/transfer.mjs +114 -0
- package/src/transform.mjs +75 -0
- package/src/utils.mjs +9 -174
package/src/transfer.mjs
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import http2 from 'node:http2';
|
|
3
|
+
import https from 'node:https';
|
|
4
|
+
import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
|
|
5
|
+
import { ackn } from './ackn.mjs';
|
|
6
|
+
import { RequestError } from './errors.mjs';
|
|
7
|
+
import { postflight } from './postflight.mjs';
|
|
8
|
+
import { preflight } from './preflight.mjs';
|
|
9
|
+
import { transform } from './transform.mjs';
|
|
10
|
+
import {
|
|
11
|
+
affix,
|
|
12
|
+
dispatch,
|
|
13
|
+
maxRetryAfterError,
|
|
14
|
+
} from './utils.mjs';
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
HTTP2_HEADER_RETRY_AFTER,
|
|
18
|
+
} = http2.constants;
|
|
19
|
+
|
|
20
|
+
export const transfer = async (options, overact) => {
|
|
21
|
+
const { digest, redirected, thenable, url } = options;
|
|
22
|
+
|
|
23
|
+
if (options.follow === 0) {
|
|
24
|
+
throw new RequestError(`Maximum redirect reached at: ${ url.href }`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (url.protocol === 'https:') {
|
|
28
|
+
options = !options.h2 ? await ackn(options) : {
|
|
29
|
+
...options,
|
|
30
|
+
createConnection: null,
|
|
31
|
+
protocol: url.protocol,
|
|
32
|
+
};
|
|
33
|
+
} else if (Reflect.has(options, 'alpnProtocol')) {
|
|
34
|
+
[
|
|
35
|
+
'alpnProtocol',
|
|
36
|
+
'createConnection',
|
|
37
|
+
'h2',
|
|
38
|
+
'protocol',
|
|
39
|
+
].forEach((it) => Reflect.deleteProperty(options, it));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
options = await transform(preflight(options));
|
|
44
|
+
} catch (ex) {
|
|
45
|
+
options.createConnection?.().destroy();
|
|
46
|
+
throw ex;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const promise = new Promise((resolve, reject) => {
|
|
50
|
+
let client, req;
|
|
51
|
+
|
|
52
|
+
if (options.h2) {
|
|
53
|
+
client = http2.connect(url.origin, options);
|
|
54
|
+
req = client.request(options.headers, options);
|
|
55
|
+
} else {
|
|
56
|
+
const { request } = url.protocol === 'http:' ? http : https;
|
|
57
|
+
|
|
58
|
+
req = request(url, options);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
affix(client, req, options);
|
|
62
|
+
|
|
63
|
+
req.once('error', reject);
|
|
64
|
+
req.once('frameError', reject);
|
|
65
|
+
req.once('goaway', reject);
|
|
66
|
+
req.once('response', (res) => postflight(req, res, options, {
|
|
67
|
+
reject,
|
|
68
|
+
resolve,
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
dispatch(options, req);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const res = await promise;
|
|
76
|
+
|
|
77
|
+
if (digest && !redirected) {
|
|
78
|
+
res.body = await res.body();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return res;
|
|
82
|
+
} catch (ex) {
|
|
83
|
+
const { maxRetryAfter, retry } = options;
|
|
84
|
+
|
|
85
|
+
if (retry?.attempts && retry?.statusCodes.includes(ex.statusCode)) {
|
|
86
|
+
let { interval } = retry;
|
|
87
|
+
|
|
88
|
+
if (retry.retryAfter && ex.headers[HTTP2_HEADER_RETRY_AFTER]) {
|
|
89
|
+
interval = ex.headers[HTTP2_HEADER_RETRY_AFTER];
|
|
90
|
+
interval = Number(interval) * 1000 || new Date(interval) - Date.now();
|
|
91
|
+
if (interval > maxRetryAfter) {
|
|
92
|
+
throw maxRetryAfterError(interval, { cause: ex });
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
interval = new Function('interval', `return Math.ceil(${ retry.backoffStrategy });`)(interval);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
retry.attempts--;
|
|
99
|
+
retry.interval = interval;
|
|
100
|
+
|
|
101
|
+
return setTimeoutPromise(interval).then(() => overact(url, options));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (digest && !redirected && ex.body) {
|
|
105
|
+
ex.body = await ex.body();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!thenable) {
|
|
109
|
+
throw ex;
|
|
110
|
+
} else {
|
|
111
|
+
return ex;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import http2 from 'node:http2';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
|
+
import { buffer } from 'node:stream/consumers';
|
|
4
|
+
import { types } from 'node:util';
|
|
5
|
+
import { File } from './file.mjs';
|
|
6
|
+
import { FormData } from './formdata.mjs';
|
|
7
|
+
import {
|
|
8
|
+
APPLICATION_FORM_URLENCODED,
|
|
9
|
+
APPLICATION_JSON,
|
|
10
|
+
APPLICATION_OCTET_STREAM,
|
|
11
|
+
} from './mediatypes.mjs';
|
|
12
|
+
import { compress } from './utils.mjs';
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
HTTP2_HEADER_CONTENT_ENCODING,
|
|
16
|
+
HTTP2_HEADER_CONTENT_LENGTH,
|
|
17
|
+
HTTP2_HEADER_CONTENT_TYPE,
|
|
18
|
+
} = http2.constants;
|
|
19
|
+
|
|
20
|
+
export const transform = async (options) => {
|
|
21
|
+
let { body, headers } = options;
|
|
22
|
+
|
|
23
|
+
if (!body) {
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (File.alike(body)) {
|
|
28
|
+
headers = {
|
|
29
|
+
[HTTP2_HEADER_CONTENT_LENGTH]: body.size,
|
|
30
|
+
[HTTP2_HEADER_CONTENT_TYPE]: body.type || APPLICATION_OCTET_STREAM,
|
|
31
|
+
};
|
|
32
|
+
body = body.stream();
|
|
33
|
+
} else if (FormData.alike(body)) {
|
|
34
|
+
body = FormData.actuate(body);
|
|
35
|
+
headers = { [HTTP2_HEADER_CONTENT_TYPE]: body.contentType };
|
|
36
|
+
} else if (!Buffer.isBuffer(body)) {
|
|
37
|
+
if (types.isAnyArrayBuffer(body)) {
|
|
38
|
+
body = Buffer.from(body);
|
|
39
|
+
} else if (types.isArrayBufferView(body)) {
|
|
40
|
+
body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
|
|
41
|
+
} else if (body === Object(body) && !Reflect.has(body, Symbol.asyncIterator)) {
|
|
42
|
+
if (body.constructor === URLSearchParams) {
|
|
43
|
+
headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_FORM_URLENCODED };
|
|
44
|
+
body = body.toString();
|
|
45
|
+
} else if (!(!Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
|
|
46
|
+
headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_JSON };
|
|
47
|
+
body = JSON.stringify(body);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const encodings = options.headers[HTTP2_HEADER_CONTENT_ENCODING];
|
|
53
|
+
|
|
54
|
+
if (body === Object(body)
|
|
55
|
+
&& (Reflect.has(body, Symbol.asyncIterator) || (!Array.isArray(body) && Reflect.has(body, Symbol.iterator)))) {
|
|
56
|
+
body = encodings ? compress(Readable.from(body), encodings) : Readable.from(body);
|
|
57
|
+
} else if (encodings) {
|
|
58
|
+
body = await buffer(compress(Readable.from(body), encodings));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
Object.assign(options.headers, {
|
|
62
|
+
...headers,
|
|
63
|
+
...!body[Symbol.asyncIterator] && {
|
|
64
|
+
[HTTP2_HEADER_CONTENT_LENGTH]: Buffer.byteLength(body),
|
|
65
|
+
},
|
|
66
|
+
...options.headers[HTTP2_HEADER_CONTENT_TYPE] && {
|
|
67
|
+
[HTTP2_HEADER_CONTENT_TYPE]: options.headers[HTTP2_HEADER_CONTENT_TYPE],
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
...options,
|
|
73
|
+
body,
|
|
74
|
+
};
|
|
75
|
+
};
|
package/src/utils.mjs
CHANGED
|
@@ -1,34 +1,13 @@
|
|
|
1
|
-
import http from 'node:http';
|
|
2
1
|
import http2 from 'node:http2';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
pipeline,
|
|
6
|
-
Readable,
|
|
7
|
-
} from 'node:stream';
|
|
8
|
-
import { buffer } from 'node:stream/consumers';
|
|
9
|
-
import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
|
|
10
|
-
import { types } from 'node:util';
|
|
2
|
+
import { pipeline } from 'node:stream';
|
|
11
3
|
import zlib from 'node:zlib';
|
|
12
|
-
import { ackn } from './ackn.mjs';
|
|
13
4
|
import defaults from './defaults.mjs';
|
|
14
5
|
import {
|
|
15
6
|
RequestError,
|
|
16
7
|
TimeoutError,
|
|
17
8
|
} from './errors.mjs';
|
|
18
|
-
import { File } from './file.mjs';
|
|
19
|
-
import { FormData } from './formdata.mjs';
|
|
20
|
-
import {
|
|
21
|
-
APPLICATION_FORM_URLENCODED,
|
|
22
|
-
APPLICATION_JSON,
|
|
23
|
-
APPLICATION_OCTET_STREAM,
|
|
24
|
-
} from './mediatypes.mjs';
|
|
25
|
-
import { postflight } from './postflight.mjs';
|
|
26
|
-
import { preflight } from './preflight.mjs';
|
|
27
9
|
|
|
28
10
|
const {
|
|
29
|
-
HTTP2_HEADER_CONTENT_ENCODING,
|
|
30
|
-
HTTP2_HEADER_CONTENT_LENGTH,
|
|
31
|
-
HTTP2_HEADER_CONTENT_TYPE,
|
|
32
11
|
HTTP2_HEADER_RETRY_AFTER,
|
|
33
12
|
HTTP2_HEADER_STATUS,
|
|
34
13
|
} = http2.constants;
|
|
@@ -180,6 +159,10 @@ export const normalize = (url, options = {}) => {
|
|
|
180
159
|
url = `${ url }`.replace(/(?<!:)\/+/g, '/');
|
|
181
160
|
}
|
|
182
161
|
|
|
162
|
+
if (options.stripTrailingSlash) {
|
|
163
|
+
url = `${ url }`.replace(/\/$|\/(?=#)|\/(?=\?)/g, '');
|
|
164
|
+
}
|
|
165
|
+
|
|
183
166
|
url = new URL(url, options.baseURL);
|
|
184
167
|
|
|
185
168
|
return Object.assign(options, { url });
|
|
@@ -197,157 +180,9 @@ export async function* tap(value) {
|
|
|
197
180
|
}
|
|
198
181
|
}
|
|
199
182
|
|
|
200
|
-
export const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
throw new RequestError(`Maximum redirect reached at: ${ url.href }`);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (url.protocol === 'https:') {
|
|
208
|
-
options = !options.h2 ? await ackn(options) : {
|
|
209
|
-
...options,
|
|
210
|
-
createConnection: null,
|
|
211
|
-
protocol: url.protocol,
|
|
212
|
-
};
|
|
213
|
-
} else if (Reflect.has(options, 'alpnProtocol')) {
|
|
214
|
-
[
|
|
215
|
-
'alpnProtocol',
|
|
216
|
-
'createConnection',
|
|
217
|
-
'h2',
|
|
218
|
-
'protocol',
|
|
219
|
-
].forEach((it) => Reflect.deleteProperty(options, it));
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
try {
|
|
223
|
-
options = await transform(preflight(options));
|
|
224
|
-
} catch (ex) {
|
|
225
|
-
options.createConnection?.().destroy();
|
|
226
|
-
throw ex;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const promise = new Promise((resolve, reject) => {
|
|
230
|
-
let client, req;
|
|
231
|
-
|
|
232
|
-
if (options.h2) {
|
|
233
|
-
client = http2.connect(url.origin, options);
|
|
234
|
-
req = client.request(options.headers, options);
|
|
235
|
-
} else {
|
|
236
|
-
const { request } = url.protocol === 'http:' ? http : https;
|
|
237
|
-
|
|
238
|
-
req = request(url, options);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
affix(client, req, options);
|
|
242
|
-
|
|
243
|
-
req.once('error', reject);
|
|
244
|
-
req.once('frameError', reject);
|
|
245
|
-
req.once('goaway', reject);
|
|
246
|
-
req.once('response', (res) => postflight(req, res, options, {
|
|
247
|
-
reject,
|
|
248
|
-
resolve,
|
|
249
|
-
}));
|
|
250
|
-
|
|
251
|
-
dispatch(options, req);
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
try {
|
|
255
|
-
const res = await promise;
|
|
256
|
-
|
|
257
|
-
if (digest && !redirected) {
|
|
258
|
-
res.body = await res.body();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return res;
|
|
262
|
-
} catch (ex) {
|
|
263
|
-
const { maxRetryAfter, retry } = options;
|
|
264
|
-
|
|
265
|
-
if (retry?.attempts && retry?.statusCodes.includes(ex.statusCode)) {
|
|
266
|
-
let { interval } = retry;
|
|
267
|
-
|
|
268
|
-
if (retry.retryAfter && ex.headers[HTTP2_HEADER_RETRY_AFTER]) {
|
|
269
|
-
interval = ex.headers[HTTP2_HEADER_RETRY_AFTER];
|
|
270
|
-
interval = Number(interval) * 1000 || new Date(interval) - Date.now();
|
|
271
|
-
if (interval > maxRetryAfter) {
|
|
272
|
-
throw maxRetryAfterError(interval, { cause: ex });
|
|
273
|
-
}
|
|
274
|
-
} else {
|
|
275
|
-
interval = new Function('interval', `return Math.ceil(${ retry.backoffStrategy });`)(interval);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
retry.attempts--;
|
|
279
|
-
retry.interval = interval;
|
|
280
|
-
|
|
281
|
-
return setTimeoutPromise(interval).then(() => overact(url, options));
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (digest && !redirected && ex.body) {
|
|
285
|
-
ex.body = await ex.body();
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (!thenable) {
|
|
289
|
-
throw ex;
|
|
290
|
-
} else {
|
|
291
|
-
return ex;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
export const transform = async (options) => {
|
|
297
|
-
let { body, headers } = options;
|
|
298
|
-
|
|
299
|
-
if (!body) {
|
|
300
|
-
return options;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (File.alike(body)) {
|
|
304
|
-
headers = {
|
|
305
|
-
[HTTP2_HEADER_CONTENT_LENGTH]: body.size,
|
|
306
|
-
[HTTP2_HEADER_CONTENT_TYPE]: body.type || APPLICATION_OCTET_STREAM,
|
|
307
|
-
};
|
|
308
|
-
body = body.stream();
|
|
309
|
-
} else if (FormData.alike(body)) {
|
|
310
|
-
body = FormData.actuate(body);
|
|
311
|
-
headers = { [HTTP2_HEADER_CONTENT_TYPE]: body.contentType };
|
|
312
|
-
} else if (!Buffer.isBuffer(body)) {
|
|
313
|
-
if (types.isAnyArrayBuffer(body)) {
|
|
314
|
-
body = Buffer.from(body);
|
|
315
|
-
} else if (types.isArrayBufferView(body)) {
|
|
316
|
-
body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
|
|
317
|
-
} else if (body === Object(body) && !Reflect.has(body, Symbol.asyncIterator)) {
|
|
318
|
-
if (body.constructor === URLSearchParams) {
|
|
319
|
-
headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_FORM_URLENCODED };
|
|
320
|
-
body = body.toString();
|
|
321
|
-
} else if (!(!Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
|
|
322
|
-
headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_JSON };
|
|
323
|
-
body = JSON.stringify(body);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const encodings = options.headers[HTTP2_HEADER_CONTENT_ENCODING];
|
|
329
|
-
|
|
330
|
-
if (body === Object(body)
|
|
331
|
-
&& (Reflect.has(body, Symbol.asyncIterator) || (!Array.isArray(body) && Reflect.has(body, Symbol.iterator)))) {
|
|
332
|
-
body = encodings ? compress(Readable.from(body), encodings) : Readable.from(body);
|
|
333
|
-
} else if (encodings) {
|
|
334
|
-
body = await buffer(compress(Readable.from(body), encodings));
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
Object.assign(options.headers, {
|
|
338
|
-
...headers,
|
|
339
|
-
...!body[Symbol.asyncIterator] && {
|
|
340
|
-
[HTTP2_HEADER_CONTENT_LENGTH]: Buffer.byteLength(body),
|
|
341
|
-
},
|
|
342
|
-
...options.headers[HTTP2_HEADER_CONTENT_TYPE] && {
|
|
343
|
-
[HTTP2_HEADER_CONTENT_TYPE]: options.headers[HTTP2_HEADER_CONTENT_TYPE],
|
|
344
|
-
},
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
return {
|
|
348
|
-
...options,
|
|
349
|
-
body,
|
|
350
|
-
};
|
|
351
|
-
};
|
|
183
|
+
export const toCamelCase = (str) => str?.toLowerCase().replace(
|
|
184
|
+
/\p{Punctuation}.|\p{White_Space}./gu,
|
|
185
|
+
(val) => val.replace(/\p{Punctuation}+|\p{White_Space}+/gu, '').toUpperCase(),
|
|
186
|
+
);
|
|
352
187
|
|
|
353
188
|
export const unwind = (encodings) => encodings.split(',').map((it) => it.trim());
|