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.
@@ -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 https from 'node:https';
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 transfer = async (options, overact) => {
201
- const { digest, redirected, thenable, url } = options;
202
-
203
- if (options.follow === 0) {
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());