rekwest 7.2.3 → 7.2.5
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 +63 -58
- package/dist/ackn.cjs +3 -3
- package/dist/cookies.cjs +45 -26
- package/dist/formdata.cjs +59 -60
- package/dist/preflight.cjs +1 -1
- package/dist/retries.cjs +2 -2
- package/dist/transfer.cjs +3 -5
- package/dist/transform.cjs +3 -3
- package/dist/utils.cjs +41 -38
- package/package.json +6 -5
- package/src/ackn.js +3 -3
- package/src/cookies.js +47 -30
- package/src/formdata.js +80 -89
- package/src/preflight.js +1 -1
- package/src/retries.js +2 -2
- package/src/transfer.js +1 -5
- package/src/transform.js +8 -5
- package/src/utils.js +36 -47
package/README.md
CHANGED
|
@@ -48,10 +48,11 @@ const res = await rekwest(url, {
|
|
|
48
48
|
body: { celestial: 'payload' },
|
|
49
49
|
headers: {
|
|
50
50
|
[HTTP2_HEADER_AUTHORIZATION]: 'Bearer [token]',
|
|
51
|
-
[HTTP2_HEADER_CONTENT_ENCODING]: 'br',
|
|
52
|
-
/**
|
|
51
|
+
[HTTP2_HEADER_CONTENT_ENCODING]: 'br', // Enables: body encoding
|
|
52
|
+
/**
|
|
53
|
+
* [HTTP2_HEADER_CONTENT_TYPE] is undue for
|
|
53
54
|
* Array/Blob/File/FormData/Object/URLSearchParams body types
|
|
54
|
-
* and will be set automatically, with an option to override it here
|
|
55
|
+
* and will be set automatically, with an option to override it here.
|
|
55
56
|
*/
|
|
56
57
|
},
|
|
57
58
|
method: HTTP2_METHOD_POST,
|
|
@@ -82,16 +83,21 @@ const {
|
|
|
82
83
|
|
|
83
84
|
const blob = new Blob(['bits']);
|
|
84
85
|
const file = new File(['bits'], 'file.xyz');
|
|
85
|
-
const
|
|
86
|
+
const rbl = Readable.from('bits');
|
|
87
|
+
const rds = ReadableStream.from('bits');
|
|
86
88
|
|
|
87
89
|
const fd = new FormData({
|
|
88
|
-
aux: Date
|
|
90
|
+
aux: new Date(), // Either [[key, value]] or kv sequenceable
|
|
89
91
|
});
|
|
90
92
|
|
|
91
93
|
fd.append('celestial', 'payload');
|
|
92
94
|
fd.append('blob', blob, 'blob.xyz');
|
|
93
95
|
fd.append('file', file);
|
|
94
|
-
fd.append('
|
|
96
|
+
fd.append('rbl', rbl, 'rbl.xyz');
|
|
97
|
+
fd.append('rds', rds, 'rds.xyz');
|
|
98
|
+
/**
|
|
99
|
+
* Streamable entries are consumed on request submittion.
|
|
100
|
+
*/
|
|
95
101
|
|
|
96
102
|
const url = 'https://somewhe.re/somewhat/endpoint';
|
|
97
103
|
|
|
@@ -99,7 +105,7 @@ const res = await rekwest(url, {
|
|
|
99
105
|
body: fd,
|
|
100
106
|
headers: {
|
|
101
107
|
[HTTP2_HEADER_AUTHORIZATION]: 'Bearer [token]',
|
|
102
|
-
[HTTP2_HEADER_CONTENT_ENCODING]: 'zstd',
|
|
108
|
+
[HTTP2_HEADER_CONTENT_ENCODING]: 'zstd', // Enables: body encoding
|
|
103
109
|
},
|
|
104
110
|
method: HTTP2_METHOD_POST,
|
|
105
111
|
});
|
|
@@ -113,71 +119,70 @@ console.log(res.body);
|
|
|
113
119
|
|
|
114
120
|
#### `rekwest(url[, options])`
|
|
115
121
|
|
|
116
|
-
* `url` **{string | URL}** The URL to send the request to
|
|
122
|
+
* `url` **{string | URL}** The URL to send the request to.
|
|
117
123
|
* `options` **{Object}**
|
|
118
124
|
Extends [http(s).RequestOptions](https://nodejs.org/api/https.html#httpsrequesturl-options-callback) along with
|
|
119
125
|
extra [http2.ClientSessionOptions](https://nodejs.org/api/http2.html#http2connectauthority-options-listener)
|
|
120
126
|
& [http2.ClientSessionRequestOptions](https://nodejs.org/api/http2.html#clienthttp2sessionrequestheaders-options)
|
|
121
127
|
and [tls.ConnectionOptions](https://nodejs.org/api/tls.html#tlsconnectoptions-callback)
|
|
122
|
-
for HTTP/2 attunes
|
|
123
|
-
* `allowDowngrade` **{boolean}** `Default: false` Controls whether `https:` redirects to `http:` are allowed
|
|
124
|
-
* `baseURL` **{string | URL}** The base URL to use in cases where `url` is a relative URL
|
|
128
|
+
for HTTP/2 attunes.
|
|
129
|
+
* `allowDowngrade` **{boolean}** `Default: false` Controls whether `https:` redirects to `http:` are allowed.
|
|
130
|
+
* `baseURL` **{string | URL}** The base URL to use in cases where `url` is a relative URL.
|
|
125
131
|
* `body` **{string | Array | ArrayBuffer | ArrayBufferView | AsyncIterator | Blob | Buffer | DataView | File |
|
|
126
132
|
FormData | Iterator | Object | Readable | ReadableStream | SharedArrayBuffer | URLSearchParams}** The body to send
|
|
127
|
-
with the request
|
|
133
|
+
with the request.
|
|
128
134
|
* `bufferBody` **{boolean}** `Default: false` Toggles the buffering of the streamable request bodies for redirects and
|
|
129
|
-
retries
|
|
130
|
-
* `cookies` **{boolean | string[] |
|
|
131
|
-
cookies to add to
|
|
132
|
-
|
|
133
|
-
* `cookiesTTL` **{boolean}** `Default: false` Controls enablement of TTL for the cookies cache
|
|
135
|
+
retries.
|
|
136
|
+
* `cookies` **{boolean | string | string[] | [k, v][] | Cookies | Object | URLSearchParams}** `Default: true` The
|
|
137
|
+
cookies to add to the request. Manually set `cookie` header to override.
|
|
138
|
+
* `cookiesTTL` **{boolean}** `Default: false` Controls enablement of TTL for the cookies cache.
|
|
134
139
|
* `credentials` **{include | omit | same-origin}** `Default: same-origin` Controls credentials in case of cross-origin
|
|
135
|
-
redirects
|
|
136
|
-
* `decodersOptions` **{Object}** Configures decoders options, e.g.: `brotli`, `zlib`, `zstd
|
|
137
|
-
* `digest` **{boolean}** `Default: true` Controls whether to read the response stream or add a mixin
|
|
138
|
-
* `encodersOptions` **{Object}** Configures encoders options, e.g.: `brotli`, `zlib`, `zstd
|
|
139
|
-
* `follow` **{number}** `Default: 20` The number of redirects to follow
|
|
140
|
-
* `h2` **{boolean}** `Default: false` Forces the use of HTTP/2 protocol
|
|
141
|
-
* `headers` **{Object}** The headers to add to the request
|
|
142
|
-
* `params` **{Object}** The search params to add to the `url
|
|
143
|
-
* `parse` **{boolean}** `Default: true` Controls whether to parse response body or return a buffer
|
|
144
|
-
* `redirect` **{error | follow | manual}** `Default: follow` Controls the redirect flows
|
|
145
|
-
* `retry` **{Object}** Represents the retry options
|
|
146
|
-
* `attempts` **{number}** `Default: 0` The number of retry attempts
|
|
140
|
+
redirects.
|
|
141
|
+
* `decodersOptions` **{Object}** Configures decoders options, e.g.: `brotli`, `zlib`, `zstd`.
|
|
142
|
+
* `digest` **{boolean}** `Default: true` Controls whether to read the response stream or add a mixin.
|
|
143
|
+
* `encodersOptions` **{Object}** Configures encoders options, e.g.: `brotli`, `zlib`, `zstd`.
|
|
144
|
+
* `follow` **{number}** `Default: 20` The number of redirects to follow.
|
|
145
|
+
* `h2` **{boolean}** `Default: false` Forces the use of HTTP/2 protocol.
|
|
146
|
+
* `headers` **{Object}** The headers to add to the request.
|
|
147
|
+
* `params` **{Object}** The search params to add to the `url`.
|
|
148
|
+
* `parse` **{boolean}** `Default: true` Controls whether to parse response body or return a buffer.
|
|
149
|
+
* `redirect` **{error | follow | manual}** `Default: follow` Controls the redirect flows.
|
|
150
|
+
* `retry` **{Object}** Represents the retry options.
|
|
151
|
+
* `attempts` **{number}** `Default: 0` The number of retry attempts.
|
|
147
152
|
* `backoffStrategy` **{string}** `Default: interval * Math.log(Math.random() * (Math.E * Math.E - Math.E) + Math.E)`
|
|
148
153
|
The backoff strategy uses a log-uniform algorithm. To fix the interval, set the value to `interval * 1`.
|
|
149
154
|
* `errorCodes` **{string[]}**
|
|
150
155
|
`Default: ['ECONNREFUSED', 'ECONNRESET', 'EHOSTDOWN', 'EHOSTUNREACH', 'ENETDOWN', 'ENETUNREACH', 'ENOTFOUND', 'ERR_HTTP2_STREAM_ERROR']`
|
|
151
|
-
The list of error codes to retry on
|
|
152
|
-
* `interval` **{number}** `Default: 1e3` The initial retry interval
|
|
153
|
-
* `maxRetryAfter` **{number}** `Default: 3e5` The maximum `retry-after` limit in milliseconds
|
|
154
|
-
* `retryAfter` **{boolean}** `Default: true` Controls `retry-after` header receptiveness
|
|
155
|
-
* `statusCodes` **{number[]}** `Default: [429, 500, 502, 503, 504]` The list of status codes to retry on
|
|
156
|
-
* `stripTrailingSlash` **{boolean}** `Default: false` Controls whether to strip trailing slash at the end of the URL
|
|
157
|
-
* `thenable` **{boolean}** `Default: false` Controls the promise resolutions
|
|
158
|
-
* `timeout` **{number}** `Default: 3e5` The number of milliseconds a request can take before termination
|
|
159
|
-
* `trimTrailingSlashes` **{boolean}** `Default: false` Controls whether to trim trailing slashes within the URL
|
|
156
|
+
The list of error codes to retry on.
|
|
157
|
+
* `interval` **{number}** `Default: 1e3` The initial retry interval.
|
|
158
|
+
* `maxRetryAfter` **{number}** `Default: 3e5` The maximum `retry-after` limit in milliseconds.
|
|
159
|
+
* `retryAfter` **{boolean}** `Default: true` Controls `retry-after` header receptiveness.
|
|
160
|
+
* `statusCodes` **{number[]}** `Default: [429, 500, 502, 503, 504]` The list of status codes to retry on.
|
|
161
|
+
* `stripTrailingSlash` **{boolean}** `Default: false` Controls whether to strip trailing slash at the end of the URL.
|
|
162
|
+
* `thenable` **{boolean}** `Default: false` Controls the promise resolutions.
|
|
163
|
+
* `timeout` **{number}** `Default: 3e5` The number of milliseconds a request can take before termination.
|
|
164
|
+
* `trimTrailingSlashes` **{boolean}** `Default: false` Controls whether to trim trailing slashes within the URL.
|
|
160
165
|
* **Returns:** Promise that resolves to
|
|
161
166
|
extended [http.IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage)
|
|
162
167
|
or [http2.ClientHttp2Stream](https://nodejs.org/api/http2.html#class-clienthttp2stream) which is respectively
|
|
163
|
-
readable and duplex streams
|
|
168
|
+
readable and duplex streams.
|
|
164
169
|
* if `digest: true` & `parse: true`
|
|
165
|
-
* `body` **{string | Array | Buffer | Object}** The body based on its content type
|
|
170
|
+
* `body` **{string | Array | Buffer | Object}** The body based on its content type.
|
|
166
171
|
* if `digest: false`
|
|
167
|
-
* `arrayBuffer` **{AsyncFunction}** Reads the response and returns **ArrayBuffer
|
|
168
|
-
* `blob` **{AsyncFunction}** Reads the response and returns **Blob
|
|
169
|
-
* `body` **{AsyncFunction}** Reads the response and returns **Buffer** if `parse: false
|
|
170
|
-
* `bytes` **{AsyncFunction}** Reads the response and returns **Uint8Array
|
|
171
|
-
* `json` **{AsyncFunction}** Reads the response and returns **Object
|
|
172
|
-
* `text` **{AsyncFunction}** Reads the response and returns **String
|
|
173
|
-
* `bodyUsed` **{boolean}** Indicates whether the response was read or not
|
|
174
|
-
* `cookies` **{undefined | Cookies}** The cookies sent and received with the response
|
|
175
|
-
* `headers` **{Object}** The headers received with the response
|
|
176
|
-
* `httpVersion` **{string}** Indicates a protocol version negotiated with the server
|
|
177
|
-
* `ok` **{boolean}** Indicates if the response was successful (statusCode: **200-299**)
|
|
178
|
-
* `redirected` **{boolean}** Indicates if the response is the result of a redirect
|
|
179
|
-
* `statusCode` **{number}** Indicates the status code of the response
|
|
180
|
-
* `trailers` **{undefined | Object}** The trailer headers received with the response
|
|
172
|
+
* `arrayBuffer` **{AsyncFunction}** Reads the response and returns **ArrayBuffer**.
|
|
173
|
+
* `blob` **{AsyncFunction}** Reads the response and returns **Blob**.
|
|
174
|
+
* `body` **{AsyncFunction}** Reads the response and returns **Buffer** if `parse: false`.
|
|
175
|
+
* `bytes` **{AsyncFunction}** Reads the response and returns **Uint8Array**.
|
|
176
|
+
* `json` **{AsyncFunction}** Reads the response and returns **Object**.
|
|
177
|
+
* `text` **{AsyncFunction}** Reads the response and returns **String**.
|
|
178
|
+
* `bodyUsed` **{boolean}** Indicates whether the response was read or not.
|
|
179
|
+
* `cookies` **{undefined | Cookies}** The cookies sent and received with the response.
|
|
180
|
+
* `headers` **{Object}** The headers received with the response.
|
|
181
|
+
* `httpVersion` **{string}** Indicates a protocol version negotiated with the server.
|
|
182
|
+
* `ok` **{boolean}** Indicates if the response was successful (statusCode: **200-299**).
|
|
183
|
+
* `redirected` **{boolean}** Indicates if the response is the result of a redirect.
|
|
184
|
+
* `statusCode` **{number}** Indicates the status code of the response.
|
|
185
|
+
* `trailers` **{undefined | Object}** The trailer headers received with the response.
|
|
181
186
|
|
|
182
187
|
---
|
|
183
188
|
|
|
@@ -205,7 +210,7 @@ const rk = rekwest.extend({
|
|
|
205
210
|
const params = {
|
|
206
211
|
id: '[uid]',
|
|
207
212
|
signature: '[code]',
|
|
208
|
-
variant: '
|
|
213
|
+
variant: '[any]',
|
|
209
214
|
};
|
|
210
215
|
const signal = AbortSignal.timeout(3e4);
|
|
211
216
|
const url = '/somewhat/endpoint';
|
|
@@ -226,9 +231,9 @@ console.log(res.body);
|
|
|
226
231
|
|
|
227
232
|
The method with limited functionality to use with streams and/or pipes.
|
|
228
233
|
|
|
229
|
-
* No automata (redirects & retries)
|
|
230
|
-
* Pass `h2: true` in options to use HTTP/2 protocol
|
|
231
|
-
* Use `ackn({ url: URL })` method in advance to check the available protocols
|
|
234
|
+
* No automata (redirects & retries).
|
|
235
|
+
* Pass `h2: true` in options to use HTTP/2 protocol.
|
|
236
|
+
* Use `ackn({ url: URL })` method in advance to check the available protocols.
|
|
232
237
|
|
|
233
238
|
```javascript
|
|
234
239
|
import fs from 'node:fs';
|
package/dist/ackn.cjs
CHANGED
|
@@ -27,11 +27,11 @@ const ackn = options => new Promise((resolve, reject) => {
|
|
|
27
27
|
createConnection() {
|
|
28
28
|
return socket;
|
|
29
29
|
},
|
|
30
|
-
h2: /
|
|
30
|
+
h2: /\bh2\b/i.test(alpnProtocol),
|
|
31
31
|
protocol: url.protocol
|
|
32
32
|
});
|
|
33
33
|
});
|
|
34
|
-
socket.
|
|
35
|
-
socket.
|
|
34
|
+
socket.once('error', reject);
|
|
35
|
+
socket.once('timeout', reject);
|
|
36
36
|
});
|
|
37
37
|
exports.ackn = ackn;
|
package/dist/cookies.cjs
CHANGED
|
@@ -3,10 +3,17 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.Cookies = void 0;
|
|
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
|
-
const
|
|
9
|
-
|
|
8
|
+
const cookieRex = exports.cookieRex = /^[\w-]+=(?:"[^"]*"|[^\p{Control};]*)(?:;\s*(?:[\w-]+=(?:"[^"]*"|[^\p{Control};]*)|[\w-]+))*$/u;
|
|
9
|
+
const cookiePairRex = exports.cookiePairRex = /(?:[^;"\s]+="[^"]*"|[^;]+)(?=;|$)/g;
|
|
10
|
+
const illegalCookieChars = exports.illegalCookieChars = /\p{Control}/u;
|
|
11
|
+
const isValidCookie = str => str?.constructor === String && cookieRex.test(str);
|
|
12
|
+
exports.isValidCookie = isValidCookie;
|
|
13
|
+
const maxCookieLifetimeCap = exports.maxCookieLifetimeCap = 3456e7; // 400 days
|
|
14
|
+
const maxCookieSize = exports.maxCookieSize = 4096;
|
|
15
|
+
const splitCookie = str => str.match(cookiePairRex).map(str => str.trim());
|
|
16
|
+
exports.splitCookie = splitCookie;
|
|
10
17
|
class Cookies extends URLSearchParams {
|
|
11
18
|
static #finalizers = new Set();
|
|
12
19
|
static jar = new Map();
|
|
@@ -27,27 +34,39 @@ class Cookies extends URLSearchParams {
|
|
|
27
34
|
} = {
|
|
28
35
|
cookiesTTL: false
|
|
29
36
|
}) {
|
|
30
|
-
if (
|
|
31
|
-
input = input
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
if (isValidCookie(input)) {
|
|
38
|
+
input = splitCookie(input);
|
|
39
|
+
}
|
|
40
|
+
const ttlMap = new Map();
|
|
41
|
+
if (Array.isArray(input)) {
|
|
42
|
+
if (input.every(it => isValidCookie(it))) {
|
|
43
|
+
input = input.filter(it => !illegalCookieChars.test(it) && it.length <= maxCookieSize);
|
|
44
|
+
input = input.map(splitCookie).map(([cookie, ...attrs]) => {
|
|
45
|
+
try {
|
|
46
|
+
cookie = cookie.split('=').map(it => decodeURIComponent(it.trim()));
|
|
47
|
+
return cookie;
|
|
48
|
+
} finally {
|
|
49
|
+
if (cookiesTTL) {
|
|
50
|
+
for (const attr of attrs) {
|
|
51
|
+
if (/(?:expires|max-age)=/i.test(attr)) {
|
|
52
|
+
const [key, val] = attr.toLowerCase().split('=');
|
|
53
|
+
let interval = val * 1e3 || Date.parse(val) - Date.now();
|
|
54
|
+
if (interval < 0 || Number.isNaN(interval)) {
|
|
55
|
+
interval = 0;
|
|
56
|
+
}
|
|
57
|
+
ttlMap.set(cookie[0], {
|
|
58
|
+
[(0, _utils.toCamelCase)(key.trim())]: Math.min(interval, maxCookieLifetimeCap)
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
42
63
|
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
46
66
|
}
|
|
47
|
-
super(
|
|
48
|
-
if (
|
|
49
|
-
for (const [
|
|
50
|
-
const key = cookie.split('=')[0];
|
|
67
|
+
super(input);
|
|
68
|
+
if (ttlMap.size) {
|
|
69
|
+
for (const [key, attrs] of ttlMap) {
|
|
51
70
|
if (this.#chronometry.has(key)) {
|
|
52
71
|
clearTimeout(this.#chronometry.get(key));
|
|
53
72
|
this.#chronometry.delete(key);
|
|
@@ -55,9 +74,9 @@ class Cookies extends URLSearchParams {
|
|
|
55
74
|
const {
|
|
56
75
|
expires,
|
|
57
76
|
maxAge
|
|
58
|
-
} =
|
|
59
|
-
for (const
|
|
60
|
-
if (!Number.isInteger(
|
|
77
|
+
} = attrs;
|
|
78
|
+
for (const interval of [maxAge, expires]) {
|
|
79
|
+
if (!Number.isInteger(interval)) {
|
|
61
80
|
continue;
|
|
62
81
|
}
|
|
63
82
|
const ref = new WeakRef(this);
|
|
@@ -67,7 +86,7 @@ class Cookies extends URLSearchParams {
|
|
|
67
86
|
ctx.#chronometry.delete(key);
|
|
68
87
|
ctx.delete(key);
|
|
69
88
|
}
|
|
70
|
-
}, Math.max(
|
|
89
|
+
}, Math.max(interval, 0));
|
|
71
90
|
this.constructor.#register(this, tid);
|
|
72
91
|
this.#chronometry.set(key, tid);
|
|
73
92
|
break;
|
package/dist/formdata.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.FormData = void 0;
|
|
6
|
+
exports.parseFormData = exports.isFormData = exports.fdToAsyncIterable = exports.FormData = void 0;
|
|
7
7
|
var _nodeBuffer = require("node:buffer");
|
|
8
8
|
var _nodeCrypto = require("node:crypto");
|
|
9
9
|
var _nodeHttp = _interopRequireDefault(require("node:http2"));
|
|
@@ -16,40 +16,24 @@ const {
|
|
|
16
16
|
HTTP2_HEADER_CONTENT_TYPE
|
|
17
17
|
} = _nodeHttp.default.constants;
|
|
18
18
|
class FormData {
|
|
19
|
-
static
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
contentType,
|
|
27
|
-
async *[Symbol.asyncIterator]() {
|
|
28
|
-
const encoder = new TextEncoder();
|
|
29
|
-
for (const [name, val] of fd) {
|
|
30
|
-
if (val.constructor === String) {
|
|
31
|
-
yield encoder.encode(`${prefix}; name="${escape(redress(name))}"${CRLF.repeat(2)}${redress(val)}${CRLF}`);
|
|
32
|
-
} else {
|
|
33
|
-
yield encoder.encode(`${prefix}; name="${escape(redress(name))}"${val.name ? `; filename="${escape(val.name)}"` : ''}${CRLF}${HTTP2_HEADER_CONTENT_TYPE}: ${val.type || _mediatypes.APPLICATION_OCTET_STREAM}${CRLF.repeat(2)}`);
|
|
34
|
-
yield* (0, _utils.tap)(val);
|
|
35
|
-
yield new Uint8Array([13, 10]);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
yield encoder.encode(`--${boundary}--`);
|
|
19
|
+
static #ensureArgs(args, expect, method) {
|
|
20
|
+
if (args.length < expect) {
|
|
21
|
+
throw new TypeError(`Failed to execute '${method}' on '${this[Symbol.toStringTag]}': ${expect} arguments required, but only ${args.length} present`);
|
|
22
|
+
}
|
|
23
|
+
if (method === 'forEach') {
|
|
24
|
+
if (args[0]?.constructor !== Function) {
|
|
25
|
+
throw new TypeError(`Failed to execute '${method}' on '${this[Symbol.toStringTag]}': parameter ${expect} is not of type 'Function'`);
|
|
39
26
|
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
static alike(val) {
|
|
43
|
-
return FormData.name === val?.[Symbol.toStringTag];
|
|
27
|
+
}
|
|
44
28
|
}
|
|
45
|
-
static #
|
|
29
|
+
static #formEntry(name, value, filename) {
|
|
46
30
|
name = String(name).toWellFormed();
|
|
47
31
|
filename &&= String(filename).toWellFormed();
|
|
48
|
-
if ((0, _utils.
|
|
49
|
-
filename ??= value.name
|
|
32
|
+
if ((0, _utils.isBlobLike)(value)) {
|
|
33
|
+
filename ??= String(value.name ?? 'blob').toWellFormed();
|
|
50
34
|
value = new _nodeBuffer.File([value], filename, value);
|
|
51
|
-
} else if (
|
|
52
|
-
value.name = filename;
|
|
35
|
+
} else if ((0, _utils.isPipeStream)(value) || (0, _utils.isReadableStream)(value)) {
|
|
36
|
+
value.name = filename ?? 'blob';
|
|
53
37
|
} else {
|
|
54
38
|
value = String(value).toWellFormed();
|
|
55
39
|
}
|
|
@@ -58,9 +42,6 @@ class FormData {
|
|
|
58
42
|
value
|
|
59
43
|
};
|
|
60
44
|
}
|
|
61
|
-
static #ensureInstance(val) {
|
|
62
|
-
return (0, _utils.isFileLike)(val) || Object(val) === val && Reflect.has(val, Symbol.asyncIterator);
|
|
63
|
-
}
|
|
64
45
|
#entries = [];
|
|
65
46
|
get [Symbol.toStringTag]() {
|
|
66
47
|
return this.constructor.name;
|
|
@@ -70,10 +51,10 @@ class FormData {
|
|
|
70
51
|
if (Array.isArray(input)) {
|
|
71
52
|
if (!input.every(it => Array.isArray(it))) {
|
|
72
53
|
throw new TypeError(`Failed to construct '${this[Symbol.toStringTag]}': The provided value cannot be converted to a sequence`);
|
|
73
|
-
}
|
|
54
|
+
}
|
|
55
|
+
if (!input.every(it => it.length === 2)) {
|
|
74
56
|
throw new TypeError(`Failed to construct '${this[Symbol.toStringTag]}': Sequence initializer must only contain pair elements`);
|
|
75
57
|
}
|
|
76
|
-
input = Array.from(input);
|
|
77
58
|
} else if (!Reflect.has(input, Symbol.iterator)) {
|
|
78
59
|
input = Object.entries(input);
|
|
79
60
|
}
|
|
@@ -82,35 +63,20 @@ class FormData {
|
|
|
82
63
|
}
|
|
83
64
|
}
|
|
84
65
|
}
|
|
85
|
-
#ensureArgs(args, expected, method) {
|
|
86
|
-
if (args.length < expected) {
|
|
87
|
-
throw new TypeError(`Failed to execute '${method}' on '${this[Symbol.toStringTag]}': ${expected} arguments required, but only ${args.length} present`);
|
|
88
|
-
}
|
|
89
|
-
if (['append', 'set'].includes(method)) {
|
|
90
|
-
if (args.length === 3 && !this.constructor.#ensureInstance(args[1])) {
|
|
91
|
-
throw new TypeError(`Failed to execute '${method}' on '${this[Symbol.toStringTag]}': parameter ${expected} is not of type 'Blob'`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (method === 'forEach') {
|
|
95
|
-
if (args[0]?.constructor !== Function) {
|
|
96
|
-
throw new TypeError(`Failed to execute '${method}' on '${this[Symbol.toStringTag]}': parameter ${expected} is not of type 'Function'`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
66
|
append(...args) {
|
|
101
67
|
(0, _utils.brandCheck)(this, FormData);
|
|
102
|
-
this.#ensureArgs(args, 2,
|
|
103
|
-
this.#entries.push(this.constructor.#
|
|
68
|
+
this.constructor.#ensureArgs(args, 2, this.append.name);
|
|
69
|
+
this.#entries.push(this.constructor.#formEntry(...args));
|
|
104
70
|
}
|
|
105
71
|
delete(...args) {
|
|
106
72
|
(0, _utils.brandCheck)(this, FormData);
|
|
107
|
-
this.#ensureArgs(args, 1,
|
|
73
|
+
this.constructor.#ensureArgs(args, 1, this.delete.name);
|
|
108
74
|
const name = String(args[0]).toWellFormed();
|
|
109
75
|
this.#entries = this.#entries.filter(it => it.name !== name);
|
|
110
76
|
}
|
|
111
77
|
forEach(...args) {
|
|
112
78
|
(0, _utils.brandCheck)(this, FormData);
|
|
113
|
-
this.#ensureArgs(args, 1,
|
|
79
|
+
this.constructor.#ensureArgs(args, 1, this.forEach.name);
|
|
114
80
|
const [callback, thisArg] = args;
|
|
115
81
|
for (const entry of this) {
|
|
116
82
|
Reflect.apply(callback, thisArg, [...entry.reverse(), this]);
|
|
@@ -118,26 +84,26 @@ class FormData {
|
|
|
118
84
|
}
|
|
119
85
|
get(...args) {
|
|
120
86
|
(0, _utils.brandCheck)(this, FormData);
|
|
121
|
-
this.#ensureArgs(args, 1,
|
|
87
|
+
this.constructor.#ensureArgs(args, 1, this.get.name);
|
|
122
88
|
const name = String(args[0]).toWellFormed();
|
|
123
89
|
return this.#entries.find(it => it.name === name)?.value ?? null;
|
|
124
90
|
}
|
|
125
91
|
getAll(...args) {
|
|
126
92
|
(0, _utils.brandCheck)(this, FormData);
|
|
127
|
-
this.#ensureArgs(args, 1,
|
|
93
|
+
this.constructor.#ensureArgs(args, 1, this.getAll.name);
|
|
128
94
|
const name = String(args[0]).toWellFormed();
|
|
129
95
|
return this.#entries.filter(it => it.name === name).map(it => it.value);
|
|
130
96
|
}
|
|
131
97
|
has(...args) {
|
|
132
98
|
(0, _utils.brandCheck)(this, FormData);
|
|
133
|
-
this.#ensureArgs(args, 1,
|
|
99
|
+
this.constructor.#ensureArgs(args, 1, this.has.name);
|
|
134
100
|
const name = String(args[0]).toWellFormed();
|
|
135
101
|
return !!this.#entries.find(it => it.name === name);
|
|
136
102
|
}
|
|
137
103
|
set(...args) {
|
|
138
104
|
(0, _utils.brandCheck)(this, FormData);
|
|
139
|
-
this.#ensureArgs(args, 2,
|
|
140
|
-
const entry = this.constructor.#
|
|
105
|
+
this.constructor.#ensureArgs(args, 2, this.set.name);
|
|
106
|
+
const entry = this.constructor.#formEntry(...args);
|
|
141
107
|
const idx = this.#entries.findIndex(it => it.name === entry.name);
|
|
142
108
|
if (idx !== -1) {
|
|
143
109
|
this.#entries.splice(idx, 1, entry);
|
|
@@ -171,4 +137,37 @@ class FormData {
|
|
|
171
137
|
return this.entries();
|
|
172
138
|
}
|
|
173
139
|
}
|
|
174
|
-
exports.FormData = FormData;
|
|
140
|
+
exports.FormData = FormData;
|
|
141
|
+
const fdToAsyncIterable = fd => {
|
|
142
|
+
const boundary = (0, _nodeCrypto.randomBytes)(32).toString('hex');
|
|
143
|
+
const contentType = `${_mediatypes.MULTIPART_FORM_DATA}; boundary=${boundary}`;
|
|
144
|
+
const prefix = `--${boundary}${CRLF}${HTTP2_HEADER_CONTENT_DISPOSITION}: form-data`;
|
|
145
|
+
const escape = str => str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22');
|
|
146
|
+
const normalize = str => str.replace(/\r?\n|\r/g, CRLF);
|
|
147
|
+
return {
|
|
148
|
+
contentType,
|
|
149
|
+
async *[Symbol.asyncIterator]() {
|
|
150
|
+
const encoder = new TextEncoder();
|
|
151
|
+
for (const [name, val] of fd) {
|
|
152
|
+
if (val.constructor === String) {
|
|
153
|
+
yield encoder.encode(`${prefix}; name="${escape(normalize(name))}"${CRLF.repeat(2)}${normalize(val)}${CRLF}`);
|
|
154
|
+
} else {
|
|
155
|
+
yield encoder.encode(`${prefix}; name="${escape(normalize(name))}"${val.name ? `; filename="${escape(val.name)}"` : ''}${CRLF}${HTTP2_HEADER_CONTENT_TYPE}: ${val.type || _mediatypes.APPLICATION_OCTET_STREAM}${CRLF.repeat(2)}`);
|
|
156
|
+
yield* (0, _utils.tap)(val);
|
|
157
|
+
yield new Uint8Array([13, 10]);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
yield encoder.encode(`--${boundary}--${CRLF}`);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
exports.fdToAsyncIterable = fdToAsyncIterable;
|
|
165
|
+
const isFormData = val => FormData.name === val?.[Symbol.toStringTag];
|
|
166
|
+
exports.isFormData = isFormData;
|
|
167
|
+
const parseFormData = str => {
|
|
168
|
+
const rex = /^-+[^\r\n]+\r?\ncontent-disposition:\s*form-data;\s*name="(?<name>[^"]+)"(?:;\s*filename="(?<filename>[^"]+)")?(?:\r?\n[^\r\n:]+:[^\r\n]*)*\r?\n\r?\n(?<content>.*?)(?=\r?\n-+[^\r\n]+)/gims;
|
|
169
|
+
return [...str.matchAll(rex)].map(({
|
|
170
|
+
groups
|
|
171
|
+
}) => structuredClone(groups));
|
|
172
|
+
};
|
|
173
|
+
exports.parseFormData = parseFormData;
|
package/dist/preflight.cjs
CHANGED
package/dist/retries.cjs
CHANGED
|
@@ -35,7 +35,7 @@ const retries = (err, options) => {
|
|
|
35
35
|
} = retry;
|
|
36
36
|
if (retry.retryAfter && err.headers?.[HTTP2_HEADER_RETRY_AFTER]) {
|
|
37
37
|
interval = err.headers[HTTP2_HEADER_RETRY_AFTER];
|
|
38
|
-
interval =
|
|
38
|
+
interval = interval * 1e3 || Date.parse(interval) - Date.now();
|
|
39
39
|
if (interval > retry.maxRetryAfter) {
|
|
40
40
|
throw new _errors.RequestError(`Maximum '${HTTP2_HEADER_RETRY_AFTER}' limit exceeded: ${interval} ms`, {
|
|
41
41
|
cause: err
|
|
@@ -44,7 +44,7 @@ const retries = (err, options) => {
|
|
|
44
44
|
} else {
|
|
45
45
|
interval = new Function('interval', `return Math.ceil(${retry.backoffStrategy});`)(interval);
|
|
46
46
|
}
|
|
47
|
-
if (interval < 0) {
|
|
47
|
+
if (interval < 0 || Number.isNaN(interval)) {
|
|
48
48
|
interval = 0;
|
|
49
49
|
}
|
|
50
50
|
retry.attempts--;
|
package/dist/transfer.cjs
CHANGED
|
@@ -53,11 +53,9 @@ const transfer = async options => {
|
|
|
53
53
|
} = url.protocol === 'http:' ? _nodeHttp.default : _nodeHttps.default;
|
|
54
54
|
req = request(url, options);
|
|
55
55
|
}
|
|
56
|
-
(0, _utils.snoop)(client, req, options
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
req.once('frameError', reject);
|
|
60
|
-
req.once('goaway', reject);
|
|
56
|
+
(0, _utils.snoop)(client, req, options, {
|
|
57
|
+
reject
|
|
58
|
+
});
|
|
61
59
|
req.once('response', res => (0, _postflight.postflight)(req, res, options, {
|
|
62
60
|
reject,
|
|
63
61
|
resolve
|
package/dist/transform.cjs
CHANGED
|
@@ -28,7 +28,7 @@ const transform = async options => {
|
|
|
28
28
|
}
|
|
29
29
|
if (!Buffer.isBuffer(body)) {
|
|
30
30
|
switch (true) {
|
|
31
|
-
case (0, _utils.
|
|
31
|
+
case (0, _utils.isBlobLike)(body):
|
|
32
32
|
{
|
|
33
33
|
headers = {
|
|
34
34
|
[HTTP2_HEADER_CONTENT_LENGTH]: body.size,
|
|
@@ -37,9 +37,9 @@ const transform = async options => {
|
|
|
37
37
|
body = body.stream();
|
|
38
38
|
break;
|
|
39
39
|
}
|
|
40
|
-
case _formdata.
|
|
40
|
+
case (0, _formdata.isFormData)(body):
|
|
41
41
|
{
|
|
42
|
-
body = _formdata.
|
|
42
|
+
body = (0, _formdata.fdToAsyncIterable)(body);
|
|
43
43
|
headers = {
|
|
44
44
|
[HTTP2_HEADER_CONTENT_TYPE]: body.contentType
|
|
45
45
|
};
|