rekwest 4.1.0 → 4.2.1

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/utils.js CHANGED
@@ -1,38 +1,37 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.sanitize = exports.sameOrigin = exports.preflight = exports.mixin = exports.merge = exports.dispatch = exports.decompress = exports.compress = exports.brandCheck = exports.affix = exports.admix = void 0;
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:http2"));
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
- HTTP2_HEADER_COOKIE,
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
- } = _nodeHttp.default.constants;
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/.test(charset)) {
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(/(?<!:)\/+/gi, '/');
243
+ url = `${url}`.replace(/(?<!:)\/+/g, '/');
298
244
  }
299
245
  url = new URL(url);
300
246
  return Object.assign(options, {
@@ -313,6 +259,94 @@ async function* tap(value) {
313
259
  yield await value.arrayBuffer();
314
260
  }
315
261
  }
262
+ const transfer = async options => {
263
+ const {
264
+ digest,
265
+ h2,
266
+ redirected,
267
+ thenable,
268
+ url
269
+ } = options;
270
+ if (options.follow === 0) {
271
+ throw new _errors.RequestError(`Maximum redirect reached at: ${url.href}`);
272
+ }
273
+ if (url.protocol === 'https:') {
274
+ options = !h2 ? await (0, _ackn.ackn)(options) : {
275
+ ...options,
276
+ createConnection: null,
277
+ protocol: url.protocol
278
+ };
279
+ } else if (Reflect.has(options, 'alpnProtocol')) {
280
+ ['alpnProtocol', 'createConnection', 'h2', 'protocol'].forEach(it => Reflect.deleteProperty(options, it));
281
+ }
282
+ try {
283
+ options = await transform((0, _preflight.preflight)(options));
284
+ } catch (ex) {
285
+ options.createConnection?.().destroy();
286
+ throw ex;
287
+ }
288
+ const promise = new Promise((resolve, reject) => {
289
+ let client, req;
290
+ if (options.h2) {
291
+ client = _nodeHttp2.default.connect(url.origin, options);
292
+ req = client.request(options.headers, options);
293
+ } else {
294
+ const {
295
+ request
296
+ } = url.protocol === 'http:' ? _nodeHttp.default : _nodeHttps.default;
297
+ req = request(url, options);
298
+ }
299
+ affix(client, req, options);
300
+ req.once('error', reject);
301
+ req.once('frameError', reject);
302
+ req.once('goaway', reject);
303
+ req.once('response', res => (0, _postflight.postflight)(req, res, options, {
304
+ reject,
305
+ resolve
306
+ }));
307
+ dispatch(options, req);
308
+ });
309
+ try {
310
+ const res = await promise;
311
+ if (digest && !redirected) {
312
+ res.body = await res.body();
313
+ }
314
+ return res;
315
+ } catch (ex) {
316
+ const {
317
+ maxRetryAfter,
318
+ retry
319
+ } = options;
320
+ if (retry?.attempts && retry?.statusCodes.includes(ex.statusCode)) {
321
+ let {
322
+ interval
323
+ } = retry;
324
+ if (retry.retryAfter && ex.headers[HTTP2_HEADER_RETRY_AFTER]) {
325
+ interval = ex.headers[HTTP2_HEADER_RETRY_AFTER];
326
+ interval = Number(interval) * 1000 || new Date(interval) - Date.now();
327
+ if (interval > maxRetryAfter) {
328
+ throw maxRetryAfterError(interval, {
329
+ cause: ex
330
+ });
331
+ }
332
+ } else {
333
+ interval = new Function('interval', `return Math.ceil(${retry.backoffStrategy});`)(interval);
334
+ }
335
+ retry.attempts--;
336
+ retry.interval = interval;
337
+ return (0, _promises.setTimeout)(interval).then(() => (0, _index.default)(url, options));
338
+ }
339
+ if (digest && !redirected && ex.body) {
340
+ ex.body = await ex.body();
341
+ }
342
+ if (!thenable) {
343
+ throw ex;
344
+ } else {
345
+ return ex;
346
+ }
347
+ }
348
+ };
349
+ exports.transfer = transfer;
316
350
  const transform = async options => {
317
351
  let {
318
352
  body,
@@ -371,4 +405,19 @@ const transform = async options => {
371
405
  body
372
406
  };
373
407
  };
374
- exports.transform = transform;
408
+ exports.transform = transform;
409
+ const unwind = encodings => encodings.split(',').map(it => it.trim());
410
+ exports.unwind = unwind;
411
+ const validation = (options = {}) => {
412
+ if (options.body && [HTTP2_METHOD_GET, HTTP2_METHOD_HEAD].includes(options.method)) {
413
+ throw new TypeError(`Request with ${HTTP2_METHOD_GET}/${HTTP2_METHOD_HEAD} method cannot have body.`);
414
+ }
415
+ if (!Object.values(_constants.requestCredentials).includes(options.credentials)) {
416
+ throw new TypeError(`Failed to read the 'credentials' property from 'options': The provided value '${options.credentials}' is not a valid enum value.`);
417
+ }
418
+ if (!Reflect.has(_constants.requestRedirect, options.redirect)) {
419
+ throw new TypeError(`Failed to read the 'redirect' property from 'options': The provided value '${options.redirect}' is not a valid enum value.`);
420
+ }
421
+ return options;
422
+ };
423
+ exports.validation = validation;
package/package.json CHANGED
@@ -67,5 +67,5 @@
67
67
  "test:bail": "mocha --bail",
68
68
  "test:cover": "c8 --include=src --reporter=lcov --reporter=text npm test"
69
69
  },
70
- "version": "4.1.0"
70
+ "version": "4.2.1"
71
71
  }
package/src/ackn.mjs CHANGED
@@ -23,7 +23,7 @@ export const ackn = (options) => new Promise((resolve, reject) => {
23
23
  createConnection() {
24
24
  return socket;
25
25
  },
26
- h2: /h2c?/.test(alpnProtocol),
26
+ h2: /h2c?/i.test(alpnProtocol),
27
27
  protocol: url.protocol,
28
28
  });
29
29
  });
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 redirectModes = {
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 redirectStatusCodes = [
23
+ export const requestRedirectCodes = [
18
24
  HTTP_STATUS_MOVED_PERMANENTLY,
19
25
  HTTP_STATUS_FOUND,
20
26
  HTTP_STATUS_SEE_OTHER,
@@ -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
+ };
package/src/index.mjs CHANGED
@@ -1,25 +1,17 @@
1
1
  import http from 'node:http';
2
2
  import http2 from 'node:http2';
3
3
  import https from 'node:https';
4
- import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
5
- import { ackn } from './ackn.mjs';
6
- import {
7
- redirectModes,
8
- redirectStatusCodes,
9
- } from './constants.mjs';
10
- import { Cookies } from './cookies.mjs';
11
- import { RequestError } from './errors.mjs';
4
+ import { requestRedirect } from './constants.mjs';
5
+ import defaults from './defaults.mjs';
12
6
  import { APPLICATION_OCTET_STREAM } from './mediatypes.mjs';
7
+ import { preflight } from './preflight.mjs';
13
8
  import {
14
9
  admix,
15
10
  affix,
16
- dispatch,
17
11
  merge,
18
- mixin,
19
- preflight,
20
- sameOrigin,
21
12
  sanitize,
22
- transform,
13
+ transfer,
14
+ validation,
23
15
  } from './utils.mjs';
24
16
 
25
17
  export { constants } from 'node:http2';
@@ -34,263 +26,43 @@ export * as mediatypes from './mediatypes.mjs';
34
26
  export * from './utils.mjs';
35
27
 
36
28
  const {
37
- HTTP2_HEADER_AUTHORIZATION,
38
29
  HTTP2_HEADER_CONTENT_TYPE,
39
- HTTP2_HEADER_LOCATION,
40
- HTTP2_HEADER_RETRY_AFTER,
41
- HTTP2_HEADER_SET_COOKIE,
42
- HTTP2_METHOD_GET,
43
- HTTP2_METHOD_HEAD,
44
- HTTP2_METHOD_POST,
45
- HTTP_STATUS_BAD_REQUEST,
46
- HTTP_STATUS_FOUND,
47
- HTTP_STATUS_MOVED_PERMANENTLY,
48
- HTTP_STATUS_SEE_OTHER,
49
- HTTP_STATUS_SERVICE_UNAVAILABLE,
50
- HTTP_STATUS_TOO_MANY_REQUESTS,
51
30
  } = http2.constants;
52
31
 
53
- const maxRetryAfter = Symbol('maxRetryAfter');
54
- const maxRetryAfterError = (
55
- interval,
56
- options,
57
- ) => new RequestError(`Maximum '${ HTTP2_HEADER_RETRY_AFTER }' limit exceeded: ${ interval } ms.`, options);
58
- let defaults = {
59
- follow: 20,
60
- get maxRetryAfter() {
61
- return this[maxRetryAfter] ?? this.timeout;
62
- },
63
- set maxRetryAfter(value) {
64
- this[maxRetryAfter] = value;
65
- },
66
- method: HTTP2_METHOD_GET,
67
- retry: {
68
- attempts: 0,
69
- backoffStrategy: 'interval * Math.log(Math.random() * (Math.E * Math.E - Math.E) + Math.E)',
70
- interval: 1e3,
71
- retryAfter: true,
72
- statusCodes: [
73
- HTTP_STATUS_TOO_MANY_REQUESTS,
74
- HTTP_STATUS_SERVICE_UNAVAILABLE,
75
- ],
76
- },
77
- timeout: 3e5,
78
- };
79
-
80
- export default async function rekwest(...args) {
32
+ export default function rekwest(...args) {
81
33
  let options = sanitize(...args);
82
- const { url } = options;
83
34
 
84
35
  if (!options.redirected) {
85
36
  options = merge(rekwest.defaults, options);
86
37
  }
87
38
 
88
- if (options.body && [
89
- HTTP2_METHOD_GET,
90
- HTTP2_METHOD_HEAD,
91
- ].includes(options.method)) {
92
- throw new TypeError(`Request with ${ HTTP2_METHOD_GET }/${ HTTP2_METHOD_HEAD } method cannot have body.`);
93
- }
94
-
95
- if (options.follow === 0) {
96
- throw new RequestError(`Maximum redirect reached at: ${ url.href }`);
97
- }
98
-
99
- if (url.protocol === 'https:') {
100
- options = await ackn(options);
101
- } else if (Reflect.has(options, 'alpnProtocol')) {
102
- [
103
- 'alpnProtocol',
104
- 'createConnection',
105
- 'h2',
106
- 'protocol',
107
- ].forEach((it) => Reflect.deleteProperty(options, it));
108
- }
109
-
110
- options = await transform(preflight(options));
111
-
112
- const { cookies, digest, follow, h2, redirect, redirected, thenable } = options;
113
- const { request } = (url.protocol === 'http:' ? http : https);
114
-
115
- const promise = new Promise((resolve, reject) => {
116
- let client, req;
117
-
118
- if (h2) {
119
- client = http2.connect(url.origin, options);
120
- req = client.request(options.headers, options);
121
- } else {
122
- req = request(url, options);
123
- }
124
-
125
- affix(client, req, options);
126
- req.once('error', reject);
127
- req.once('frameError', reject);
128
- req.once('goaway', reject);
129
- req.once('response', (res) => {
130
- let headers;
131
-
132
- if (h2) {
133
- headers = res;
134
- res = req;
135
- } else {
136
- res.once('error', reject);
137
- }
138
-
139
- admix(res, headers, options);
140
-
141
- if (cookies !== false && res.headers[HTTP2_HEADER_SET_COOKIE]) {
142
- if (Cookies.jar.has(url.origin)) {
143
- new Cookies(res.headers[HTTP2_HEADER_SET_COOKIE]).forEach(function (val, key) {
144
- this.set(key, val);
145
- }, Cookies.jar.get(url.origin));
146
- } else {
147
- Cookies.jar.set(url.origin, new Cookies(res.headers[HTTP2_HEADER_SET_COOKIE]));
148
- }
149
- }
150
-
151
- Reflect.defineProperty(res, 'cookies', {
152
- enumerable: true,
153
- value: cookies !== false && Cookies.jar.has(url.origin)
154
- ? Cookies.jar.get(url.origin)
155
- : void 0,
156
- });
157
-
158
- const { statusCode } = res;
159
-
160
- if (follow && /3\d{2}/.test(statusCode) && res.headers[HTTP2_HEADER_LOCATION]) {
161
- if (!redirectStatusCodes.includes(statusCode)) {
162
- return res.emit('error', new RangeError(`Invalid status code: ${ statusCode }`));
163
- }
164
-
165
- if (redirect === redirectModes.error) {
166
- return res.emit('error', new RequestError(`Unexpected redirect, redirect mode is set to '${ redirect }'.`));
167
- }
168
-
169
- if (redirect === redirectModes.follow) {
170
- const location = new URL(res.headers[HTTP2_HEADER_LOCATION], url);
171
-
172
- if (!/^https?:/.test(location.protocol)) {
173
- return res.emit('error', new RequestError('URL scheme must be "http" or "https".'));
174
- }
175
-
176
- if (!sameOrigin(location, url)) {
177
- Reflect.deleteProperty(options.headers, HTTP2_HEADER_AUTHORIZATION);
178
- location.password = location.username = '';
179
- }
180
-
181
- options.url = location;
182
-
183
- if (statusCode !== HTTP_STATUS_SEE_OTHER && options?.body?.pipe?.constructor === Function) {
184
- return res.emit('error', new RequestError(`Unable to ${ redirect } redirect with streamable body.`));
185
- }
186
-
187
- options.follow--;
188
-
189
- if (([
190
- HTTP_STATUS_MOVED_PERMANENTLY,
191
- HTTP_STATUS_FOUND,
192
- ].includes(statusCode) && request.method === HTTP2_METHOD_POST) || (statusCode === HTTP_STATUS_SEE_OTHER && ![
193
- HTTP2_METHOD_GET,
194
- HTTP2_METHOD_HEAD,
195
- ].includes(options.method))) {
196
- Object.keys(options.headers).filter((it) => /^content-/i.test(it))
197
- .forEach((it) => Reflect.deleteProperty(options.headers, it));
198
- options.body = null;
199
- options.method = HTTP2_METHOD_GET;
200
- }
201
-
202
- Reflect.set(options, 'redirected', true);
203
-
204
- if (statusCode === HTTP_STATUS_MOVED_PERMANENTLY && res.headers[HTTP2_HEADER_RETRY_AFTER]) {
205
- let interval = res.headers[HTTP2_HEADER_RETRY_AFTER];
206
-
207
- interval = Number(interval) * 1000 || new Date(interval) - Date.now();
208
-
209
- if (interval > options.maxRetryAfter) {
210
- return res.emit('error', maxRetryAfterError(interval, { cause: mixin(res, options) }));
211
- }
212
-
213
- return setTimeoutPromise(interval).then(() => rekwest(options.url, options).then(resolve, reject));
214
- }
215
-
216
- return rekwest(options.url, options).then(resolve, reject);
217
- }
218
- }
219
-
220
- if (statusCode >= HTTP_STATUS_BAD_REQUEST) {
221
- return reject(mixin(res, options));
222
- }
223
-
224
- resolve(mixin(res, options));
225
- });
226
-
227
- dispatch(options, req);
228
- });
229
-
230
- try {
231
- const res = await promise;
232
-
233
- if (digest && !redirected) {
234
- res.body = await res.body();
235
- }
236
-
237
- return res;
238
- } catch (ex) {
239
- const { maxRetryAfter, retry } = options;
240
-
241
- if (retry?.attempts && retry?.statusCodes.includes(ex.statusCode)) {
242
- let { interval } = retry;
243
-
244
- if (retry.retryAfter && ex.headers[HTTP2_HEADER_RETRY_AFTER]) {
245
- interval = ex.headers[HTTP2_HEADER_RETRY_AFTER];
246
- interval = Number(interval) * 1000 || new Date(interval) - Date.now();
247
- if (interval > maxRetryAfter) {
248
- throw maxRetryAfterError(interval, { cause: ex });
249
- }
250
- } else {
251
- interval = new Function('interval', `return Math.ceil(${ retry.backoffStrategy });`)(interval);
252
- }
253
-
254
- retry.attempts--;
255
- retry.interval = interval;
256
-
257
- return setTimeoutPromise(interval).then(() => rekwest(url, options));
258
- }
259
-
260
- if (digest && !redirected && ex.body) {
261
- ex.body = await ex.body();
262
- }
263
-
264
- if (!thenable) {
265
- throw ex;
266
- } else {
267
- return ex;
268
- }
269
- }
39
+ return transfer(validation(options));
270
40
  }
271
41
 
272
42
  Reflect.defineProperty(rekwest, 'stream', {
273
43
  enumerable: true,
274
44
  value(...args) {
275
45
  const options = preflight({
276
- ...merge(rekwest.defaults, {
46
+ ...validation(merge(rekwest.defaults, {
277
47
  headers: { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_OCTET_STREAM },
278
- }, sanitize(...args)),
279
- redirect: redirectModes.manual,
48
+ }, sanitize(...args))),
49
+ redirect: requestRedirect.manual,
280
50
  });
281
51
 
282
52
  const { h2, url } = options;
283
- const { request } = (url.protocol === 'http:' ? http : https);
284
53
  let client, req;
285
54
 
286
55
  if (h2) {
287
56
  client = http2.connect(url.origin, options);
288
57
  req = client.request(options.headers, options);
289
58
  } else {
59
+ const { request } = (url.protocol === 'http:' ? http : https);
60
+
290
61
  req = request(url, options);
291
62
  }
292
63
 
293
64
  affix(client, req, options);
65
+
294
66
  req.once('response', (res) => {
295
67
  let headers;
296
68
 
@@ -308,6 +80,6 @@ Reflect.defineProperty(rekwest, 'stream', {
308
80
 
309
81
  Reflect.defineProperty(rekwest, 'defaults', {
310
82
  enumerable: true,
311
- get() { return defaults; },
312
- set(value) { defaults = merge(defaults, value); },
83
+ get() { return defaults.stash; },
84
+ set(value) { defaults.stash = merge(defaults.stash, value); },
313
85
  });