rekwest 4.2.3 → 4.4.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
@@ -121,6 +121,7 @@ console.log(res.body);
121
121
  & [http2.ClientSessionRequestOptions](https://nodejs.org/api/http2.html#http2_clienthttp2session_request_headers_options)
122
122
  and [tls.ConnectionOptions](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback)
123
123
  for HTTP/2 attunes
124
+ * `baseURL` **{string | URL}** The base URL to use in cases where `url` is a relative URL
124
125
  * `body` **{string | Array | ArrayBuffer | ArrayBufferView | AsyncIterator | Blob | Buffer | DataView | File |
125
126
  FormData | Iterator | Object | Readable | SharedArrayBuffer | URLSearchParams}** The body to send with the request
126
127
  * `cookies` **{boolean | Array<[k, v]> | Cookies | Object | URLSearchParams}** `Default: true` The cookies to add to
@@ -174,6 +175,35 @@ The object to fulfill with default [options](#rekwesturl-options)
174
175
 
175
176
  ---
176
177
 
178
+ #### `rekwest.extend(options)`
179
+
180
+ The method to extend default [options](#rekwesturl-options) per instance
181
+
182
+ ```javascript
183
+ import rekwest, { constants } from 'rekwest';
184
+
185
+ const {
186
+ HTTP_STATUS_OK,
187
+ } = constants;
188
+
189
+ const rk = rekwest.extend({
190
+ baseURL: 'https://somewhe.re'
191
+ });
192
+
193
+ const signal = AbortSignal.timeout(1e4);
194
+ const url = '/somewhat/endpoint';
195
+
196
+ const res = await rk(url, {
197
+ signal,
198
+ });
199
+
200
+ console.assert(res.statusCode === HTTP_STATUS_OK);
201
+ console.info(res.headers);
202
+ console.log(res.body);
203
+ ```
204
+
205
+ ---
206
+
177
207
  #### `rekwest.stream(url[, options])`
178
208
 
179
209
  The method with limited functionality to use with streams and/or pipes
package/dist/formdata.js CHANGED
@@ -20,16 +20,16 @@ class FormData {
20
20
  const contentType = `${_mediatypes.MULTIPART_FORM_DATA}; boundary=${boundary}`;
21
21
  const prefix = `--${boundary}${CRLF}${HTTP2_HEADER_CONTENT_DISPOSITION}: form-data`;
22
22
  const escape = str => str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22');
23
- const normalize = value => value.replace(/\r?\n|\r/g, CRLF);
23
+ const redress = value => value.replace(/\r?\n|\r/g, CRLF);
24
24
  return {
25
25
  contentType,
26
26
  async *[Symbol.asyncIterator]() {
27
27
  const encoder = new TextEncoder();
28
28
  for (const [name, value] of fd) {
29
29
  if (value.constructor === String) {
30
- yield encoder.encode(`${prefix}; name="${escape(normalize(name))}"${CRLF.repeat(2)}${normalize(value)}${CRLF}`);
30
+ yield encoder.encode(`${prefix}; name="${escape(redress(name))}"${CRLF.repeat(2)}${redress(value)}${CRLF}`);
31
31
  } else {
32
- yield encoder.encode(`${prefix}; name="${escape(normalize(name))}"${value.name ? `; filename="${escape(value.name)}"` : ''}${CRLF}${HTTP2_HEADER_CONTENT_TYPE}: ${value.type || _mediatypes.APPLICATION_OCTET_STREAM}${CRLF.repeat(2)}`);
32
+ yield encoder.encode(`${prefix}; name="${escape(redress(name))}"${value.name ? `; filename="${escape(value.name)}"` : ''}${CRLF}${HTTP2_HEADER_CONTENT_TYPE}: ${value.type || _mediatypes.APPLICATION_OCTET_STREAM}${CRLF.repeat(2)}`);
33
33
  yield* (0, _utils.tap)(value);
34
34
  yield new Uint8Array([13, 10]);
35
35
  }
package/dist/index.js CHANGED
@@ -30,6 +30,13 @@ Object.keys(_utils).forEach(function (key) {
30
30
  if (key in exports && exports[key] === _utils[key]) return;
31
31
  exports[key] = _utils[key];
32
32
  });
33
+ var _validation = require("./validation");
34
+ Object.keys(_validation).forEach(function (key) {
35
+ if (key === "default" || key === "__esModule") return;
36
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
37
+ if (key in exports && exports[key] === _validation[key]) return;
38
+ exports[key] = _validation[key];
39
+ });
33
40
  var _ackn = require("./ackn");
34
41
  Object.keys(_ackn).forEach(function (key) {
35
42
  if (key === "default" || key === "__esModule") return;
@@ -65,48 +72,51 @@ Object.keys(_formdata).forEach(function (key) {
65
72
  if (key in exports && exports[key] === _formdata[key]) return;
66
73
  exports[key] = _formdata[key];
67
74
  });
75
+ var _mixin = require("./mixin");
76
+ Object.keys(_mixin).forEach(function (key) {
77
+ if (key === "default" || key === "__esModule") return;
78
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
79
+ if (key in exports && exports[key] === _mixin[key]) return;
80
+ exports[key] = _mixin[key];
81
+ });
68
82
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
69
83
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
70
84
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
71
85
  const {
72
86
  HTTP2_HEADER_CONTENT_TYPE
73
87
  } = _nodeHttp2.default.constants;
74
- function rekwest(...args) {
75
- let options = (0, _utils.sanitize)(...args);
76
- if (!options.redirected) {
77
- options = (0, _utils.merge)(rekwest.defaults, options);
78
- }
79
- return (0, _utils.transfer)((0, _utils.validation)(options));
88
+ function rekwest(url, options) {
89
+ return (0, _utils.transfer)((0, _validation.validation)((0, _utils.normalize)(url, options)));
80
90
  }
91
+ Reflect.defineProperty(rekwest, 'extend', {
92
+ enumerable: true,
93
+ value(options) {
94
+ return (url, opts) => rekwest(url, (0, _utils.merge)(options, opts));
95
+ }
96
+ });
81
97
  Reflect.defineProperty(rekwest, 'stream', {
82
98
  enumerable: true,
83
- value(...args) {
84
- const options = (0, _preflight.preflight)({
85
- ...(0, _utils.validation)((0, _utils.merge)(rekwest.defaults, {
86
- headers: {
87
- [HTTP2_HEADER_CONTENT_TYPE]: _mediatypes.APPLICATION_OCTET_STREAM
88
- }
89
- }, (0, _utils.sanitize)(...args))),
99
+ value(url, options) {
100
+ options = (0, _preflight.preflight)((0, _validation.validation)((0, _utils.normalize)(url, (0, _utils.merge)(options, {
101
+ headers: {
102
+ [HTTP2_HEADER_CONTENT_TYPE]: _mediatypes.APPLICATION_OCTET_STREAM
103
+ },
90
104
  redirect: _constants.requestRedirect.manual
91
- });
92
- const {
93
- h2,
94
- url
95
- } = options;
105
+ }))));
96
106
  let client, req;
97
- if (h2) {
98
- client = _nodeHttp2.default.connect(url.origin, options);
107
+ if (options.h2) {
108
+ client = _nodeHttp2.default.connect(options.url.origin, options);
99
109
  req = client.request(options.headers, options);
100
110
  } else {
101
111
  const {
102
112
  request
103
- } = url.protocol === 'http:' ? _nodeHttp.default : _nodeHttps.default;
104
- req = request(url, options);
113
+ } = options.url.protocol === 'http:' ? _nodeHttp.default : _nodeHttps.default;
114
+ req = request(options.url, options);
105
115
  }
106
116
  (0, _utils.affix)(client, req, options);
107
117
  req.once('response', res => {
108
118
  let headers;
109
- if (h2) {
119
+ if (options.h2) {
110
120
  headers = res;
111
121
  res = req;
112
122
  }
package/dist/mixin.js ADDED
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.mixin = void 0;
5
+ var _nodeBuffer = require("node:buffer");
6
+ var _nodeHttp = _interopRequireDefault(require("node:http2"));
7
+ var _utils = require("./utils");
8
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
+ const {
10
+ HTTP2_HEADER_CONTENT_ENCODING,
11
+ HTTP2_HEADER_CONTENT_TYPE
12
+ } = _nodeHttp.default.constants;
13
+ const mixin = (res, {
14
+ digest = false,
15
+ parse = false
16
+ } = {}) => {
17
+ if (!digest) {
18
+ Object.defineProperties(res, {
19
+ arrayBuffer: {
20
+ enumerable: true,
21
+ value: async function () {
22
+ (0, _utils.brandCheck)(this, res?.constructor);
23
+ parse &&= false;
24
+ const {
25
+ buffer,
26
+ byteLength,
27
+ byteOffset
28
+ } = await this.body();
29
+ return buffer.slice(byteOffset, byteOffset + byteLength);
30
+ }
31
+ },
32
+ blob: {
33
+ enumerable: true,
34
+ value: async function () {
35
+ (0, _utils.brandCheck)(this, res?.constructor);
36
+ const val = await this.arrayBuffer();
37
+ return new _nodeBuffer.Blob([val]);
38
+ }
39
+ },
40
+ json: {
41
+ enumerable: true,
42
+ value: async function () {
43
+ (0, _utils.brandCheck)(this, res?.constructor);
44
+ const val = await this.text();
45
+ return JSON.parse(val);
46
+ }
47
+ },
48
+ text: {
49
+ enumerable: true,
50
+ value: async function () {
51
+ (0, _utils.brandCheck)(this, res?.constructor);
52
+ const blob = await this.blob();
53
+ return blob.text();
54
+ }
55
+ }
56
+ });
57
+ }
58
+ return Object.defineProperties(res, {
59
+ body: {
60
+ enumerable: true,
61
+ value: async function () {
62
+ (0, _utils.brandCheck)(this, res?.constructor);
63
+ if (this.bodyUsed) {
64
+ throw new TypeError('Response stream already read');
65
+ }
66
+ let body = [];
67
+ for await (const chunk of (0, _utils.decompress)(this, this.headers[HTTP2_HEADER_CONTENT_ENCODING])) {
68
+ body.push(chunk);
69
+ }
70
+ body = Buffer.concat(body);
71
+ if (!body.length && parse) {
72
+ return null;
73
+ }
74
+ if (body.length && parse) {
75
+ const contentType = this.headers[HTTP2_HEADER_CONTENT_TYPE] ?? '';
76
+ const charset = contentType.split(';').find(it => /charset=/i.test(it))?.toLowerCase().replace('charset=', '').replace('iso-8859-1', 'latin1').trim() || 'utf-8';
77
+ if (/\bjson\b/i.test(contentType)) {
78
+ body = JSON.parse(body.toString(charset));
79
+ } else if (/\b(?:text|xml)\b/i.test(contentType)) {
80
+ if (/\b(?:latin1|ucs-2|utf-(?:8|16le))\b/i.test(charset)) {
81
+ body = body.toString(charset);
82
+ } else {
83
+ body = new TextDecoder(charset).decode(body);
84
+ }
85
+ }
86
+ }
87
+ return body;
88
+ },
89
+ writable: true
90
+ },
91
+ bodyUsed: {
92
+ enumerable: true,
93
+ get() {
94
+ return this.readableEnded;
95
+ }
96
+ }
97
+ });
98
+ };
99
+ exports.mixin = mixin;
@@ -8,6 +8,7 @@ var _constants = require("./constants");
8
8
  var _cookies = require("./cookies");
9
9
  var _errors = require("./errors");
10
10
  var _index = _interopRequireDefault(require("./index"));
11
+ var _mixin = require("./mixin");
11
12
  var _utils = require("./utils");
12
13
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
14
  const {
@@ -97,7 +98,7 @@ const postflight = (req, res, options, {
97
98
  interval = Number(interval) * 1000 || new Date(interval) - Date.now();
98
99
  if (interval > options.maxRetryAfter) {
99
100
  return res.emit('error', (0, _utils.maxRetryAfterError)(interval, {
100
- cause: (0, _utils.mixin)(res, options)
101
+ cause: (0, _mixin.mixin)(res, options)
101
102
  }));
102
103
  }
103
104
  return (0, _promises.setTimeout)(interval).then(() => (0, _index.default)(options.url, options).then(resolve, reject));
@@ -106,8 +107,8 @@ const postflight = (req, res, options, {
106
107
  }
107
108
  }
108
109
  if (statusCode >= HTTP_STATUS_BAD_REQUEST) {
109
- return reject((0, _utils.mixin)(res, options));
110
+ return reject((0, _mixin.mixin)(res, options));
110
111
  }
111
- resolve((0, _utils.mixin)(res, options));
112
+ resolve((0, _mixin.mixin)(res, options));
112
113
  };
113
114
  exports.postflight = postflight;
package/dist/utils.js CHANGED
@@ -1,10 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
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;
4
+ exports.sameOrigin = exports.normalize = 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.validation = exports.unwind = exports.transform = exports.transfer = void 0;
7
- var _nodeBuffer = require("node:buffer");
6
+ exports.unwind = exports.transform = exports.transfer = void 0;
8
7
  var _nodeHttp = _interopRequireDefault(require("node:http"));
9
8
  var _nodeHttp2 = _interopRequireDefault(require("node:http2"));
10
9
  var _nodeHttps = _interopRequireDefault(require("node:https"));
@@ -14,7 +13,6 @@ var _promises = require("node:timers/promises");
14
13
  var _nodeUtil = require("node:util");
15
14
  var _nodeZlib = _interopRequireDefault(require("node:zlib"));
16
15
  var _ackn = require("./ackn");
17
- var _constants = require("./constants");
18
16
  var _errors = require("./errors");
19
17
  var _file = require("./file");
20
18
  var _formdata = require("./formdata");
@@ -28,9 +26,7 @@ const {
28
26
  HTTP2_HEADER_CONTENT_LENGTH,
29
27
  HTTP2_HEADER_CONTENT_TYPE,
30
28
  HTTP2_HEADER_RETRY_AFTER,
31
- HTTP2_HEADER_STATUS,
32
- HTTP2_METHOD_GET,
33
- HTTP2_METHOD_HEAD
29
+ HTTP2_HEADER_STATUS
34
30
  } = _nodeHttp2.default.constants;
35
31
  const admix = (res, headers, options) => {
36
32
  const {
@@ -151,103 +147,19 @@ const merge = (target = {}, ...rest) => {
151
147
  return target;
152
148
  };
153
149
  exports.merge = merge;
154
- const mixin = (res, {
155
- digest = false,
156
- parse = false
157
- } = {}) => {
158
- if (!digest) {
159
- Object.defineProperties(res, {
160
- arrayBuffer: {
161
- enumerable: true,
162
- value: async function () {
163
- brandCheck(this, res?.constructor);
164
- parse &&= false;
165
- const {
166
- buffer,
167
- byteLength,
168
- byteOffset
169
- } = await this.body();
170
- return buffer.slice(byteOffset, byteOffset + byteLength);
171
- }
172
- },
173
- blob: {
174
- enumerable: true,
175
- value: async function () {
176
- brandCheck(this, res?.constructor);
177
- const val = await this.arrayBuffer();
178
- return new _nodeBuffer.Blob([val]);
179
- }
180
- },
181
- json: {
182
- enumerable: true,
183
- value: async function () {
184
- brandCheck(this, res?.constructor);
185
- const val = await this.text();
186
- return JSON.parse(val);
187
- }
188
- },
189
- text: {
190
- enumerable: true,
191
- value: async function () {
192
- brandCheck(this, res?.constructor);
193
- const blob = await this.blob();
194
- return blob.text();
195
- }
196
- }
197
- });
150
+ const normalize = (url, options = {}) => {
151
+ if (!options.redirected) {
152
+ options = merge(_index.default.defaults, options);
198
153
  }
199
- return Object.defineProperties(res, {
200
- body: {
201
- enumerable: true,
202
- value: async function () {
203
- brandCheck(this, res?.constructor);
204
- if (this.bodyUsed) {
205
- throw new TypeError('Response stream already read');
206
- }
207
- let body = [];
208
- for await (const chunk of decompress(this, this.headers[HTTP2_HEADER_CONTENT_ENCODING])) {
209
- body.push(chunk);
210
- }
211
- body = Buffer.concat(body);
212
- if (!body.length && parse) {
213
- return null;
214
- }
215
- if (body.length && parse) {
216
- const contentType = this.headers[HTTP2_HEADER_CONTENT_TYPE] ?? '';
217
- const charset = contentType.split(';').find(it => /charset=/i.test(it))?.toLowerCase().replace('charset=', '').replace('iso-8859-1', 'latin1').trim() || 'utf-8';
218
- if (/\bjson\b/i.test(contentType)) {
219
- body = JSON.parse(body.toString(charset));
220
- } else if (/\b(?:text|xml)\b/i.test(contentType)) {
221
- if (/\b(?:latin1|ucs-2|utf-(?:8|16le))\b/i.test(charset)) {
222
- body = body.toString(charset);
223
- } else {
224
- body = new TextDecoder(charset).decode(body);
225
- }
226
- }
227
- }
228
- return body;
229
- },
230
- writable: true
231
- },
232
- bodyUsed: {
233
- enumerable: true,
234
- get() {
235
- return this.readableEnded;
236
- }
237
- }
238
- });
239
- };
240
- exports.mixin = mixin;
241
- const sanitize = (url, options = {}) => {
242
154
  if (options.trimTrailingSlashes) {
243
155
  url = `${url}`.replace(/(?<!:)\/+/g, '/');
244
156
  }
245
- url = new URL(url);
157
+ url = new URL(url, options.baseURL);
246
158
  return Object.assign(options, {
247
159
  url
248
160
  });
249
161
  };
250
- exports.sanitize = sanitize;
162
+ exports.normalize = normalize;
251
163
  const sameOrigin = (a, b) => a.protocol === b.protocol && a.hostname === b.hostname && a.port === b.port;
252
164
  exports.sameOrigin = sameOrigin;
253
165
  async function* tap(value) {
@@ -262,7 +174,6 @@ async function* tap(value) {
262
174
  const transfer = async options => {
263
175
  const {
264
176
  digest,
265
- h2,
266
177
  redirected,
267
178
  thenable,
268
179
  url
@@ -271,7 +182,7 @@ const transfer = async options => {
271
182
  throw new _errors.RequestError(`Maximum redirect reached at: ${url.href}`);
272
183
  }
273
184
  if (url.protocol === 'https:') {
274
- options = !h2 ? await (0, _ackn.ackn)(options) : {
185
+ options = !options.h2 ? await (0, _ackn.ackn)(options) : {
275
186
  ...options,
276
187
  createConnection: null,
277
188
  protocol: url.protocol
@@ -407,17 +318,4 @@ const transform = async options => {
407
318
  };
408
319
  exports.transform = transform;
409
320
  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;
321
+ exports.unwind = unwind;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.validation = void 0;
5
+ var _nodeHttp = _interopRequireDefault(require("node:http2"));
6
+ var _constants = require("./constants");
7
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
8
+ const {
9
+ HTTP2_METHOD_GET,
10
+ HTTP2_METHOD_HEAD
11
+ } = _nodeHttp.default.constants;
12
+ const validation = (options = {}) => {
13
+ if (options.body && [HTTP2_METHOD_GET, HTTP2_METHOD_HEAD].includes(options.method)) {
14
+ throw new TypeError(`Request with ${HTTP2_METHOD_GET}/${HTTP2_METHOD_HEAD} method cannot have body.`);
15
+ }
16
+ if (!Object.values(_constants.requestCredentials).includes(options.credentials)) {
17
+ throw new TypeError(`Failed to read the 'credentials' property from 'options': The provided value '${options.credentials}' is not a valid enum value.`);
18
+ }
19
+ if (!Reflect.has(_constants.requestRedirect, options.redirect)) {
20
+ throw new TypeError(`Failed to read the 'redirect' property from 'options': The provided value '${options.redirect}' is not a valid enum value.`);
21
+ }
22
+ return options;
23
+ };
24
+ exports.validation = validation;
package/package.json CHANGED
@@ -8,14 +8,14 @@
8
8
  "url": "https://github.com/bricss/rekwest/issues"
9
9
  },
10
10
  "devDependencies": {
11
- "@babel/cli": "^7.19.3",
12
- "@babel/core": "^7.20.5",
13
- "@babel/eslint-parser": "^7.19.1",
14
- "@babel/preset-env": "^7.20.2",
15
- "c8": "^7.12.0",
16
- "eslint": "^8.29.0",
17
- "eslint-config-ultra-refined": "^2.10.0",
18
- "mocha": "^10.1.0"
11
+ "@babel/cli": "^7.21.5",
12
+ "@babel/core": "^7.21.8",
13
+ "@babel/eslint-parser": "^7.21.8",
14
+ "@babel/preset-env": "^7.21.5",
15
+ "c8": "^7.13.0",
16
+ "eslint": "^8.40.0",
17
+ "eslint-config-ultra-refined": "^2.13.0",
18
+ "mocha": "^10.2.0"
19
19
  },
20
20
  "description": "The robust request library that humanity deserves 🌐",
21
21
  "engines": {
@@ -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.2.3"
70
+ "version": "4.4.0"
71
71
  }
package/src/formdata.mjs CHANGED
@@ -25,7 +25,7 @@ export class FormData {
25
25
  const prefix = `--${ boundary }${ CRLF }${ HTTP2_HEADER_CONTENT_DISPOSITION }: form-data`;
26
26
 
27
27
  const escape = (str) => str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22');
28
- const normalize = (value) => value.replace(/\r?\n|\r/g, CRLF);
28
+ const redress = (value) => value.replace(/\r?\n|\r/g, CRLF);
29
29
 
30
30
  return {
31
31
  contentType,
@@ -35,11 +35,11 @@ export class FormData {
35
35
  for (const [name, value] of fd) {
36
36
  if (value.constructor === String) {
37
37
  yield encoder.encode(`${ prefix }; name="${
38
- escape(normalize(name))
39
- }"${ CRLF.repeat(2) }${ normalize(value) }${ CRLF }`);
38
+ escape(redress(name))
39
+ }"${ CRLF.repeat(2) }${ redress(value) }${ CRLF }`);
40
40
  } else {
41
41
  yield encoder.encode(`${ prefix }; name="${
42
- escape(normalize(name))
42
+ escape(redress(name))
43
43
  }"${ value.name ? `; filename="${ escape(value.name) }"` : '' }${ CRLF }${
44
44
  HTTP2_HEADER_CONTENT_TYPE
45
45
  }: ${
package/src/index.mjs CHANGED
@@ -9,10 +9,10 @@ import {
9
9
  admix,
10
10
  affix,
11
11
  merge,
12
- sanitize,
12
+ normalize,
13
13
  transfer,
14
- validation,
15
14
  } from './utils.mjs';
15
+ import { validation } from './validation.mjs';
16
16
 
17
17
  export { constants } from 'node:http2';
18
18
 
@@ -23,42 +23,41 @@ export * from './errors.mjs';
23
23
  export * from './file.mjs';
24
24
  export * from './formdata.mjs';
25
25
  export * as mediatypes from './mediatypes.mjs';
26
+ export * from './mixin.mjs';
26
27
  export * from './utils.mjs';
28
+ export * from './validation.mjs';
27
29
 
28
30
  const {
29
31
  HTTP2_HEADER_CONTENT_TYPE,
30
32
  } = http2.constants;
31
33
 
32
- export default function rekwest(...args) {
33
- let options = sanitize(...args);
34
-
35
- if (!options.redirected) {
36
- options = merge(rekwest.defaults, options);
37
- }
38
-
39
- return transfer(validation(options));
34
+ export default function rekwest(url, options) {
35
+ return transfer(validation(normalize(url, options)));
40
36
  }
41
37
 
38
+ Reflect.defineProperty(rekwest, 'extend', {
39
+ enumerable: true,
40
+ value(options) {
41
+ return (url, opts) => rekwest(url, merge(options, opts));
42
+ },
43
+ });
44
+
42
45
  Reflect.defineProperty(rekwest, 'stream', {
43
46
  enumerable: true,
44
- value(...args) {
45
- const options = preflight({
46
- ...validation(merge(rekwest.defaults, {
47
- headers: { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_OCTET_STREAM },
48
- }, sanitize(...args))),
47
+ value(url, options) {
48
+ options = preflight(validation(normalize(url, merge(options, {
49
+ headers: { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_OCTET_STREAM },
49
50
  redirect: requestRedirect.manual,
50
- });
51
-
52
- const { h2, url } = options;
51
+ }))));
53
52
  let client, req;
54
53
 
55
- if (h2) {
56
- client = http2.connect(url.origin, options);
54
+ if (options.h2) {
55
+ client = http2.connect(options.url.origin, options);
57
56
  req = client.request(options.headers, options);
58
57
  } else {
59
- const { request } = (url.protocol === 'http:' ? http : https);
58
+ const { request } = options.url.protocol === 'http:' ? http : https;
60
59
 
61
- req = request(url, options);
60
+ req = request(options.url, options);
62
61
  }
63
62
 
64
63
  affix(client, req, options);
@@ -66,7 +65,7 @@ Reflect.defineProperty(rekwest, 'stream', {
66
65
  req.once('response', (res) => {
67
66
  let headers;
68
67
 
69
- if (h2) {
68
+ if (options.h2) {
70
69
  headers = res;
71
70
  res = req;
72
71
  }
package/src/mixin.mjs ADDED
@@ -0,0 +1,109 @@
1
+ import { Blob } from 'node:buffer';
2
+ import http2 from 'node:http2';
3
+ import {
4
+ brandCheck,
5
+ decompress,
6
+ } from './utils.mjs';
7
+
8
+ const {
9
+ HTTP2_HEADER_CONTENT_ENCODING,
10
+ HTTP2_HEADER_CONTENT_TYPE,
11
+ } = http2.constants;
12
+
13
+ export const mixin = (res, { digest = false, parse = false } = {}) => {
14
+ if (!digest) {
15
+ Object.defineProperties(res, {
16
+ arrayBuffer: {
17
+ enumerable: true,
18
+ value: async function () {
19
+ brandCheck(this, res?.constructor);
20
+ parse &&= false;
21
+ const { buffer, byteLength, byteOffset } = await this.body();
22
+
23
+ return buffer.slice(byteOffset, byteOffset + byteLength);
24
+ },
25
+ },
26
+ blob: {
27
+ enumerable: true,
28
+ value: async function () {
29
+ brandCheck(this, res?.constructor);
30
+ const val = await this.arrayBuffer();
31
+
32
+ return new Blob([val]);
33
+ },
34
+ },
35
+ json: {
36
+ enumerable: true,
37
+ value: async function () {
38
+ brandCheck(this, res?.constructor);
39
+ const val = await this.text();
40
+
41
+ return JSON.parse(val);
42
+ },
43
+ },
44
+ text: {
45
+ enumerable: true,
46
+ value: async function () {
47
+ brandCheck(this, res?.constructor);
48
+ const blob = await this.blob();
49
+
50
+ return blob.text();
51
+ },
52
+ },
53
+ });
54
+ }
55
+
56
+ return Object.defineProperties(res, {
57
+ body: {
58
+ enumerable: true,
59
+ value: async function () {
60
+ brandCheck(this, res?.constructor);
61
+
62
+ if (this.bodyUsed) {
63
+ throw new TypeError('Response stream already read');
64
+ }
65
+
66
+ let body = [];
67
+
68
+ for await (const chunk of decompress(this, this.headers[HTTP2_HEADER_CONTENT_ENCODING])) {
69
+ body.push(chunk);
70
+ }
71
+
72
+ body = Buffer.concat(body);
73
+
74
+ if (!body.length && parse) {
75
+ return null;
76
+ }
77
+
78
+ if (body.length && parse) {
79
+ const contentType = this.headers[HTTP2_HEADER_CONTENT_TYPE] ?? '';
80
+ const charset = contentType.split(';')
81
+ .find((it) => /charset=/i.test(it))
82
+ ?.toLowerCase()
83
+ .replace('charset=', '')
84
+ .replace('iso-8859-1', 'latin1')
85
+ .trim() || 'utf-8';
86
+
87
+ if (/\bjson\b/i.test(contentType)) {
88
+ body = JSON.parse(body.toString(charset));
89
+ } else if (/\b(?:text|xml)\b/i.test(contentType)) {
90
+ if (/\b(?:latin1|ucs-2|utf-(?:8|16le))\b/i.test(charset)) {
91
+ body = body.toString(charset);
92
+ } else {
93
+ body = new TextDecoder(charset).decode(body);
94
+ }
95
+ }
96
+ }
97
+
98
+ return body;
99
+ },
100
+ writable: true,
101
+ },
102
+ bodyUsed: {
103
+ enumerable: true,
104
+ get() {
105
+ return this.readableEnded;
106
+ },
107
+ },
108
+ });
109
+ };
@@ -8,10 +8,10 @@ import {
8
8
  import { Cookies } from './cookies.mjs';
9
9
  import { RequestError } from './errors.mjs';
10
10
  import rekwest from './index.mjs';
11
+ import { mixin } from './mixin.mjs';
11
12
  import {
12
13
  admix,
13
14
  maxRetryAfterError,
14
- mixin,
15
15
  sameOrigin,
16
16
  } from './utils.mjs';
17
17
 
package/src/utils.mjs CHANGED
@@ -1,4 +1,3 @@
1
- import { Blob } from 'node:buffer';
2
1
  import http from 'node:http';
3
2
  import http2 from 'node:http2';
4
3
  import https from 'node:https';
@@ -11,10 +10,6 @@ import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
11
10
  import { types } from 'node:util';
12
11
  import zlib from 'node:zlib';
13
12
  import { ackn } from './ackn.mjs';
14
- import {
15
- requestCredentials,
16
- requestRedirect,
17
- } from './constants.mjs';
18
13
  import {
19
14
  RequestError,
20
15
  TimeoutError,
@@ -36,8 +31,6 @@ const {
36
31
  HTTP2_HEADER_CONTENT_TYPE,
37
32
  HTTP2_HEADER_RETRY_AFTER,
38
33
  HTTP2_HEADER_STATUS,
39
- HTTP2_METHOD_GET,
40
- HTTP2_METHOD_HEAD,
41
34
  } = http2.constants;
42
35
 
43
36
  export const admix = (res, headers, options) => {
@@ -178,110 +171,16 @@ export const merge = (target = {}, ...rest) => {
178
171
  return target;
179
172
  };
180
173
 
181
- export const mixin = (res, { digest = false, parse = false } = {}) => {
182
- if (!digest) {
183
- Object.defineProperties(res, {
184
- arrayBuffer: {
185
- enumerable: true,
186
- value: async function () {
187
- brandCheck(this, res?.constructor);
188
- parse &&= false;
189
- const { buffer, byteLength, byteOffset } = await this.body();
190
-
191
- return buffer.slice(byteOffset, byteOffset + byteLength);
192
- },
193
- },
194
- blob: {
195
- enumerable: true,
196
- value: async function () {
197
- brandCheck(this, res?.constructor);
198
- const val = await this.arrayBuffer();
199
-
200
- return new Blob([val]);
201
- },
202
- },
203
- json: {
204
- enumerable: true,
205
- value: async function () {
206
- brandCheck(this, res?.constructor);
207
- const val = await this.text();
208
-
209
- return JSON.parse(val);
210
- },
211
- },
212
- text: {
213
- enumerable: true,
214
- value: async function () {
215
- brandCheck(this, res?.constructor);
216
- const blob = await this.blob();
217
-
218
- return blob.text();
219
- },
220
- },
221
- });
174
+ export const normalize = (url, options = {}) => {
175
+ if (!options.redirected) {
176
+ options = merge(rekwest.defaults, options);
222
177
  }
223
178
 
224
- return Object.defineProperties(res, {
225
- body: {
226
- enumerable: true,
227
- value: async function () {
228
- brandCheck(this, res?.constructor);
229
-
230
- if (this.bodyUsed) {
231
- throw new TypeError('Response stream already read');
232
- }
233
-
234
- let body = [];
235
-
236
- for await (const chunk of decompress(this, this.headers[HTTP2_HEADER_CONTENT_ENCODING])) {
237
- body.push(chunk);
238
- }
239
-
240
- body = Buffer.concat(body);
241
-
242
- if (!body.length && parse) {
243
- return null;
244
- }
245
-
246
- if (body.length && parse) {
247
- const contentType = this.headers[HTTP2_HEADER_CONTENT_TYPE] ?? '';
248
- const charset = contentType.split(';')
249
- .find((it) => /charset=/i.test(it))
250
- ?.toLowerCase()
251
- .replace('charset=', '')
252
- .replace('iso-8859-1', 'latin1')
253
- .trim() || 'utf-8';
254
-
255
- if (/\bjson\b/i.test(contentType)) {
256
- body = JSON.parse(body.toString(charset));
257
- } else if (/\b(?:text|xml)\b/i.test(contentType)) {
258
- if (/\b(?:latin1|ucs-2|utf-(?:8|16le))\b/i.test(charset)) {
259
- body = body.toString(charset);
260
- } else {
261
- body = new TextDecoder(charset).decode(body);
262
- }
263
- }
264
- }
265
-
266
- return body;
267
- },
268
- writable: true,
269
- },
270
- bodyUsed: {
271
- enumerable: true,
272
- get() {
273
- return this.readableEnded;
274
- },
275
- },
276
- });
277
- };
278
-
279
- export const sanitize = (url, options = {}) => {
280
179
  if (options.trimTrailingSlashes) {
281
180
  url = `${ url }`.replace(/(?<!:)\/+/g, '/');
282
181
  }
283
182
 
284
- url = new URL(url);
183
+ url = new URL(url, options.baseURL);
285
184
 
286
185
  return Object.assign(options, { url });
287
186
  };
@@ -299,14 +198,14 @@ export async function* tap(value) {
299
198
  }
300
199
 
301
200
  export const transfer = async (options) => {
302
- const { digest, h2, redirected, thenable, url } = options;
201
+ const { digest, redirected, thenable, url } = options;
303
202
 
304
203
  if (options.follow === 0) {
305
204
  throw new RequestError(`Maximum redirect reached at: ${ url.href }`);
306
205
  }
307
206
 
308
207
  if (url.protocol === 'https:') {
309
- options = !h2 ? await ackn(options) : {
208
+ options = !options.h2 ? await ackn(options) : {
310
209
  ...options,
311
210
  createConnection: null,
312
211
  protocol: url.protocol,
@@ -334,7 +233,7 @@ export const transfer = async (options) => {
334
233
  client = http2.connect(url.origin, options);
335
234
  req = client.request(options.headers, options);
336
235
  } else {
337
- const { request } = (url.protocol === 'http:' ? http : https);
236
+ const { request } = url.protocol === 'http:' ? http : https;
338
237
 
339
238
  req = request(url, options);
340
239
  }
@@ -452,26 +351,3 @@ export const transform = async (options) => {
452
351
  };
453
352
 
454
353
  export const unwind = (encodings) => encodings.split(',').map((it) => it.trim());
455
-
456
- export const validation = (options = {}) => {
457
- if (options.body && [
458
- HTTP2_METHOD_GET,
459
- HTTP2_METHOD_HEAD,
460
- ].includes(options.method)) {
461
- throw new TypeError(`Request with ${ HTTP2_METHOD_GET }/${ HTTP2_METHOD_HEAD } method cannot have body.`);
462
- }
463
-
464
- if (!Object.values(requestCredentials).includes(options.credentials)) {
465
- throw new TypeError(`Failed to read the 'credentials' property from 'options': The provided value '${
466
- options.credentials
467
- }' is not a valid enum value.`);
468
- }
469
-
470
- if (!Reflect.has(requestRedirect, options.redirect)) {
471
- throw new TypeError(`Failed to read the 'redirect' property from 'options': The provided value '${
472
- options.redirect
473
- }' is not a valid enum value.`);
474
- }
475
-
476
- return options;
477
- };
@@ -0,0 +1,33 @@
1
+ import http2 from 'node:http2';
2
+ import {
3
+ requestCredentials,
4
+ requestRedirect,
5
+ } from './constants.mjs';
6
+
7
+ const {
8
+ HTTP2_METHOD_GET,
9
+ HTTP2_METHOD_HEAD,
10
+ } = http2.constants;
11
+
12
+ export const validation = (options = {}) => {
13
+ if (options.body && [
14
+ HTTP2_METHOD_GET,
15
+ HTTP2_METHOD_HEAD,
16
+ ].includes(options.method)) {
17
+ throw new TypeError(`Request with ${ HTTP2_METHOD_GET }/${ HTTP2_METHOD_HEAD } method cannot have body.`);
18
+ }
19
+
20
+ if (!Object.values(requestCredentials).includes(options.credentials)) {
21
+ throw new TypeError(`Failed to read the 'credentials' property from 'options': The provided value '${
22
+ options.credentials
23
+ }' is not a valid enum value.`);
24
+ }
25
+
26
+ if (!Reflect.has(requestRedirect, options.redirect)) {
27
+ throw new TypeError(`Failed to read the 'redirect' property from 'options': The provided value '${
28
+ options.redirect
29
+ }' is not a valid enum value.`);
30
+ }
31
+
32
+ return options;
33
+ };