rekwest 4.1.0 → 4.2.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 +188 -187
- package/dist/ackn.js +1 -1
- package/dist/constants.js +11 -5
- package/dist/defaults.js +41 -0
- package/dist/index.js +27 -208
- package/dist/postflight.js +110 -0
- package/dist/preflight.js +66 -0
- package/dist/utils.js +120 -73
- package/package.json +1 -1
- package/src/ackn.mjs +1 -1
- package/src/constants.mjs +8 -2
- package/src/defaults.mjs +44 -0
- package/src/index.mjs +84 -313
- package/src/postflight.mjs +135 -0
- package/src/preflight.mjs +70 -0
- package/src/utils.mjs +143 -79
package/dist/utils.js
CHANGED
|
@@ -1,38 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
|
-
exports.sanitize = exports.sameOrigin = exports.
|
|
4
|
+
exports.sanitize = exports.sameOrigin = exports.mixin = exports.merge = exports.maxRetryAfterError = exports.maxRetryAfter = exports.dispatch = exports.decompress = exports.compress = exports.brandCheck = exports.affix = exports.admix = void 0;
|
|
5
5
|
exports.tap = tap;
|
|
6
|
-
exports.transform = void 0;
|
|
6
|
+
exports.validation = exports.unwind = exports.transform = exports.transfer = void 0;
|
|
7
7
|
var _nodeBuffer = require("node:buffer");
|
|
8
|
-
var _nodeHttp = _interopRequireDefault(require("node:
|
|
8
|
+
var _nodeHttp = _interopRequireDefault(require("node:http"));
|
|
9
|
+
var _nodeHttp2 = _interopRequireDefault(require("node:http2"));
|
|
10
|
+
var _nodeHttps = _interopRequireDefault(require("node:https"));
|
|
9
11
|
var _nodeStream = require("node:stream");
|
|
10
12
|
var _consumers = require("node:stream/consumers");
|
|
13
|
+
var _promises = require("node:timers/promises");
|
|
11
14
|
var _nodeUtil = require("node:util");
|
|
12
15
|
var _nodeZlib = _interopRequireDefault(require("node:zlib"));
|
|
16
|
+
var _ackn = require("./ackn");
|
|
13
17
|
var _constants = require("./constants");
|
|
14
|
-
var _cookies = require("./cookies");
|
|
15
18
|
var _errors = require("./errors");
|
|
16
19
|
var _file = require("./file");
|
|
17
20
|
var _formdata = require("./formdata");
|
|
21
|
+
var _index = _interopRequireDefault(require("./index"));
|
|
18
22
|
var _mediatypes = require("./mediatypes");
|
|
23
|
+
var _postflight = require("./postflight");
|
|
24
|
+
var _preflight = require("./preflight");
|
|
19
25
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
20
26
|
const {
|
|
21
|
-
HTTP2_HEADER_ACCEPT,
|
|
22
|
-
HTTP2_HEADER_ACCEPT_ENCODING,
|
|
23
|
-
HTTP2_HEADER_AUTHORITY,
|
|
24
27
|
HTTP2_HEADER_CONTENT_ENCODING,
|
|
25
28
|
HTTP2_HEADER_CONTENT_LENGTH,
|
|
26
29
|
HTTP2_HEADER_CONTENT_TYPE,
|
|
27
|
-
|
|
28
|
-
HTTP2_HEADER_METHOD,
|
|
29
|
-
HTTP2_HEADER_PATH,
|
|
30
|
-
HTTP2_HEADER_SCHEME,
|
|
30
|
+
HTTP2_HEADER_RETRY_AFTER,
|
|
31
31
|
HTTP2_HEADER_STATUS,
|
|
32
32
|
HTTP2_METHOD_GET,
|
|
33
33
|
HTTP2_METHOD_HEAD
|
|
34
|
-
} =
|
|
35
|
-
const unwind = encodings => encodings.split(',').map(it => it.trim());
|
|
34
|
+
} = _nodeHttp2.default.constants;
|
|
36
35
|
const admix = (res, headers, options) => {
|
|
37
36
|
const {
|
|
38
37
|
h2
|
|
@@ -126,6 +125,10 @@ const dispatch = ({
|
|
|
126
125
|
}
|
|
127
126
|
};
|
|
128
127
|
exports.dispatch = dispatch;
|
|
128
|
+
const maxRetryAfter = Symbol('maxRetryAfter');
|
|
129
|
+
exports.maxRetryAfter = maxRetryAfter;
|
|
130
|
+
const maxRetryAfterError = (interval, options) => new _errors.RequestError(`Maximum '${HTTP2_HEADER_RETRY_AFTER}' limit exceeded: ${interval} ms.`, options);
|
|
131
|
+
exports.maxRetryAfterError = maxRetryAfterError;
|
|
129
132
|
const merge = (target = {}, ...rest) => {
|
|
130
133
|
target = JSON.parse(JSON.stringify(target));
|
|
131
134
|
if (!rest.length) {
|
|
@@ -215,7 +218,7 @@ const mixin = (res, {
|
|
|
215
218
|
if (/\bjson\b/i.test(contentType)) {
|
|
216
219
|
body = JSON.parse(body.toString(charset));
|
|
217
220
|
} else if (/\b(?:text|xml)\b/i.test(contentType)) {
|
|
218
|
-
if (/\b(?:latin1|ucs-2|utf-(?:8|16le))\b
|
|
221
|
+
if (/\b(?:latin1|ucs-2|utf-(?:8|16le))\b/i.test(charset)) {
|
|
219
222
|
body = body.toString(charset);
|
|
220
223
|
} else {
|
|
221
224
|
body = new TextDecoder(charset).decode(body);
|
|
@@ -235,66 +238,9 @@ const mixin = (res, {
|
|
|
235
238
|
});
|
|
236
239
|
};
|
|
237
240
|
exports.mixin = mixin;
|
|
238
|
-
const preflight = options => {
|
|
239
|
-
const {
|
|
240
|
-
cookies,
|
|
241
|
-
h2 = false,
|
|
242
|
-
headers,
|
|
243
|
-
method = HTTP2_METHOD_GET,
|
|
244
|
-
redirected,
|
|
245
|
-
url
|
|
246
|
-
} = options;
|
|
247
|
-
if (h2) {
|
|
248
|
-
options.endStream = [HTTP2_METHOD_GET, HTTP2_METHOD_HEAD].includes(method);
|
|
249
|
-
}
|
|
250
|
-
if (cookies !== false) {
|
|
251
|
-
let cookie = _cookies.Cookies.jar.get(url.origin);
|
|
252
|
-
if (cookies === Object(cookies) && !redirected) {
|
|
253
|
-
if (cookie) {
|
|
254
|
-
new _cookies.Cookies(cookies).forEach(function (val, key) {
|
|
255
|
-
this.set(key, val);
|
|
256
|
-
}, cookie);
|
|
257
|
-
} else {
|
|
258
|
-
cookie = new _cookies.Cookies(cookies);
|
|
259
|
-
_cookies.Cookies.jar.set(url.origin, cookie);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
options.headers = {
|
|
263
|
-
...(cookie && {
|
|
264
|
-
[HTTP2_HEADER_COOKIE]: cookie
|
|
265
|
-
}),
|
|
266
|
-
...headers
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
options.digest ??= true;
|
|
270
|
-
options.follow ??= 20;
|
|
271
|
-
options.h2 ??= h2;
|
|
272
|
-
options.headers = {
|
|
273
|
-
[HTTP2_HEADER_ACCEPT]: `${_mediatypes.APPLICATION_JSON}, ${_mediatypes.TEXT_PLAIN}, ${_mediatypes.WILDCARD}`,
|
|
274
|
-
[HTTP2_HEADER_ACCEPT_ENCODING]: 'br, deflate, deflate-raw, gzip, identity',
|
|
275
|
-
...Object.entries(options.headers ?? {}).reduce((acc, [key, val]) => (acc[key.toLowerCase()] = val, acc), {}),
|
|
276
|
-
...(h2 && {
|
|
277
|
-
[HTTP2_HEADER_AUTHORITY]: url.host,
|
|
278
|
-
[HTTP2_HEADER_METHOD]: method,
|
|
279
|
-
[HTTP2_HEADER_PATH]: `${url.pathname}${url.search}`,
|
|
280
|
-
[HTTP2_HEADER_SCHEME]: url.protocol.replace(/\p{Punctuation}/gu, '')
|
|
281
|
-
})
|
|
282
|
-
};
|
|
283
|
-
options.method ??= method;
|
|
284
|
-
options.parse ??= true;
|
|
285
|
-
options.redirect ??= _constants.redirectModes.follow;
|
|
286
|
-
if (!Reflect.has(_constants.redirectModes, options.redirect)) {
|
|
287
|
-
options.createConnection?.().destroy();
|
|
288
|
-
throw new TypeError(`Failed to read the 'redirect' property from 'options': The provided value '${options.redirect}' is not a valid enum value.`);
|
|
289
|
-
}
|
|
290
|
-
options.redirected ??= false;
|
|
291
|
-
options.thenable ??= false;
|
|
292
|
-
return options;
|
|
293
|
-
};
|
|
294
|
-
exports.preflight = preflight;
|
|
295
241
|
const sanitize = (url, options = {}) => {
|
|
296
242
|
if (options.trimTrailingSlashes) {
|
|
297
|
-
url = `${url}`.replace(/(?<!:)\/+/
|
|
243
|
+
url = `${url}`.replace(/(?<!:)\/+/g, '/');
|
|
298
244
|
}
|
|
299
245
|
url = new URL(url);
|
|
300
246
|
return Object.assign(options, {
|
|
@@ -313,6 +259,92 @@ async function* tap(value) {
|
|
|
313
259
|
yield await value.arrayBuffer();
|
|
314
260
|
}
|
|
315
261
|
}
|
|
262
|
+
const transfer = async options => {
|
|
263
|
+
const {
|
|
264
|
+
url
|
|
265
|
+
} = options;
|
|
266
|
+
if (options.follow === 0) {
|
|
267
|
+
throw new _errors.RequestError(`Maximum redirect reached at: ${url.href}`);
|
|
268
|
+
}
|
|
269
|
+
if (url.protocol === 'https:') {
|
|
270
|
+
options = await (0, _ackn.ackn)(options);
|
|
271
|
+
} else if (Reflect.has(options, 'alpnProtocol')) {
|
|
272
|
+
['alpnProtocol', 'createConnection', 'h2', 'protocol'].forEach(it => Reflect.deleteProperty(options, it));
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
options = await transform((0, _preflight.preflight)(options));
|
|
276
|
+
} catch (ex) {
|
|
277
|
+
options.createConnection?.().destroy();
|
|
278
|
+
throw ex;
|
|
279
|
+
}
|
|
280
|
+
const {
|
|
281
|
+
digest,
|
|
282
|
+
h2,
|
|
283
|
+
redirected,
|
|
284
|
+
thenable
|
|
285
|
+
} = options;
|
|
286
|
+
const {
|
|
287
|
+
request
|
|
288
|
+
} = url.protocol === 'http:' ? _nodeHttp.default : _nodeHttps.default;
|
|
289
|
+
const promise = new Promise((resolve, reject) => {
|
|
290
|
+
let client, req;
|
|
291
|
+
if (h2) {
|
|
292
|
+
client = _nodeHttp2.default.connect(url.origin, options);
|
|
293
|
+
req = client.request(options.headers, options);
|
|
294
|
+
} else {
|
|
295
|
+
req = request(url, options);
|
|
296
|
+
}
|
|
297
|
+
affix(client, req, options);
|
|
298
|
+
req.once('error', reject);
|
|
299
|
+
req.once('frameError', reject);
|
|
300
|
+
req.once('goaway', reject);
|
|
301
|
+
req.once('response', res => (0, _postflight.postflight)(req, res, options, {
|
|
302
|
+
reject,
|
|
303
|
+
resolve
|
|
304
|
+
}));
|
|
305
|
+
dispatch(options, req);
|
|
306
|
+
});
|
|
307
|
+
try {
|
|
308
|
+
const res = await promise;
|
|
309
|
+
if (digest && !redirected) {
|
|
310
|
+
res.body = await res.body();
|
|
311
|
+
}
|
|
312
|
+
return res;
|
|
313
|
+
} catch (ex) {
|
|
314
|
+
const {
|
|
315
|
+
maxRetryAfter,
|
|
316
|
+
retry
|
|
317
|
+
} = options;
|
|
318
|
+
if (retry?.attempts && retry?.statusCodes.includes(ex.statusCode)) {
|
|
319
|
+
let {
|
|
320
|
+
interval
|
|
321
|
+
} = retry;
|
|
322
|
+
if (retry.retryAfter && ex.headers[HTTP2_HEADER_RETRY_AFTER]) {
|
|
323
|
+
interval = ex.headers[HTTP2_HEADER_RETRY_AFTER];
|
|
324
|
+
interval = Number(interval) * 1000 || new Date(interval) - Date.now();
|
|
325
|
+
if (interval > maxRetryAfter) {
|
|
326
|
+
throw maxRetryAfterError(interval, {
|
|
327
|
+
cause: ex
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
interval = new Function('interval', `return Math.ceil(${retry.backoffStrategy});`)(interval);
|
|
332
|
+
}
|
|
333
|
+
retry.attempts--;
|
|
334
|
+
retry.interval = interval;
|
|
335
|
+
return (0, _promises.setTimeout)(interval).then(() => (0, _index.default)(url, options));
|
|
336
|
+
}
|
|
337
|
+
if (digest && !redirected && ex.body) {
|
|
338
|
+
ex.body = await ex.body();
|
|
339
|
+
}
|
|
340
|
+
if (!thenable) {
|
|
341
|
+
throw ex;
|
|
342
|
+
} else {
|
|
343
|
+
return ex;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
exports.transfer = transfer;
|
|
316
348
|
const transform = async options => {
|
|
317
349
|
let {
|
|
318
350
|
body,
|
|
@@ -371,4 +403,19 @@ const transform = async options => {
|
|
|
371
403
|
body
|
|
372
404
|
};
|
|
373
405
|
};
|
|
374
|
-
exports.transform = transform;
|
|
406
|
+
exports.transform = transform;
|
|
407
|
+
const unwind = encodings => encodings.split(',').map(it => it.trim());
|
|
408
|
+
exports.unwind = unwind;
|
|
409
|
+
const validation = (options = {}) => {
|
|
410
|
+
if (options.body && [HTTP2_METHOD_GET, HTTP2_METHOD_HEAD].includes(options.method)) {
|
|
411
|
+
throw new TypeError(`Request with ${HTTP2_METHOD_GET}/${HTTP2_METHOD_HEAD} method cannot have body.`);
|
|
412
|
+
}
|
|
413
|
+
if (!Object.values(_constants.requestCredentials).includes(options.credentials)) {
|
|
414
|
+
throw new TypeError(`Failed to read the 'credentials' property from 'options': The provided value '${options.credentials}' is not a valid enum value.`);
|
|
415
|
+
}
|
|
416
|
+
if (!Reflect.has(_constants.requestRedirect, options.redirect)) {
|
|
417
|
+
throw new TypeError(`Failed to read the 'redirect' property from 'options': The provided value '${options.redirect}' is not a valid enum value.`);
|
|
418
|
+
}
|
|
419
|
+
return options;
|
|
420
|
+
};
|
|
421
|
+
exports.validation = validation;
|
package/package.json
CHANGED
package/src/ackn.mjs
CHANGED
package/src/constants.mjs
CHANGED
|
@@ -8,13 +8,19 @@ const {
|
|
|
8
8
|
HTTP_STATUS_TEMPORARY_REDIRECT,
|
|
9
9
|
} = http2.constants;
|
|
10
10
|
|
|
11
|
-
export const
|
|
11
|
+
export const requestCredentials = {
|
|
12
|
+
include: 'include',
|
|
13
|
+
omit: 'omit',
|
|
14
|
+
sameOrigin: 'same-origin',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const requestRedirect = {
|
|
12
18
|
error: 'error',
|
|
13
19
|
follow: 'follow',
|
|
14
20
|
manual: 'manual',
|
|
15
21
|
};
|
|
16
22
|
|
|
17
|
-
export const
|
|
23
|
+
export const requestRedirectCodes = [
|
|
18
24
|
HTTP_STATUS_MOVED_PERMANENTLY,
|
|
19
25
|
HTTP_STATUS_FOUND,
|
|
20
26
|
HTTP_STATUS_SEE_OTHER,
|
package/src/defaults.mjs
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import http2 from 'node:http2';
|
|
2
|
+
import {
|
|
3
|
+
requestCredentials,
|
|
4
|
+
requestRedirect,
|
|
5
|
+
} from './constants.mjs';
|
|
6
|
+
import { maxRetryAfter } from './utils.mjs';
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
HTTP2_METHOD_GET,
|
|
10
|
+
HTTP_STATUS_SERVICE_UNAVAILABLE,
|
|
11
|
+
HTTP_STATUS_TOO_MANY_REQUESTS,
|
|
12
|
+
} = http2.constants;
|
|
13
|
+
|
|
14
|
+
const stash = {
|
|
15
|
+
credentials: requestCredentials.sameOrigin,
|
|
16
|
+
digest: true,
|
|
17
|
+
follow: 20,
|
|
18
|
+
get maxRetryAfter() {
|
|
19
|
+
return this[maxRetryAfter] ?? this.timeout;
|
|
20
|
+
},
|
|
21
|
+
set maxRetryAfter(value) {
|
|
22
|
+
this[maxRetryAfter] = value;
|
|
23
|
+
},
|
|
24
|
+
method: HTTP2_METHOD_GET,
|
|
25
|
+
parse: true,
|
|
26
|
+
redirect: requestRedirect.follow,
|
|
27
|
+
redirected: false,
|
|
28
|
+
retry: {
|
|
29
|
+
attempts: 0,
|
|
30
|
+
backoffStrategy: 'interval * Math.log(Math.random() * (Math.E * Math.E - Math.E) + Math.E)',
|
|
31
|
+
interval: 1e3,
|
|
32
|
+
retryAfter: true,
|
|
33
|
+
statusCodes: [
|
|
34
|
+
HTTP_STATUS_TOO_MANY_REQUESTS,
|
|
35
|
+
HTTP_STATUS_SERVICE_UNAVAILABLE,
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
thenable: false,
|
|
39
|
+
timeout: 3e5,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default {
|
|
43
|
+
stash,
|
|
44
|
+
};
|