rekwest 4.5.6 → 5.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/README.md CHANGED
@@ -12,7 +12,7 @@ and [http2.request](https://nodejs.org/api/http2.html#clienthttp2sessionrequesth
12
12
  * Automatic or opt-in body parse (with non-UTF-8 charset decoding)
13
13
  * Automatic and simplistic `Cookies` treatment (with built-in **jar** & **ttl**)
14
14
  * Automatic decompression (with opt-in body compression)
15
- * Built-in streamable `File` & `FormData` interfaces
15
+ * Built-in streamable `FormData` interface
16
16
  * Support redirects & retries with fine-grained tune-ups
17
17
  * Support all legit request body types (include blobs & streams)
18
18
  * Support both CJS and ESM module systems
@@ -21,7 +21,7 @@ and [http2.request](https://nodejs.org/api/http2.html#clienthttp2sessionrequesth
21
21
 
22
22
  ## Prerequisites
23
23
 
24
- * Node.js `>= 16.7.x`
24
+ * Node.js `>= 18.13.0`
25
25
 
26
26
  ## Installation
27
27
 
@@ -65,13 +65,13 @@ console.log(res.body);
65
65
  ---
66
66
 
67
67
  ```javascript
68
+ import { Readable } from 'node:stream';
68
69
  import rekwest, {
69
70
  constants,
70
71
  Blob,
71
72
  File,
72
73
  FormData,
73
74
  } from 'rekwest';
74
- import { Readable } from 'node:stream';
75
75
 
76
76
  const {
77
77
  HTTP2_HEADER_AUTHORIZATION,
@@ -122,7 +122,8 @@ console.log(res.body);
122
122
  for HTTP/2 attunes
123
123
  * `baseURL` **{string | URL}** The base URL to use in cases where `url` is a relative URL
124
124
  * `body` **{string | Array | ArrayBuffer | ArrayBufferView | AsyncIterator | Blob | Buffer | DataView | File |
125
- FormData | Iterator | Object | Readable | SharedArrayBuffer | URLSearchParams}** The body to send with the request
125
+ FormData | Iterator | Object | Readable | ReadableStream | SharedArrayBuffer | URLSearchParams}** The body to send
126
+ with the request
126
127
  * `cookies` **{boolean | Array<[k, v]> | Array<string\> | Cookies | Object | URLSearchParams}** `Default: true` The
127
128
  cookies to add to
128
129
  the request
@@ -172,13 +173,13 @@ console.log(res.body);
172
173
 
173
174
  #### `rekwest.defaults`
174
175
 
175
- The object to fulfill with default [options](#rekwesturl-options)
176
+ The object to fulfill with default [options](#rekwesturl-options).
176
177
 
177
178
  ---
178
179
 
179
180
  #### `rekwest.extend(options)`
180
181
 
181
- The method to extend default [options](#rekwesturl-options) per instance
182
+ The method to extend default [options](#rekwesturl-options) per instance.
182
183
 
183
184
  ```javascript
184
185
  import rekwest, { constants } from 'rekwest';
@@ -207,13 +208,35 @@ console.log(res.body);
207
208
 
208
209
  #### `rekwest.stream(url[, options])`
209
210
 
210
- The method with limited functionality to use with streams and/or pipes
211
+ The method with limited functionality to use with streams and/or pipes.
211
212
 
212
- * No automata
213
- * No redirects
213
+ * No automata (redirects & retries)
214
214
  * Pass `h2: true` in options to use HTTP/2 protocol
215
- * Use `ackn({ url: URL })` method beforehand to check the available protocols
215
+ * Use `ackn({ url: URL })` method in advance to check the available protocols
216
+
217
+ ```javascript
218
+ import fs from 'node:fs';
219
+ import { pipeline } from 'node:stream/promises';
220
+ import rekwest, {
221
+ ackn,
222
+ constants,
223
+ normalize,
224
+ } from 'rekwest';
225
+
226
+ const {
227
+ HTTP2_METHOD_POST,
228
+ } = constants;
229
+
230
+ const url = new URL('https://somewhe.re/somewhat/endpoint');
231
+ const options = await ackn({ url });
232
+
233
+ await pipeline(
234
+ fs.createReadStream('input.dab'),
235
+ rekwest.stream(url, { ...options, method: HTTP2_METHOD_POST }),
236
+ fs.createWriteStream('output.dab'),
237
+ );
238
+ ```
216
239
 
217
240
  ---
218
241
 
219
- For more details, please check tests (coverage: **>97%**) in the repository
242
+ For more details, please check tests (coverage: **>97%**) in the repository.
package/dist/formdata.js CHANGED
@@ -4,10 +4,10 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.FormData = void 0;
7
+ var _nodeBuffer = require("node:buffer");
7
8
  var _nodeCrypto = require("node:crypto");
8
9
  var _nodeHttp = _interopRequireDefault(require("node:http2"));
9
10
  var _nodeUtil = require("node:util");
10
- var _file = require("./file");
11
11
  var _mediatypes = require("./mediatypes");
12
12
  var _utils = require("./utils");
13
13
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -22,7 +22,7 @@ class FormData {
22
22
  const contentType = `${_mediatypes.MULTIPART_FORM_DATA}; boundary=${boundary}`;
23
23
  const prefix = `--${boundary}${CRLF}${HTTP2_HEADER_CONTENT_DISPOSITION}: form-data`;
24
24
  const escape = str => str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22');
25
- const redress = value => value.replace(/\r?\n|\r/g, CRLF);
25
+ const redress = str => str.replace(/\r?\n|\r/g, CRLF);
26
26
  return {
27
27
  contentType,
28
28
  async *[Symbol.asyncIterator]() {
@@ -41,14 +41,14 @@ class FormData {
41
41
  };
42
42
  }
43
43
  static alike(instance) {
44
- return instance?.constructor.name === FormData.name;
44
+ return FormData.name === instance?.[Symbol.toStringTag];
45
45
  }
46
46
  static #enfoldEntry(name, value, filename) {
47
47
  name = (0, _nodeUtil.toUSVString)(name);
48
48
  filename &&= (0, _nodeUtil.toUSVString)(filename);
49
- if (_file.File.alike(value)) {
49
+ if ((0, _utils.isFileLike)(value)) {
50
50
  filename ??= value.name || 'blob';
51
- value = new _file.File([value], filename, value);
51
+ value = new _nodeBuffer.File([value], filename, value);
52
52
  } else if (this.#ensureInstance(value)) {
53
53
  value.name = filename;
54
54
  } else {
@@ -60,7 +60,7 @@ class FormData {
60
60
  };
61
61
  }
62
62
  static #ensureInstance(value) {
63
- return _file.File.alike(value) || value === Object(value) && Reflect.has(value, Symbol.asyncIterator);
63
+ return (0, _utils.isFileLike)(value) || value === Object(value) && Reflect.has(value, Symbol.asyncIterator);
64
64
  }
65
65
  #entries = [];
66
66
  get [Symbol.toStringTag]() {
package/dist/index.js CHANGED
@@ -5,8 +5,22 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  var _exportNames = {
7
7
  constants: true,
8
- mediatypes: true
8
+ mediatypes: true,
9
+ Blob: true,
10
+ File: true
9
11
  };
12
+ Object.defineProperty(exports, "Blob", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _nodeBuffer.Blob;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "File", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _nodeBuffer.File;
22
+ }
23
+ });
10
24
  Object.defineProperty(exports, "constants", {
11
25
  enumerable: true,
12
26
  get: function () {
@@ -31,8 +45,9 @@ Object.keys(_constants).forEach(function (key) {
31
45
  });
32
46
  });
33
47
  var _defaults = _interopRequireDefault(require("./defaults"));
34
- var _mediatypes = _interopRequireWildcard(require("./mediatypes"));
35
- exports.mediatypes = _mediatypes;
48
+ var _mediatypes2 = _interopRequireWildcard(require("./mediatypes"));
49
+ var _mediatypes = _mediatypes2;
50
+ exports.mediatypes = _mediatypes2;
36
51
  var _preflight = require("./preflight");
37
52
  var _transfer = require("./transfer");
38
53
  var _utils = require("./utils");
@@ -59,6 +74,7 @@ Object.keys(_validation).forEach(function (key) {
59
74
  }
60
75
  });
61
76
  });
77
+ var _nodeBuffer = require("node:buffer");
62
78
  var _ackn = require("./ackn");
63
79
  Object.keys(_ackn).forEach(function (key) {
64
80
  if (key === "default" || key === "__esModule") return;
@@ -95,18 +111,6 @@ Object.keys(_errors).forEach(function (key) {
95
111
  }
96
112
  });
97
113
  });
98
- var _file = require("./file");
99
- Object.keys(_file).forEach(function (key) {
100
- if (key === "default" || key === "__esModule") return;
101
- if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
102
- if (key in exports && exports[key] === _file[key]) return;
103
- Object.defineProperty(exports, key, {
104
- enumerable: true,
105
- get: function () {
106
- return _file[key];
107
- }
108
- });
109
- });
110
114
  var _formdata = require("./formdata");
111
115
  Object.keys(_formdata).forEach(function (key) {
112
116
  if (key === "default" || key === "__esModule") return;
@@ -146,21 +150,21 @@ Reflect.defineProperty(rekwest, 'defaults', {
146
150
  return _defaults.default.stash;
147
151
  },
148
152
  set(value) {
149
- _defaults.default.stash = (0, _utils.merge)(_defaults.default.stash, value);
153
+ _defaults.default.stash = (0, _utils.copyWithMerge)(_defaults.default.stash, value);
150
154
  }
151
155
  });
152
156
  Reflect.defineProperty(rekwest, 'extend', {
153
157
  enumerable: true,
154
158
  value(options) {
155
- return (url, opts) => rekwest(url, (0, _utils.merge)(options, opts));
159
+ return (url, opts) => rekwest(url, (0, _utils.copyWithMerge)(options, opts));
156
160
  }
157
161
  });
158
162
  Reflect.defineProperty(rekwest, 'stream', {
159
163
  enumerable: true,
160
164
  value(url, options) {
161
- options = (0, _preflight.preflight)((0, _validation.validation)((0, _utils.normalize)(url, (0, _utils.merge)(options, {
165
+ options = (0, _preflight.preflight)((0, _validation.validation)((0, _utils.normalize)(url, (0, _utils.copyWithMerge)({}, options, {
162
166
  headers: {
163
- [HTTP2_HEADER_CONTENT_TYPE]: _mediatypes.APPLICATION_OCTET_STREAM
167
+ [HTTP2_HEADER_CONTENT_TYPE]: _mediatypes2.APPLICATION_OCTET_STREAM
164
168
  },
165
169
  redirect: _constants.requestRedirect.manual
166
170
  }))));
package/dist/transform.js CHANGED
@@ -8,7 +8,6 @@ var _nodeHttp = _interopRequireDefault(require("node:http2"));
8
8
  var _nodeStream = require("node:stream");
9
9
  var _consumers = require("node:stream/consumers");
10
10
  var _nodeUtil = require("node:util");
11
- var _file = require("./file");
12
11
  var _formdata = require("./formdata");
13
12
  var _mediatypes = require("./mediatypes");
14
13
  var _utils = require("./utils");
@@ -26,12 +25,12 @@ const transform = async options => {
26
25
  if (!body) {
27
26
  return options;
28
27
  }
29
- if (_file.File.alike(body)) {
28
+ if ((0, _utils.isFileLike)(body)) {
30
29
  headers = {
31
30
  [HTTP2_HEADER_CONTENT_LENGTH]: body.size,
32
31
  [HTTP2_HEADER_CONTENT_TYPE]: body.type || _mediatypes.APPLICATION_OCTET_STREAM
33
32
  };
34
- body = (0, _utils.tap)(body);
33
+ body = body.stream();
35
34
  } else if (_formdata.FormData.alike(body)) {
36
35
  body = _formdata.FormData.actuate(body);
37
36
  headers = {
@@ -58,7 +57,8 @@ const transform = async options => {
58
57
  }
59
58
  const encodings = options.headers[HTTP2_HEADER_CONTENT_ENCODING];
60
59
  if (body === Object(body) && (Reflect.has(body, Symbol.asyncIterator) || !Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
61
- body = encodings ? (0, _utils.compress)(_nodeStream.Readable.from(body), encodings) : _nodeStream.Readable.from(body);
60
+ body = (0, _nodeStream.isReadable)(body) ? (0, _utils.isReadableStream)(body) ? _nodeStream.Readable.fromWeb(body) : body : _nodeStream.Readable.from(body);
61
+ body = encodings ? (0, _utils.compress)(body, encodings) : body;
62
62
  } else if (encodings) {
63
63
  body = await (0, _consumers.buffer)((0, _utils.compress)(_nodeStream.Readable.from(body), encodings));
64
64
  }
package/dist/utils.js CHANGED
@@ -3,9 +3,10 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.sameOrigin = exports.normalize = exports.merge = exports.maxRetryAfterError = exports.maxRetryAfter = exports.dispatch = exports.decompress = exports.compress = exports.brandCheck = exports.affix = exports.admix = void 0;
6
+ exports.sameOrigin = exports.normalize = exports.merge = exports.maxRetryAfterError = exports.maxRetryAfter = exports.isReadableStream = exports.isFileLike = exports.dispatch = exports.decompress = exports.copyWithMerge = exports.compress = exports.brandCheck = exports.affix = exports.admix = void 0;
7
7
  exports.tap = tap;
8
8
  exports.unwind = exports.toCamelCase = void 0;
9
+ var _nodeBuffer = require("node:buffer");
9
10
  var _nodeHttp = _interopRequireDefault(require("node:http2"));
10
11
  var _nodeStream = require("node:stream");
11
12
  var _nodeZlib = _interopRequireDefault(require("node:zlib"));
@@ -80,6 +81,14 @@ const compress = (readable, encodings = '') => {
80
81
  return (0, _nodeStream.pipeline)(readable, ...encoders, () => void 0);
81
82
  };
82
83
  exports.compress = compress;
84
+ const copyWithMerge = (target, ...rest) => {
85
+ target = structuredClone(target);
86
+ if (!rest.length) {
87
+ return target;
88
+ }
89
+ return merge(target, ...rest);
90
+ };
91
+ exports.copyWithMerge = copyWithMerge;
83
92
  const decompress = (readable, encodings = '') => {
84
93
  const decoders = [];
85
94
  encodings = unwind(encodings);
@@ -109,35 +118,37 @@ const dispatch = ({
109
118
  }
110
119
  };
111
120
  exports.dispatch = dispatch;
121
+ const isFileLike = instance => {
122
+ return [_nodeBuffer.Blob.name, _nodeBuffer.File.name].includes(instance?.[Symbol.toStringTag]);
123
+ };
124
+ exports.isFileLike = isFileLike;
125
+ const isReadableStream = instance => {
126
+ return ReadableStream.name === instance?.[Symbol.toStringTag];
127
+ };
128
+ exports.isReadableStream = isReadableStream;
112
129
  const maxRetryAfter = Symbol('maxRetryAfter');
113
130
  exports.maxRetryAfter = maxRetryAfter;
114
131
  const maxRetryAfterError = (interval, options) => new _errors.RequestError(`Maximum '${HTTP2_HEADER_RETRY_AFTER}' limit exceeded: ${interval} ms.`, options);
115
132
  exports.maxRetryAfterError = maxRetryAfterError;
116
- const merge = (target = {}, ...rest) => {
117
- target = JSON.parse(JSON.stringify(target));
118
- if (!rest.length) {
119
- return target;
120
- }
121
- rest.filter(it => it === Object(it)).forEach(it => {
122
- Object.entries(it).reduce((acc, [key, val]) => {
123
- if ([acc[key]?.constructor, val?.constructor].every(it => [Array, Object].includes(it))) {
124
- if (acc[key]?.constructor === val.constructor) {
125
- acc[key] = merge(acc[key], val);
126
- } else {
127
- acc[key] = val;
128
- }
129
- } else {
130
- acc[key] = val;
133
+ const merge = (target, ...rest) => {
134
+ rest = rest.filter(it => it === Object(it));
135
+ for (const source of rest) {
136
+ for (const key of Object.getOwnPropertyNames(source)) {
137
+ const sv = source[key];
138
+ const tv = target[key];
139
+ if (Object(sv) === sv && Object(tv) === tv) {
140
+ target[key] = merge(tv, sv);
141
+ continue;
131
142
  }
132
- return acc;
133
- }, target);
134
- });
143
+ target[key] = source[key];
144
+ }
145
+ }
135
146
  return target;
136
147
  };
137
148
  exports.merge = merge;
138
149
  const normalize = (url, options = {}) => {
139
150
  if (!options.redirected) {
140
- options = merge(_defaults.default.stash, options);
151
+ options = copyWithMerge(_defaults.default.stash, options);
141
152
  }
142
153
  if (options.trimTrailingSlashes) {
143
154
  url = `${url}`.replace(/(?<!:)\/+/g, '/');
package/package.json CHANGED
@@ -8,18 +8,18 @@
8
8
  "url": "https://github.com/bricss/rekwest/issues"
9
9
  },
10
10
  "devDependencies": {
11
- "@babel/cli": "^7.22.9",
12
- "@babel/core": "^7.22.9",
13
- "@babel/eslint-parser": "^7.22.9",
14
- "@babel/preset-env": "^7.22.9",
15
- "c8": "^8.0.0",
16
- "eslint": "^8.45.0",
17
- "eslint-config-ultra-refined": "^2.16.0",
11
+ "@babel/cli": "^7.22.15",
12
+ "@babel/core": "^7.22.20",
13
+ "@babel/eslint-parser": "^7.22.15",
14
+ "@babel/preset-env": "^7.22.20",
15
+ "c8": "^8.0.1",
16
+ "eslint": "^8.50.0",
17
+ "eslint-config-ultra-refined": "^2.18.0",
18
18
  "mocha": "^10.2.0"
19
19
  },
20
20
  "description": "The robust request library that humanity deserves 🌐",
21
21
  "engines": {
22
- "node": ">=16.7.x"
22
+ "node": ">=18.13.0"
23
23
  },
24
24
  "exports": {
25
25
  "import": "./src/index.mjs",
@@ -69,5 +69,5 @@
69
69
  "test:bail": "mocha --bail",
70
70
  "test:cover": "c8 --include=src --reporter=lcov --reporter=text npm test"
71
71
  },
72
- "version": "4.5.6"
72
+ "version": "5.0.0"
73
73
  }
package/src/formdata.mjs CHANGED
@@ -1,13 +1,14 @@
1
+ import { File } from 'node:buffer';
1
2
  import { randomBytes } from 'node:crypto';
2
3
  import http2 from 'node:http2';
3
4
  import { toUSVString } from 'node:util';
4
- import { File } from './file.mjs';
5
5
  import {
6
6
  APPLICATION_OCTET_STREAM,
7
7
  MULTIPART_FORM_DATA,
8
8
  } from './mediatypes.mjs';
9
9
  import {
10
10
  brandCheck,
11
+ isFileLike,
11
12
  tap,
12
13
  } from './utils.mjs';
13
14
 
@@ -25,7 +26,7 @@ export class FormData {
25
26
  const prefix = `--${ boundary }${ CRLF }${ HTTP2_HEADER_CONTENT_DISPOSITION }: form-data`;
26
27
 
27
28
  const escape = (str) => str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22');
28
- const redress = (value) => value.replace(/\r?\n|\r/g, CRLF);
29
+ const redress = (str) => str.replace(/\r?\n|\r/g, CRLF);
29
30
 
30
31
  return {
31
32
  contentType,
@@ -59,14 +60,14 @@ export class FormData {
59
60
  }
60
61
 
61
62
  static alike(instance) {
62
- return instance?.constructor.name === FormData.name;
63
+ return FormData.name === instance?.[Symbol.toStringTag];
63
64
  }
64
65
 
65
66
  static #enfoldEntry(name, value, filename) {
66
67
  name = toUSVString(name);
67
68
  filename &&= toUSVString(filename);
68
69
 
69
- if (File.alike(value)) {
70
+ if (isFileLike(value)) {
70
71
  filename ??= value.name || 'blob';
71
72
  value = new File([value], filename, value);
72
73
  } else if (this.#ensureInstance(value)) {
@@ -82,7 +83,7 @@ export class FormData {
82
83
  }
83
84
 
84
85
  static #ensureInstance(value) {
85
- return File.alike(value) || (value === Object(value) && Reflect.has(value, Symbol.asyncIterator));
86
+ return isFileLike(value) || (value === Object(value) && Reflect.has(value, Symbol.asyncIterator));
86
87
  }
87
88
 
88
89
  #entries = [];
package/src/index.mjs CHANGED
@@ -9,18 +9,21 @@ import { transfer } from './transfer.mjs';
9
9
  import {
10
10
  admix,
11
11
  affix,
12
- merge,
12
+ copyWithMerge,
13
13
  normalize,
14
14
  } from './utils.mjs';
15
15
  import { validation } from './validation.mjs';
16
16
 
17
+ export {
18
+ Blob,
19
+ File,
20
+ } from 'node:buffer';
17
21
  export { constants } from 'node:http2';
18
22
 
19
23
  export * from './ackn.mjs';
20
24
  export * from './constants.mjs';
21
25
  export * from './cookies.mjs';
22
26
  export * from './errors.mjs';
23
- export * from './file.mjs';
24
27
  export * from './formdata.mjs';
25
28
  export * as mediatypes from './mediatypes.mjs';
26
29
  export * from './mixin.mjs';
@@ -38,20 +41,20 @@ export default function rekwest(url, options) {
38
41
  Reflect.defineProperty(rekwest, 'defaults', {
39
42
  enumerable: true,
40
43
  get() { return defaults.stash; },
41
- set(value) { defaults.stash = merge(defaults.stash, value); },
44
+ set(value) { defaults.stash = copyWithMerge(defaults.stash, value); },
42
45
  });
43
46
 
44
47
  Reflect.defineProperty(rekwest, 'extend', {
45
48
  enumerable: true,
46
49
  value(options) {
47
- return (url, opts) => rekwest(url, merge(options, opts));
50
+ return (url, opts) => rekwest(url, copyWithMerge(options, opts));
48
51
  },
49
52
  });
50
53
 
51
54
  Reflect.defineProperty(rekwest, 'stream', {
52
55
  enumerable: true,
53
56
  value(url, options) {
54
- options = preflight(validation(normalize(url, merge(options, {
57
+ options = preflight(validation(normalize(url, copyWithMerge({}, options, {
55
58
  headers: { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_OCTET_STREAM },
56
59
  redirect: requestRedirect.manual,
57
60
  }))));
package/src/transform.mjs CHANGED
@@ -1,8 +1,10 @@
1
1
  import http2 from 'node:http2';
2
- import { Readable } from 'node:stream';
2
+ import {
3
+ isReadable,
4
+ Readable,
5
+ } from 'node:stream';
3
6
  import { buffer } from 'node:stream/consumers';
4
7
  import { types } from 'node:util';
5
- import { File } from './file.mjs';
6
8
  import { FormData } from './formdata.mjs';
7
9
  import {
8
10
  APPLICATION_FORM_URLENCODED,
@@ -11,7 +13,8 @@ import {
11
13
  } from './mediatypes.mjs';
12
14
  import {
13
15
  compress,
14
- tap,
16
+ isFileLike,
17
+ isReadableStream,
15
18
  } from './utils.mjs';
16
19
 
17
20
  const {
@@ -27,12 +30,12 @@ export const transform = async (options) => {
27
30
  return options;
28
31
  }
29
32
 
30
- if (File.alike(body)) {
33
+ if (isFileLike(body)) {
31
34
  headers = {
32
35
  [HTTP2_HEADER_CONTENT_LENGTH]: body.size,
33
36
  [HTTP2_HEADER_CONTENT_TYPE]: body.type || APPLICATION_OCTET_STREAM,
34
37
  };
35
- body = tap(body);
38
+ body = body.stream();
36
39
  } else if (FormData.alike(body)) {
37
40
  body = FormData.actuate(body);
38
41
  headers = { [HTTP2_HEADER_CONTENT_TYPE]: body.contentType };
@@ -56,7 +59,8 @@ export const transform = async (options) => {
56
59
 
57
60
  if (body === Object(body)
58
61
  && (Reflect.has(body, Symbol.asyncIterator) || (!Array.isArray(body) && Reflect.has(body, Symbol.iterator)))) {
59
- body = encodings ? compress(Readable.from(body), encodings) : Readable.from(body);
62
+ body = isReadable(body) ? (isReadableStream(body) ? Readable.fromWeb(body) : body) : Readable.from(body);
63
+ body = encodings ? compress(body, encodings) : body;
60
64
  } else if (encodings) {
61
65
  body = await buffer(compress(Readable.from(body), encodings));
62
66
  }
package/src/utils.mjs CHANGED
@@ -1,3 +1,7 @@
1
+ import {
2
+ Blob,
3
+ File,
4
+ } from 'node:buffer';
1
5
  import http2 from 'node:http2';
2
6
  import { pipeline } from 'node:stream';
3
7
  import zlib from 'node:zlib';
@@ -82,6 +86,15 @@ export const compress = (readable, encodings = '') => {
82
86
  return pipeline(readable, ...encoders, () => void 0);
83
87
  };
84
88
 
89
+ export const copyWithMerge = (target, ...rest) => {
90
+ target = structuredClone(target);
91
+ if (!rest.length) {
92
+ return target;
93
+ }
94
+
95
+ return merge(target, ...rest);
96
+ };
97
+
85
98
  export const decompress = (readable, encodings = '') => {
86
99
  const decoders = [];
87
100
 
@@ -112,6 +125,17 @@ export const dispatch = ({ body }, req) => {
112
125
  }
113
126
  };
114
127
 
128
+ export const isFileLike = (instance) => {
129
+ return [
130
+ Blob.name,
131
+ File.name,
132
+ ].includes(instance?.[Symbol.toStringTag]);
133
+ };
134
+
135
+ export const isReadableStream = (instance) => {
136
+ return ReadableStream.name === instance?.[Symbol.toStringTag];
137
+ };
138
+
115
139
  export const maxRetryAfter = Symbol('maxRetryAfter');
116
140
 
117
141
  export const maxRetryAfterError = (
@@ -119,40 +143,28 @@ export const maxRetryAfterError = (
119
143
  options,
120
144
  ) => new RequestError(`Maximum '${ HTTP2_HEADER_RETRY_AFTER }' limit exceeded: ${ interval } ms.`, options);
121
145
 
122
- export const merge = (target = {}, ...rest) => {
123
- target = JSON.parse(JSON.stringify(target));
124
- if (!rest.length) {
125
- return target;
126
- }
146
+ export const merge = (target, ...rest) => {
147
+ rest = rest.filter((it) => it === Object(it));
148
+ for (const source of rest) {
149
+ for (const key of Object.getOwnPropertyNames(source)) {
150
+ const sv = source[key];
151
+ const tv = target[key];
127
152
 
128
- rest.filter((it) => it === Object(it)).forEach((it) => {
129
- Object.entries(it).reduce((acc, [key, val]) => {
130
- if ([
131
- acc[key]?.constructor,
132
- val?.constructor,
133
- ].every((it) => [
134
- Array,
135
- Object,
136
- ].includes(it))) {
137
- if (acc[key]?.constructor === val.constructor) {
138
- acc[key] = merge(acc[key], val);
139
- } else {
140
- acc[key] = val;
141
- }
142
- } else {
143
- acc[key] = val;
153
+ if (Object(sv) === sv && Object(tv) === tv) {
154
+ target[key] = merge(tv, sv);
155
+ continue;
144
156
  }
145
157
 
146
- return acc;
147
- }, target);
148
- });
158
+ target[key] = source[key];
159
+ }
160
+ }
149
161
 
150
162
  return target;
151
163
  };
152
164
 
153
165
  export const normalize = (url, options = {}) => {
154
166
  if (!options.redirected) {
155
- options = merge(defaults.stash, options);
167
+ options = copyWithMerge(defaults.stash, options);
156
168
  }
157
169
 
158
170
  if (options.trimTrailingSlashes) {
package/dist/file.js DELETED
@@ -1,44 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "Blob", {
7
- enumerable: true,
8
- get: function () {
9
- return _nodeBuffer.Blob;
10
- }
11
- });
12
- exports.File = void 0;
13
- var _nodeBuffer = require("node:buffer");
14
- var _nodeUtil = require("node:util");
15
- class File extends _nodeBuffer.Blob {
16
- static alike(instance) {
17
- return [_nodeBuffer.Blob.name, File.name].includes(instance?.constructor.name);
18
- }
19
- #lastModified;
20
- #name;
21
- get [Symbol.toStringTag]() {
22
- return this.constructor.name;
23
- }
24
- get lastModified() {
25
- return this.#lastModified;
26
- }
27
- get name() {
28
- return this.#name;
29
- }
30
- constructor(...args) {
31
- const len = args.length;
32
- if (len < 2) {
33
- throw new TypeError(`Failed to construct '${File.name}': 2 arguments required, but only ${len} present.`);
34
- }
35
- const [bits, name, options = {}] = args;
36
- const {
37
- lastModified = Date.now()
38
- } = options;
39
- super(bits, options);
40
- this.#lastModified = +lastModified ? lastModified : 0;
41
- this.#name = (0, _nodeUtil.toUSVString)(name);
42
- }
43
- }
44
- exports.File = File;
package/src/file.mjs DELETED
@@ -1,49 +0,0 @@
1
- import { Blob } from 'node:buffer';
2
- import { toUSVString } from 'node:util';
3
-
4
- export { Blob } from 'node:buffer';
5
-
6
- export class File extends Blob {
7
-
8
- static alike(instance) {
9
- return [
10
- Blob.name,
11
- File.name,
12
- ].includes(instance?.constructor.name);
13
- }
14
-
15
- #lastModified;
16
- #name;
17
-
18
- get [Symbol.toStringTag]() {
19
- return this.constructor.name;
20
- }
21
-
22
- get lastModified() {
23
- return this.#lastModified;
24
- }
25
-
26
- get name() {
27
- return this.#name;
28
- }
29
-
30
- constructor(...args) {
31
- const len = args.length;
32
-
33
- if (len < 2) {
34
- throw new TypeError(`Failed to construct '${
35
- File.name
36
- }': 2 arguments required, but only ${ len } present.`);
37
- }
38
-
39
- const [bits, name, options = {}] = args;
40
- const {
41
- lastModified = Date.now(),
42
- } = options;
43
-
44
- super(bits, options);
45
- this.#lastModified = +lastModified ? lastModified : 0;
46
- this.#name = toUSVString(name);
47
- }
48
-
49
- }