rekwest 7.2.7 → 8.1.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
@@ -1,260 +1,264 @@
1
- The robust request library that humanity deserves 🌐
2
- ---
3
- This package provides highly likely functional and **easy-to-use** abstraction atop of
4
- native [http(s).request](https://nodejs.org/api/https.html#httpsrequesturl-options-callback)
5
- and [http2.request](https://nodejs.org/api/http2.html#clienthttp2sessionrequestheaders-options).
6
-
7
- ## Abstract
8
-
9
- * Fetch-alike 🥏
10
- * Cool-beans 🫐 config options (with defaults) 📋
11
- * Automatic HTTP/2 support (ALPN negotiation) 💼
12
- * Automatic or opt-in body parse (with non-UTF-8 charset decoding) 🉑
13
- * Automatic and simplistic `Cookies` treatment (with **TTL** support) 🍪
14
- * Automatic body decoding (and opt-in request body encoding) 🗜️
15
- * Better error management 🚥
16
- * Built-in streamable `FormData` interface 🔌
17
- * Support redirects & retries with fine-grained tune-ups 🪛
18
- * Support plenty request body types (include blobs & streams) 📦
19
- * Support both CJS and ESM module systems 🧩
20
- * Fully promise-able and pipe-able 🌀
21
- * Zero dependencies 🗽
22
-
23
- ## Prerequisites
24
-
25
- * Node.js `>=20.0.0`
26
-
27
- ## Installation
28
-
29
- ```bash
30
- npm install rekwest --save
31
- ```
32
-
33
- ### Usage
34
-
35
- ```javascript
36
- import rekwest, { constants } from 'rekwest';
37
-
38
- const {
39
- HTTP2_HEADER_AUTHORIZATION,
40
- HTTP2_HEADER_CONTENT_ENCODING,
41
- HTTP2_METHOD_POST,
42
- HTTP_STATUS_OK,
43
- } = constants;
44
-
45
- const url = 'https://somewhe.re/somewhat/endpoint';
46
-
47
- const res = await rekwest(url, {
48
- body: { celestial: 'payload' },
49
- headers: {
50
- [HTTP2_HEADER_AUTHORIZATION]: 'Bearer [token]',
51
- [HTTP2_HEADER_CONTENT_ENCODING]: 'br', // Enables: body encoding
52
- /**
53
- * [HTTP2_HEADER_CONTENT_TYPE] is undue for
54
- * Array/Blob/File/FormData/Object/URLSearchParams body types
55
- * and will be set automatically, with an option to override it here.
56
- */
57
- },
58
- method: HTTP2_METHOD_POST,
59
- });
60
-
61
- console.assert(res.statusCode === HTTP_STATUS_OK);
62
- console.info(res.headers);
63
- console.log(res.body);
64
- ```
65
-
66
- ---
67
-
68
- ```javascript
69
- import { Readable } from 'node:stream';
70
- import rekwest, {
71
- constants,
72
- FormData,
73
- } from 'rekwest';
74
-
75
- const {
76
- HTTP2_HEADER_AUTHORIZATION,
77
- HTTP2_HEADER_CONTENT_ENCODING,
78
- HTTP2_METHOD_POST,
79
- HTTP_STATUS_OK,
80
- } = constants;
81
-
82
- const blob = new Blob(['bits']);
83
- const file = new File(['bits'], 'file.xyz');
84
- const rbl = Readable.from('bits');
85
- const rds = ReadableStream.from('bits');
86
-
87
- const fd = new FormData({
88
- aux: new Date(), // Either [[key, value]] or kv sequenceable
89
- });
90
-
91
- fd.append('celestial', 'payload');
92
- fd.append('blob', blob, 'blob.xyz');
93
- fd.append('file', file);
94
- fd.append('rbl', rbl, 'rbl.xyz');
95
- fd.append('rds', rds, 'rds.xyz');
96
- /**
97
- * Streamable entries are consumed on request submittion.
98
- */
99
-
100
- const url = 'https://somewhe.re/somewhat/endpoint';
101
-
102
- const res = await rekwest(url, {
103
- body: fd,
104
- headers: {
105
- [HTTP2_HEADER_AUTHORIZATION]: 'Bearer [token]',
106
- [HTTP2_HEADER_CONTENT_ENCODING]: 'zstd', // Enables: body encoding
107
- },
108
- method: HTTP2_METHOD_POST,
109
- });
110
-
111
- console.assert(res.statusCode === HTTP_STATUS_OK);
112
- console.info(res.headers);
113
- console.log(res.body);
114
- ```
115
-
116
- ### API
117
-
118
- #### `rekwest(url[, options])`
119
-
120
- * `url` **{string | URL}** The URL to send the request to.
121
- * `options` **{Object}**
122
- Extends [http(s).RequestOptions](https://nodejs.org/api/https.html#httpsrequesturl-options-callback) along with
123
- extra [http2.ClientSessionOptions](https://nodejs.org/api/http2.html#http2connectauthority-options-listener)
124
- & [http2.ClientSessionRequestOptions](https://nodejs.org/api/http2.html#clienthttp2sessionrequestheaders-options)
125
- and [tls.ConnectionOptions](https://nodejs.org/api/tls.html#tlsconnectoptions-callback)
126
- for HTTP/2 attunes.
127
- * `allowDowngrade` **{boolean}** `Default: false` Controls whether `https:` redirects to `http:` are allowed.
128
- * `baseURL` **{string | URL}** The base URL to use in cases where `url` is a relative URL.
129
- * `body` **{string | Array | ArrayBuffer | ArrayBufferView | AsyncIterator | Blob | Buffer | DataView | File |
130
- FormData | Iterator | Object | Readable | ReadableStream | SharedArrayBuffer | URLSearchParams}** The body to send
131
- with the request.
132
- * `bufferBody` **{boolean}** `Default: false` Toggles the buffering of the streamable request bodies for redirects and
133
- retries.
134
- * `cookies` **{boolean | string | string[] | [k, v][] | Cookies | Object | URLSearchParams}** `Default: true` The
135
- cookies to add to the request. Manually set `cookie` header to override.
136
- * `cookiesTTL` **{boolean}** `Default: false` Controls enablement of TTL for the cookies cache.
137
- * `credentials` **{include | omit | same-origin}** `Default: same-origin` Controls credentials in case of cross-origin
138
- redirects.
139
- * `decodersOptions` **{Object}** Configures decoders options, e.g.: `brotli`, `zlib`, `zstd`.
140
- * `digest` **{boolean}** `Default: true` Controls whether to read the response stream or add a mixin.
141
- * `encodersOptions` **{Object}** Configures encoders options, e.g.: `brotli`, `zlib`, `zstd`.
142
- * `follow` **{number}** `Default: 20` The number of redirects to follow.
143
- * `h2` **{boolean}** `Default: false` Forces the use of HTTP/2 protocol.
144
- * `headers` **{Object}** The headers to add to the request.
145
- * `params` **{Object}** The search params to add to the `url`.
146
- * `parse` **{boolean}** `Default: true` Controls whether to parse response body or return a buffer.
147
- * `redirect` **{error | follow | manual}** `Default: follow` Controls the redirect flows.
148
- * `retry` **{Object}** Represents the retry options.
149
- * `attempts` **{number}** `Default: 0` The number of retry attempts.
150
- * `backoffStrategy` **{string}** `Default: interval * Math.log(Math.random() * (Math.E * Math.E - Math.E) + Math.E)`
151
- The backoff strategy uses a log-uniform algorithm. To fix the interval, set the value to `interval * 1`.
152
- * `errorCodes` **{string[]}**
153
- `Default: ['ECONNREFUSED', 'ECONNRESET', 'EHOSTDOWN', 'EHOSTUNREACH', 'ENETDOWN', 'ENETUNREACH', 'ENOTFOUND', 'ERR_HTTP2_STREAM_ERROR']`
154
- The list of error codes to retry on.
155
- * `interval` **{number}** `Default: 1e3` The initial retry interval.
156
- * `maxRetryAfter` **{number}** `Default: 3e5` The maximum `retry-after` limit in milliseconds.
157
- * `retryAfter` **{boolean}** `Default: true` Controls `retry-after` header receptiveness.
158
- * `statusCodes` **{number[]}** `Default: [429, 500, 502, 503, 504]` The list of status codes to retry on.
159
- * `stripTrailingSlash` **{boolean}** `Default: false` Controls whether to strip trailing slash at the end of the URL.
160
- * `thenable` **{boolean}** `Default: false` Controls the promise resolutions.
161
- * `timeout` **{number}** `Default: 3e5` The number of milliseconds a request can take before termination.
162
- * `trimTrailingSlashes` **{boolean}** `Default: false` Controls whether to trim trailing slashes within the URL.
163
- * **Returns:** Promise that resolves to
164
- extended [http.IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage)
165
- or [http2.ClientHttp2Stream](https://nodejs.org/api/http2.html#class-clienthttp2stream) which is respectively
166
- readable and duplex streams.
167
- * if `digest: true` & `parse: true`
168
- * `body` **{string | Array | Buffer | Object}** The body based on its content type.
169
- * if `digest: false`
170
- * `arrayBuffer` **{AsyncFunction}** Reads the response and returns **ArrayBuffer**.
171
- * `blob` **{AsyncFunction}** Reads the response and returns **Blob**.
172
- * `body` **{AsyncFunction}** Reads the response and returns **Buffer** if `parse: false`.
173
- * `bytes` **{AsyncFunction}** Reads the response and returns **Uint8Array**.
174
- * `json` **{AsyncFunction}** Reads the response and returns **Object**.
175
- * `text` **{AsyncFunction}** Reads the response and returns **String**.
176
- * `bodyUsed` **{boolean}** Indicates whether the response was read or not.
177
- * `cookies` **{undefined | Cookies}** The cookies sent and received with the response.
178
- * `headers` **{Object}** The headers received with the response.
179
- * `httpVersion` **{string}** Indicates a protocol version negotiated with the server.
180
- * `ok` **{boolean}** Indicates if the response was successful (statusCode: **200-299**).
181
- * `redirected` **{boolean}** Indicates if the response is the result of a redirect.
182
- * `statusCode` **{number}** Indicates the status code of the response.
183
- * `trailers` **{undefined | Object}** The trailer headers received with the response.
184
-
185
- ---
186
-
187
- #### `rekwest.defaults`
188
-
189
- The object to fulfill with default [options](#rekwesturl-options).
190
-
191
- ---
192
-
193
- #### `rekwest.extend(options)`
194
-
195
- The method to extend default [options](#rekwesturl-options) per instance.
196
-
197
- ```javascript
198
- import rekwest, { constants } from 'rekwest';
199
-
200
- const {
201
- HTTP_STATUS_OK,
202
- } = constants;
203
-
204
- const rk = rekwest.extend({
205
- baseURL: 'https://somewhe.re',
206
- });
207
-
208
- const params = {
209
- id: '[uid]',
210
- signature: '[code]',
211
- variant: '[any]',
212
- };
213
- const signal = AbortSignal.timeout(3e4);
214
- const url = '/somewhat/endpoint';
215
-
216
- const res = await rk(url, {
217
- params,
218
- signal,
219
- });
220
-
221
- console.assert(res.statusCode === HTTP_STATUS_OK);
222
- console.info(res.headers);
223
- console.log(res.body);
224
- ```
225
-
226
- ---
227
-
228
- #### `rekwest.stream(url[, options])`
229
-
230
- The method with limited functionality to use with streams and/or pipes.
231
-
232
- * No automata (redirects & retries).
233
- * Pass `h2: true` in options to use HTTP/2 protocol.
234
- * Use `ackn({ url: URL })` method in advance to check the available protocols.
235
-
236
- ```javascript
237
- import fs from 'node:fs';
238
- import { pipeline } from 'node:stream/promises';
239
- import rekwest, {
240
- ackn,
241
- constants,
242
- } from 'rekwest';
243
-
244
- const {
245
- HTTP2_METHOD_POST,
246
- } = constants;
247
-
248
- const url = new URL('https://somewhe.re/somewhat/endpoint');
249
- const options = await ackn({ url });
250
-
251
- await pipeline(
252
- fs.createReadStream('/path/to/read/inlet.xyz'),
253
- rekwest.stream(url, { ...options, method: HTTP2_METHOD_POST }),
254
- fs.createWriteStream('/path/to/write/outlet.xyz'),
255
- );
256
- ```
257
-
258
- ---
259
-
260
- For more details, please check tests (coverage: **>97%**) in the repository.
1
+ The robust request library that humanity deserves 🌐
2
+ ---
3
+ This package provides highly likely functional and **easy-to-use** abstraction atop of
4
+ native [http(s).request](https://nodejs.org/api/https.html#httpsrequesturl-options-callback)
5
+ and [http2.request](https://nodejs.org/api/http2.html#clienthttp2sessionrequestheaders-options).
6
+
7
+ ## Abstract
8
+
9
+ * Fetch-alike 🥏
10
+ * Cool-beans 🫛 config options (with defaults) 📋
11
+ * Automatic HTTP/2 support (ALPN negotiation) 💼
12
+ * Automatic or opt-in body parse (with non-UTF-8 charset decoding) 🉑
13
+ * Automatic and simplistic `Cookies` treatment (with **TTL** support) 🍪
14
+ * Automatic body decoding (and opt-in request body encoding) 🗜️
15
+ * Better error management 🚥
16
+ * Built-in streamable `FormData` interface 🔌
17
+ * Support certificate pinning for enhanced TLS security 🛡️
18
+ * Support redirects & retries with fine-grained tune-ups 🪛
19
+ * Support plenty request body types (include blobs & streams) 📦
20
+ * Support both CJS and ESM module systems 🧩
21
+ * Fully promise-able ⏳ and pipe-able 🌀
22
+ * Zero dependencies 🗽
23
+
24
+ ## Prerequisites
25
+
26
+ * Node.js `>=22.0.0`
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install rekwest --save
32
+ ```
33
+
34
+ ### Usage
35
+
36
+ ```javascript
37
+ import rekwest, { constants } from 'rekwest';
38
+
39
+ const {
40
+ HTTP2_HEADER_AUTHORIZATION,
41
+ HTTP2_HEADER_CONTENT_ENCODING,
42
+ HTTP2_METHOD_POST,
43
+ HTTP_STATUS_OK,
44
+ } = constants;
45
+
46
+ const url = 'https://somewhe.re/somewhat/endpoint';
47
+
48
+ const res = await rekwest(url, {
49
+ body: { celestial: 'payload' },
50
+ headers: {
51
+ [HTTP2_HEADER_AUTHORIZATION]: 'Bearer [token]',
52
+ [HTTP2_HEADER_CONTENT_ENCODING]: 'br', // Enables: body encoding
53
+ /**
54
+ * [HTTP2_HEADER_CONTENT_TYPE] is undue for
55
+ * Array/Blob/File/FormData/Object/URLSearchParams body types
56
+ * and will be set automatically, with an option to override it here.
57
+ */
58
+ },
59
+ method: HTTP2_METHOD_POST,
60
+ });
61
+
62
+ console.assert(res.statusCode === HTTP_STATUS_OK);
63
+ console.info(res.headers);
64
+ console.log(res.body);
65
+ ```
66
+
67
+ ---
68
+
69
+ ```javascript
70
+ import { Readable } from 'node:stream';
71
+ import rekwest, {
72
+ constants,
73
+ FormData,
74
+ } from 'rekwest';
75
+
76
+ const {
77
+ HTTP2_HEADER_AUTHORIZATION,
78
+ HTTP2_HEADER_CONTENT_ENCODING,
79
+ HTTP2_METHOD_POST,
80
+ HTTP_STATUS_OK,
81
+ } = constants;
82
+
83
+ const blob = new Blob(['bits']);
84
+ const file = new File(['bits'], 'file.xyz');
85
+ const rbl = Readable.from('bits');
86
+ const rds = ReadableStream.from('bits');
87
+
88
+ const fd = new FormData({
89
+ aux: new Date(), // Either [[key, value]] or kv sequenceable
90
+ });
91
+
92
+ fd.append('celestial', 'payload');
93
+ fd.append('blob', blob, 'blob.xyz');
94
+ fd.append('file', file);
95
+ fd.append('rbl', rbl, 'rbl.xyz');
96
+ fd.append('rds', rds, 'rds.xyz');
97
+ /**
98
+ * Streamable entries are consumed on request submittion.
99
+ */
100
+
101
+ const url = 'https://somewhe.re/somewhat/endpoint';
102
+
103
+ const res = await rekwest(url, {
104
+ body: fd,
105
+ headers: {
106
+ [HTTP2_HEADER_AUTHORIZATION]: 'Bearer [token]',
107
+ [HTTP2_HEADER_CONTENT_ENCODING]: 'zstd', // Enables: body encoding
108
+ },
109
+ method: HTTP2_METHOD_POST,
110
+ });
111
+
112
+ console.assert(res.statusCode === HTTP_STATUS_OK);
113
+ console.info(res.headers);
114
+ console.log(res.body);
115
+ ```
116
+
117
+ ### API
118
+
119
+ #### `rekwest(url[, options])`
120
+
121
+ * `url` **{string | URL}** The URL to send the request to.
122
+ * `options` **{Object}**
123
+ Extends [http(s).RequestOptions](https://nodejs.org/api/https.html#httpsrequesturl-options-callback) along with
124
+ extra [http2.ClientSessionOptions](https://nodejs.org/api/http2.html#http2connectauthority-options-listener)
125
+ & [http2.ClientSessionRequestOptions](https://nodejs.org/api/http2.html#clienthttp2sessionrequestheaders-options)
126
+ and [tls.ConnectionOptions](https://nodejs.org/api/tls.html#tlsconnectoptions-callback)
127
+ for HTTP/2 attunes.
128
+ * `allowDowngrade` **{boolean}** `Default: false` Controls whether `https:` redirects to `http:` are allowed.
129
+ * `baseURL` **{string | URL}** The base URL to use in cases where `url` is a relative URL.
130
+ * `body` **{string | Array | ArrayBuffer | ArrayBufferView | AsyncIterator | Blob | Buffer | DataView | File |
131
+ FormData | Iterator | Object | Readable | ReadableStream | SharedArrayBuffer | URLSearchParams}** The body to send
132
+ with the request.
133
+ * `bufferBody` **{boolean}** `Default: false` Toggles the buffering of the streamable request bodies for redirects and
134
+ retries.
135
+ * `certPins` **{string[]}** `Default: []` A list of SHA-256 certificate fingerprints (colon-separated hex) used for
136
+ HTTPS certificate pinning. If the list is non-empty and none of the fingerprints match, the connection will be
137
+ rejected.
138
+ * `cookies` **{boolean | string | string[] | [k, v][] | Cookies | Object | URLSearchParams}** `Default: true` The
139
+ cookies to add to the request. Manually set `cookie` header to override.
140
+ * `cookiesTTL` **{boolean}** `Default: false` Controls enablement of TTL for the cookie cache.
141
+ * `credentials` **{include | omit | same-origin}** `Default: same-origin` Controls credentials in case of cross-origin
142
+ redirects.
143
+ * `decodersOptions` **{Object}** Configures decoder options, e.g.: `brotli`, `zlib`, `zstd`.
144
+ * `digest` **{boolean}** `Default: true` Controls whether to read the response stream or add a mixin.
145
+ * `encodersOptions` **{Object}** Configures encoder options, e.g.: `brotli`, `zlib`, `zstd`.
146
+ * `follow` **{number}** `Default: 20` The number of redirects to follow.
147
+ * `h2` **{boolean}** `Default: false` Forces the use of HTTP/2 protocol.
148
+ * `headers` **{Object}** The headers to add to the request.
149
+ * `params` **{Object}** The search params to add to the `url`.
150
+ * `parse` **{boolean}** `Default: true` Controls whether to parse response body or return a buffer.
151
+ * `redirect` **{error | follow | manual}** `Default: follow` Controls the redirect flows.
152
+ * `retry` **{Object}** Represents the retry options.
153
+ * `attempts` **{number}** `Default: 0` The number of retry attempts.
154
+ * `backoffStrategy` **{string}** `Default: interval * Math.log(Math.random() * (Math.E * Math.E - Math.E) + Math.E)`
155
+ The backoff strategy uses a log-uniform algorithm. To fix the interval, set the value to `interval * 1`.
156
+ * `errorCodes` **{string[]}**
157
+ `Default: ['ECONNREFUSED', 'ECONNRESET', 'EHOSTDOWN', 'EHOSTUNREACH', 'ENETDOWN', 'ENETUNREACH', 'ENOTFOUND', 'ERR_HTTP2_STREAM_ERROR']`
158
+ The list of error codes to retry on.
159
+ * `interval` **{number}** `Default: 1e3` The initial retry interval.
160
+ * `maxRetryAfter` **{number}** `Default: 3e5` The maximum `retry-after` limit in milliseconds.
161
+ * `retryAfter` **{boolean}** `Default: true` Controls `retry-after` header receptiveness.
162
+ * `statusCodes` **{number[]}** `Default: [429, 500, 502, 503, 504]` The list of status codes to retry on.
163
+ * `stripTrailingSlash` **{boolean}** `Default: false` Controls whether to strip trailing slash at the end of the URL.
164
+ * `thenable` **{boolean}** `Default: false` Controls the promise resolutions.
165
+ * `timeout` **{number}** `Default: 3e5` The number of milliseconds a request can take before termination.
166
+ * `trimTrailingSlashes` **{boolean}** `Default: false` Controls whether to trim trailing slashes within the URL.
167
+ * **Returns:** Promise that resolves to
168
+ extended [http.IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage)
169
+ or [http2.ClientHttp2Stream](https://nodejs.org/api/http2.html#class-clienthttp2stream) which are respectively
170
+ readable and duplex streams.
171
+ * if `digest: true` & `parse: true`
172
+ * `body` **{string | Array | Buffer | Object}** The body based on its content type.
173
+ * if `digest: false`
174
+ * `arrayBuffer` **{AsyncFunction}** Reads the response and returns **ArrayBuffer**.
175
+ * `blob` **{AsyncFunction}** Reads the response and returns **Blob**.
176
+ * `body` **{AsyncFunction}** Reads the response and returns **Buffer** if `parse: false`.
177
+ * `bytes` **{AsyncFunction}** Reads the response and returns **Uint8Array**.
178
+ * `json` **{AsyncFunction}** Reads the response and returns **Object**.
179
+ * `text` **{AsyncFunction}** Reads the response and returns **String**.
180
+ * `bodyUsed` **{boolean}** Indicates whether the response was read or not.
181
+ * `cookies` **{undefined | Cookies}** The cookies sent and received with the response.
182
+ * `headers` **{Object}** The headers received with the response.
183
+ * `httpVersion` **{string}** Indicates a protocol version negotiated with the server.
184
+ * `ok` **{boolean}** Indicates if the response was successful (statusCode: **200-299**).
185
+ * `redirected` **{boolean}** Indicates if the response is the result of a redirect.
186
+ * `statusCode` **{number}** Indicates the status code of the response.
187
+ * `trailers` **{undefined | Object}** The trailer headers received with the response.
188
+
189
+ ---
190
+
191
+ #### `rekwest.defaults`
192
+
193
+ The object to fulfill with default [options](#rekwesturl-options).
194
+
195
+ ---
196
+
197
+ #### `rekwest.extend(options)`
198
+
199
+ The method to extend default [options](#rekwesturl-options) per instance.
200
+
201
+ ```javascript
202
+ import rekwest, { constants } from 'rekwest';
203
+
204
+ const {
205
+ HTTP_STATUS_OK,
206
+ } = constants;
207
+
208
+ const rk = rekwest.extend({
209
+ baseURL: 'https://somewhe.re',
210
+ });
211
+
212
+ const params = {
213
+ id: '[uid]',
214
+ signature: '[code]',
215
+ variant: '[any]',
216
+ };
217
+ const signal = AbortSignal.timeout(3e4);
218
+ const url = '/somewhat/endpoint';
219
+
220
+ const res = await rk(url, {
221
+ params,
222
+ signal,
223
+ });
224
+
225
+ console.assert(res.statusCode === HTTP_STATUS_OK);
226
+ console.info(res.headers);
227
+ console.log(res.body);
228
+ ```
229
+
230
+ ---
231
+
232
+ #### `rekwest.stream(url[, options])`
233
+
234
+ The method with limited functionality to use with streams and/or pipes.
235
+
236
+ * No automata (redirects & retries).
237
+ * Pass `h2: true` in options to use HTTP/2 protocol.
238
+ * Use `ackn({ url: URL })` method in advance to check the available protocols.
239
+
240
+ ```javascript
241
+ import fs from 'node:fs';
242
+ import { pipeline } from 'node:stream/promises';
243
+ import rekwest, {
244
+ ackn,
245
+ constants,
246
+ } from 'rekwest';
247
+
248
+ const {
249
+ HTTP2_METHOD_POST,
250
+ } = constants;
251
+
252
+ const url = new URL('https://somewhe.re/somewhat/endpoint');
253
+ const options = await ackn({ url });
254
+
255
+ await pipeline(
256
+ fs.createReadStream('/path/to/read/inlet.xyz'),
257
+ rekwest.stream(url, { ...options, method: HTTP2_METHOD_POST }),
258
+ fs.createWriteStream('/path/to/write/outlet.xyz'),
259
+ );
260
+ ```
261
+
262
+ ---
263
+
264
+ For more details, please check tests (coverage: **>97%**) in the repository.
package/dist/ackn.cjs CHANGED
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.ackn = void 0;
7
7
  var _nodeTls = require("node:tls");
8
+ var _errors = require("./errors.cjs");
8
9
  const ackn = (options = {}) => new Promise((resolve, reject) => {
9
10
  const url = new URL(options.url);
10
11
  const socket = (0, _nodeTls.connect)({
@@ -14,6 +15,14 @@ const ackn = (options = {}) => new Promise((resolve, reject) => {
14
15
  port: parseInt(url.port, 10) || 443,
15
16
  servername: url.hostname
16
17
  }, () => {
18
+ const cert = socket.getPeerCertificate();
19
+ if (options.certPins?.length) {
20
+ const fp = cert.fingerprint256;
21
+ if (!options.certPins.includes(fp) && !socket.isSessionReused()) {
22
+ socket.destroy();
23
+ return reject(new _errors.RequestError(`Certificate pins mismatch for ${url.hostname}`));
24
+ }
25
+ }
17
26
  socket.off('error', reject);
18
27
  socket.off('timeout', reject);
19
28
  const {
package/dist/config.cjs CHANGED
@@ -25,6 +25,7 @@ const timeout = 3e5;
25
25
  const defaults = {
26
26
  allowDowngrade: false,
27
27
  bufferBody: false,
28
+ certPins: [],
28
29
  cookiesTTL: false,
29
30
  credentials: _constants.requestCredentials.sameOrigin,
30
31
  decodersOptions: {},
@@ -46,7 +47,7 @@ const defaults = {
46
47
  headers: {
47
48
  [HTTP2_HEADER_ACCEPT]: `${_mediatypes.APPLICATION_JSON}, ${_mediatypes.TEXT_PLAIN}, ${_mediatypes.WILDCARD}`,
48
49
  [HTTP2_HEADER_ACCEPT_ENCODING]: `br,${isZstdSupported ? ' zstd, ' : ' '}gzip, deflate, deflate-raw`,
49
- [HTTP2_HEADER_USER_AGENT]: `node/${process.versions.node} ${process.platform}/${process.arch}`
50
+ [HTTP2_HEADER_USER_AGENT]: navigator.userAgent
50
51
  },
51
52
  method: HTTP2_METHOD_GET,
52
53
  parse: true,
package/dist/cookies.cjs CHANGED
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.splitCookie = exports.maxCookieSize = exports.maxCookieLifetimeCap = exports.isValidCookie = exports.illegalCookieChars = exports.cookieRex = exports.cookiePairRex = exports.Cookies = void 0;
7
7
  var _utils = require("./utils.cjs");
8
8
  const cookieRex = exports.cookieRex = /^[^=]+=(?:"[^"]*"|[^\p{Control};]*)(?:;\s*(?:[^=]+=(?:"[^"]*"|[^\p{Control};]*)|[^=]+))*$/u;
9
- const cookiePairRex = exports.cookiePairRex = /[^;\s]+=(?:"[^"]*"|[^;\s]*)/g;
9
+ const cookiePairRex = exports.cookiePairRex = /[^;\s]+=(?:"[^"]*"|[^;]*)/g;
10
10
  const illegalCookieChars = exports.illegalCookieChars = /\p{Control}/u;
11
11
  const isValidCookie = str => str?.constructor === String && cookieRex.test(str);
12
12
  exports.isValidCookie = isValidCookie;
package/dist/transfer.cjs CHANGED
@@ -26,11 +26,7 @@ const transfer = async options => {
26
26
  throw new _errors.RequestError(`Maximum redirect reached at: ${url.href}`);
27
27
  }
28
28
  if (url.protocol === 'https:') {
29
- options = !options.h2 ? await (0, _ackn.ackn)(options) : {
30
- ...options,
31
- createConnection: null,
32
- protocol: url.protocol
33
- };
29
+ options = await (0, _ackn.ackn)(options);
34
30
  } else if (Reflect.has(options, 'alpnProtocol')) {
35
31
  for (const it of ['alpnProtocol', 'createConnection', 'h2', 'protocol']) {
36
32
  Reflect.deleteProperty(options, it);
package/dist/utils.cjs CHANGED
@@ -75,6 +75,10 @@ const deepMerge = (target, ...rest) => {
75
75
  for (const key of Object.getOwnPropertyNames(source)) {
76
76
  const sv = source[key];
77
77
  const tv = target[key];
78
+ if (sv instanceof Function) {
79
+ target[key] = source[key];
80
+ continue;
81
+ }
78
82
  if (Object(sv) === sv && Object(tv) === tv) {
79
83
  target[key] = deepMerge(tv, sv);
80
84
  continue;