rekwest 3.3.4 → 4.0.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/src/utils.mjs CHANGED
@@ -1,13 +1,11 @@
1
1
  import { Blob } from 'node:buffer';
2
2
  import http2 from 'node:http2';
3
3
  import {
4
- PassThrough,
4
+ pipeline,
5
5
  Readable,
6
6
  } from 'node:stream';
7
- import {
8
- promisify,
9
- types,
10
- } from 'node:util';
7
+ import { buffer } from 'node:stream/consumers';
8
+ import { types } from 'node:util';
11
9
  import zlib from 'node:zlib';
12
10
  import { Cookies } from './cookies.mjs';
13
11
  import { TimeoutError } from './errors.mjs';
@@ -37,12 +35,7 @@ const {
37
35
  HTTP2_METHOD_HEAD,
38
36
  } = http2.constants;
39
37
 
40
- const brotliCompress = promisify(zlib.brotliCompress);
41
- const brotliDecompress = promisify(zlib.brotliDecompress);
42
- const gzip = promisify(zlib.gzip);
43
- const gunzip = promisify(zlib.gunzip);
44
- const deflate = promisify(zlib.deflate);
45
- const inflate = promisify(zlib.inflate);
38
+ const unwind = (encodings) => encodings.split(',').map((it) => it.trim());
46
39
 
47
40
  export const admix = (res, headers, options) => {
48
41
  const { h2 } = options;
@@ -92,42 +85,53 @@ export const collate = (entity, primordial) => {
92
85
  }
93
86
  };
94
87
 
95
- export const compress = (buf, encoding, { async = false } = {}) => {
96
- encoding &&= encoding.match(/(?<encoding>\bbr\b|\bdeflate\b|\bgzip\b)/i)?.groups.encoding.toLowerCase();
97
- const compressor = {
98
- br: async ? brotliCompress : zlib.brotliCompressSync,
99
- deflate: async ? deflate : zlib.deflateSync,
100
- gzip: async ? gzip : zlib.gzipSync,
101
- }[encoding];
102
-
103
- return compressor?.(buf) ?? (async ? Promise.resolve(buf) : buf);
104
- };
105
-
106
- export const decompress = (buf, encoding, { async = false } = {}) => {
107
- encoding &&= encoding.match(/(?<encoding>\bbr\b|\bdeflate\b|\bgzip\b)/i)?.groups.encoding.toLowerCase();
108
- const decompressor = {
109
- br: async ? brotliDecompress : zlib.brotliDecompressSync,
110
- deflate: async ? inflate : zlib.inflateSync,
111
- gzip: async ? gunzip : zlib.gunzipSync,
112
- }[encoding];
88
+ export const compress = (readable, encodings = '') => {
89
+ const encoders = [];
90
+
91
+ encodings = unwind(encodings);
92
+
93
+ for (const encoding of encodings) {
94
+ if (/\bbr\b/i.test(encoding)) {
95
+ encoders.push(zlib.createBrotliCompress());
96
+ } else if (/\bdeflate(?!-(?:\w+)?)\b/i.test(encoding)) {
97
+ encoders.push(zlib.createDeflate());
98
+ } else if (/\bdeflate-raw\b/i.test(encoding)) {
99
+ encoders.push(zlib.createDeflateRaw());
100
+ } else if (/\bgzip\b/i.test(encoding)) {
101
+ encoders.push(zlib.createGzip());
102
+ } else {
103
+ return readable;
104
+ }
105
+ }
113
106
 
114
- return decompressor?.(buf) ?? (async ? Promise.resolve(buf) : buf);
107
+ return pipeline(readable, ...encoders, () => void 0);
115
108
  };
116
109
 
117
- export const dispatch = (req, { body, headers }) => {
118
- if (body === Object(body) && !Buffer.isBuffer(body)) {
119
- if (body.pipe?.constructor !== Function
120
- && (Reflect.has(body, Symbol.asyncIterator) || Reflect.has(body, Symbol.iterator))) {
121
- body = Readable.from(body);
110
+ export const decompress = (readable, encodings = '') => {
111
+ const decoders = [];
112
+
113
+ encodings = unwind(encodings);
114
+
115
+ for (const encoding of encodings) {
116
+ if (/\bbr\b/i.test(encoding)) {
117
+ decoders.push(zlib.createBrotliDecompress());
118
+ } else if (/\bdeflate(?!-(?:\w+)?)\b/i.test(encoding)) {
119
+ decoders.push(zlib.createInflate());
120
+ } else if (/\bdeflate-raw\b/i.test(encoding)) {
121
+ decoders.push(zlib.createInflateRaw());
122
+ } else if (/\bgzip\b/i.test(encoding)) {
123
+ decoders.push(zlib.createGunzip());
124
+ } else {
125
+ return readable;
122
126
  }
127
+ }
123
128
 
124
- const compressor = {
125
- br: zlib.createBrotliCompress,
126
- deflate: zlib.createDeflate,
127
- gzip: zlib.createGzip,
128
- }[headers[HTTP2_HEADER_CONTENT_ENCODING]] ?? PassThrough;
129
+ return pipeline(readable, ...decoders, () => void 0);
130
+ };
129
131
 
130
- body.pipe(compressor()).pipe(req);
132
+ export const dispatch = ({ body }, req) => {
133
+ if (body?.pipe?.constructor === Function) {
134
+ body.pipe(req);
131
135
  } else {
132
136
  req.end(body);
133
137
  }
@@ -217,19 +221,19 @@ export const mixin = (res, { digest = false, parse = false } = {}) => {
217
221
  throw new TypeError('Response stream already read');
218
222
  }
219
223
 
220
- let spool = [];
224
+ let body = [];
221
225
 
222
- for await (const chunk of this) {
223
- spool.push(chunk);
226
+ for await (const chunk of decompress(this, this.headers[HTTP2_HEADER_CONTENT_ENCODING])) {
227
+ body.push(chunk);
224
228
  }
225
229
 
226
- spool = Buffer.concat(spool);
230
+ body = Buffer.concat(body);
227
231
 
228
- if (spool.length) {
229
- spool = await decompress(spool, this.headers[HTTP2_HEADER_CONTENT_ENCODING], { async: true });
232
+ if (!body.length && parse) {
233
+ return null;
230
234
  }
231
235
 
232
- if (spool.length && parse) {
236
+ if (body.length && parse) {
233
237
  const contentType = this.headers[HTTP2_HEADER_CONTENT_TYPE] ?? '';
234
238
  const charset = contentType.split(';')
235
239
  .find((it) => /charset=/i.test(it))
@@ -239,17 +243,17 @@ export const mixin = (res, { digest = false, parse = false } = {}) => {
239
243
  .trim() || 'utf-8';
240
244
 
241
245
  if (/\bjson\b/i.test(contentType)) {
242
- spool = JSON.parse(spool.toString(charset));
243
- } else if (/\b(text|xml)\b/i.test(contentType)) {
244
- if (/\b(latin1|ucs-2|utf-(8|16le))\b/.test(charset)) {
245
- spool = spool.toString(charset);
246
+ body = JSON.parse(body.toString(charset));
247
+ } else if (/\b(?:text|xml)\b/i.test(contentType)) {
248
+ if (/\b(?:latin1|ucs-2|utf-(?:8|16le))\b/.test(charset)) {
249
+ body = body.toString(charset);
246
250
  } else {
247
- spool = new TextDecoder(charset).decode(spool);
251
+ body = new TextDecoder(charset).decode(body);
248
252
  }
249
253
  }
250
254
  }
251
255
 
252
- return spool;
256
+ return body;
253
257
  },
254
258
  writable: true,
255
259
  },
@@ -297,7 +301,7 @@ export const preflight = (options) => {
297
301
  options.h2 ??= h2;
298
302
  options.headers = {
299
303
  [HTTP2_HEADER_ACCEPT]: `${ APPLICATION_JSON }, ${ TEXT_PLAIN }, ${ WILDCARD }`,
300
- [HTTP2_HEADER_ACCEPT_ENCODING]: 'br, deflate, gzip, identity',
304
+ [HTTP2_HEADER_ACCEPT_ENCODING]: 'br, deflate, deflate-raw, gzip, identity',
301
305
  ...Object.entries(options.headers ?? {})
302
306
  .reduce((acc, [key, val]) => (acc[key.toLowerCase()] = val, acc), {}),
303
307
  ...h2 && {
@@ -331,7 +335,7 @@ export const redirects = {
331
335
  manual: 'manual',
332
336
  };
333
337
 
334
- export const revise = ({ url, options }) => {
338
+ export const sanitize = (url, options = {}) => {
335
339
  if (options.trimTrailingSlashes) {
336
340
  url = `${ url }`.replace(/(?<!:)\/+/gi, '/');
337
341
  }
@@ -351,13 +355,11 @@ export async function* tap(value) {
351
355
  }
352
356
  }
353
357
 
354
- export const transform = (body, options) => {
355
- let headers = {};
358
+ export const transform = async (options) => {
359
+ let { body, headers } = options;
356
360
 
357
- if (types.isAnyArrayBuffer(body) && !Buffer.isBuffer(body)) {
358
- body = Buffer.from(body);
359
- } else if (types.isArrayBufferView(body) && !Buffer.isBuffer(body)) {
360
- body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
361
+ if (!body) {
362
+ return options;
361
363
  }
362
364
 
363
365
  if (File.alike(body)) {
@@ -365,30 +367,37 @@ export const transform = (body, options) => {
365
367
  [HTTP2_HEADER_CONTENT_LENGTH]: body.size,
366
368
  [HTTP2_HEADER_CONTENT_TYPE]: body.type || APPLICATION_OCTET_STREAM,
367
369
  };
368
- body = body.stream?.() ?? Readable.from(tap(body));
370
+ body = body.stream();
369
371
  } else if (FormData.alike(body)) {
370
372
  body = FormData.actuate(body);
371
373
  headers = { [HTTP2_HEADER_CONTENT_TYPE]: body.contentType };
372
- } else if (body === Object(body) && !Reflect.has(body, Symbol.asyncIterator)) {
373
- if (body.constructor === URLSearchParams) {
374
- headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_FORM_URLENCODED };
375
- body = body.toString();
376
- } else if (!Buffer.isBuffer(body)
377
- && !(!Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
378
- headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_JSON };
379
- body = JSON.stringify(body);
374
+ } else if (!Buffer.isBuffer(body)) {
375
+ if (types.isAnyArrayBuffer(body)) {
376
+ body = Buffer.from(body);
377
+ } else if (types.isArrayBufferView(body)) {
378
+ body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
379
+ } else if (body === Object(body) && !Reflect.has(body, Symbol.asyncIterator)) {
380
+ if (body.constructor === URLSearchParams) {
381
+ headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_FORM_URLENCODED };
382
+ body = body.toString();
383
+ } else if (!(!Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
384
+ headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_JSON };
385
+ body = JSON.stringify(body);
386
+ }
380
387
  }
388
+ }
381
389
 
382
- if (Buffer.isBuffer(body) || body !== Object(body)) {
383
- if (options.headers[HTTP2_HEADER_CONTENT_ENCODING]) {
384
- body = compress(body, options.headers[HTTP2_HEADER_CONTENT_ENCODING]);
385
- }
390
+ const encodings = options.headers[HTTP2_HEADER_CONTENT_ENCODING];
386
391
 
387
- headers = {
388
- ...headers,
389
- [HTTP2_HEADER_CONTENT_LENGTH]: Buffer.byteLength(body),
390
- };
392
+ if (encodings) {
393
+ if (Reflect.has(body, Symbol.asyncIterator)) {
394
+ body = compress(Readable.from(body), encodings);
395
+ } else {
396
+ body = await buffer(compress(Readable.from(body), encodings));
391
397
  }
398
+ } else if (body === Object(body)
399
+ && (Reflect.has(body, Symbol.asyncIterator) || (!Array.isArray(body) && Reflect.has(body, Symbol.iterator)))) {
400
+ body = Readable.from(body);
392
401
  }
393
402
 
394
403
  Object.assign(options.headers, {
@@ -398,5 +407,8 @@ export const transform = (body, options) => {
398
407
  },
399
408
  });
400
409
 
401
- return body;
410
+ return {
411
+ ...options,
412
+ body,
413
+ };
402
414
  };