fetch-har 8.0.1 → 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/CHANGELOG.md +47 -0
- package/dist/index.js +83 -58
- package/package.json +13 -6
- package/src/index.ts +82 -58
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
+
## 8.1.0 (2022-07-26)
|
|
2
|
+
|
|
3
|
+
* chore: code comment cleanup ([bd344f1](https://github.com/readmeio/fetch-har/commit/bd344f1))
|
|
4
|
+
* chore(deps-dev): bump @jsdevtools/karma-config from 3.1.7 to 3.2.0 (#281) ([291f16d](https://github.com/readmeio/fetch-har/commit/291f16d)), closes [#281](https://github.com/readmeio/fetch-har/issues/281)
|
|
5
|
+
* chore(deps-dev): bump @jsdevtools/karma-config from 3.1.7 to 3.2.0 (#302) ([903f62f](https://github.com/readmeio/fetch-har/commit/903f62f)), closes [#302](https://github.com/readmeio/fetch-har/issues/302)
|
|
6
|
+
* chore(deps-dev): bump @readme/eslint-config from 8.7.3 to 8.8.0 (#285) ([47a83be](https://github.com/readmeio/fetch-har/commit/47a83be)), closes [#285](https://github.com/readmeio/fetch-har/issues/285)
|
|
7
|
+
* chore(deps-dev): bump @readme/eslint-config from 8.8.0 to 9.0.0 (#306) ([5511118](https://github.com/readmeio/fetch-har/commit/5511118)), closes [#306](https://github.com/readmeio/fetch-har/issues/306)
|
|
8
|
+
* chore(deps-dev): bump @types/node from 17.0.30 to 17.0.38 (#275) ([9ad835a](https://github.com/readmeio/fetch-har/commit/9ad835a)), closes [#275](https://github.com/readmeio/fetch-har/issues/275)
|
|
9
|
+
* chore(deps-dev): bump @types/node from 17.0.38 to 18.0.0 (#290) ([7c9c2df](https://github.com/readmeio/fetch-har/commit/7c9c2df)), closes [#290](https://github.com/readmeio/fetch-har/issues/290)
|
|
10
|
+
* chore(deps-dev): bump @types/node from 18.0.0 to 18.6.1 (#304) ([bcec171](https://github.com/readmeio/fetch-har/commit/bcec171)), closes [#304](https://github.com/readmeio/fetch-har/issues/304)
|
|
11
|
+
* chore(deps-dev): bump eslint from 8.14.0 to 8.16.0 (#279) ([e78536f](https://github.com/readmeio/fetch-har/commit/e78536f)), closes [#279](https://github.com/readmeio/fetch-har/issues/279)
|
|
12
|
+
* chore(deps-dev): bump eslint from 8.16.0 to 8.18.0 (#293) ([2c22a43](https://github.com/readmeio/fetch-har/commit/2c22a43)), closes [#293](https://github.com/readmeio/fetch-har/issues/293)
|
|
13
|
+
* chore(deps-dev): bump eslint from 8.18.0 to 8.20.0 (#300) ([5eb8e49](https://github.com/readmeio/fetch-har/commit/5eb8e49)), closes [#300](https://github.com/readmeio/fetch-har/issues/300)
|
|
14
|
+
* chore(deps-dev): bump mocha from 9.2.2 to 10.0.0 (#277) ([62cc6b6](https://github.com/readmeio/fetch-har/commit/62cc6b6)), closes [#277](https://github.com/readmeio/fetch-har/issues/277)
|
|
15
|
+
* chore(deps-dev): bump nock from 13.2.4 to 13.2.8 (#294) ([21e976f](https://github.com/readmeio/fetch-har/commit/21e976f)), closes [#294](https://github.com/readmeio/fetch-har/issues/294)
|
|
16
|
+
* chore(deps-dev): bump nock from 13.2.8 to 13.2.9 (#305) ([8e7c741](https://github.com/readmeio/fetch-har/commit/8e7c741)), closes [#305](https://github.com/readmeio/fetch-har/issues/305)
|
|
17
|
+
* chore(deps-dev): bump prettier from 2.6.2 to 2.7.1 (#295) ([589c94e](https://github.com/readmeio/fetch-har/commit/589c94e)), closes [#295](https://github.com/readmeio/fetch-har/issues/295)
|
|
18
|
+
* chore(deps-dev): bump ts-loader from 9.3.0 to 9.3.1 (#292) ([0716171](https://github.com/readmeio/fetch-har/commit/0716171)), closes [#292](https://github.com/readmeio/fetch-har/issues/292)
|
|
19
|
+
* chore(deps-dev): bump ts-node from 10.7.0 to 10.8.1 (#286) ([fe87394](https://github.com/readmeio/fetch-har/commit/fe87394)), closes [#286](https://github.com/readmeio/fetch-har/issues/286)
|
|
20
|
+
* chore(deps-dev): bump ts-node from 10.8.1 to 10.9.1 (#303) ([9188de6](https://github.com/readmeio/fetch-har/commit/9188de6)), closes [#303](https://github.com/readmeio/fetch-har/issues/303)
|
|
21
|
+
* chore(deps-dev): bump typescript from 4.6.4 to 4.7.4 (#287) ([a9e5b29](https://github.com/readmeio/fetch-har/commit/a9e5b29)), closes [#287](https://github.com/readmeio/fetch-har/issues/287)
|
|
22
|
+
* chore(deps-dev): bump undici from 5.0.0 to 5.5.1 (#284) ([6e013e2](https://github.com/readmeio/fetch-har/commit/6e013e2)), closes [#284](https://github.com/readmeio/fetch-har/issues/284)
|
|
23
|
+
* chore(deps-dev): bump undici from 5.5.1 to 5.8.0 (#297) ([4f220aa](https://github.com/readmeio/fetch-har/commit/4f220aa)), closes [#297](https://github.com/readmeio/fetch-har/issues/297)
|
|
24
|
+
* chore(deps): bump actions/cache from 3.0.2 to 3.0.3 (#274) ([17071a2](https://github.com/readmeio/fetch-har/commit/17071a2)), closes [#274](https://github.com/readmeio/fetch-har/issues/274)
|
|
25
|
+
* chore(deps): bump actions/cache from 3.0.3 to 3.0.4 (#289) ([a2c9255](https://github.com/readmeio/fetch-har/commit/a2c9255)), closes [#289](https://github.com/readmeio/fetch-har/issues/289)
|
|
26
|
+
* chore(deps): bump actions/cache from 3.0.4 to 3.0.5 (#299) ([b4a8229](https://github.com/readmeio/fetch-har/commit/b4a8229)), closes [#299](https://github.com/readmeio/fetch-har/issues/299)
|
|
27
|
+
* chore(deps): bump formdata-node from 4.3.2 to 4.3.3 (#288) ([2226cd7](https://github.com/readmeio/fetch-har/commit/2226cd7)), closes [#288](https://github.com/readmeio/fetch-har/issues/288)
|
|
28
|
+
* chore(deps): bump terser from 4.8.0 to 4.8.1 (#296) ([9fb708f](https://github.com/readmeio/fetch-har/commit/9fb708f)), closes [#296](https://github.com/readmeio/fetch-har/issues/296)
|
|
29
|
+
* fix: don't look for a length on `postData.text` if it's undefined (#272) ([cd70c2f](https://github.com/readmeio/fetch-har/commit/cd70c2f)), closes [#272](https://github.com/readmeio/fetch-har/issues/272)
|
|
30
|
+
* fix: pin to node 18.0 (#273) ([9ab5991](https://github.com/readmeio/fetch-har/commit/9ab5991)), closes [#273](https://github.com/readmeio/fetch-har/issues/273)
|
|
31
|
+
* fix: support for uploading multiple files through the same parameter (#298) ([b3f4877](https://github.com/readmeio/fetch-har/commit/b3f4877)), closes [#298](https://github.com/readmeio/fetch-har/issues/298)
|
|
32
|
+
* ci: pin ci to node 18.0 ([6ce62ac](https://github.com/readmeio/fetch-har/commit/6ce62ac))
|
|
33
|
+
* ci: pin to node 18.1 because 18.2 has broken cookie header support ([05748b4](https://github.com/readmeio/fetch-har/commit/05748b4))
|
|
34
|
+
* ci: running ci tests on the latest node 18 again ([0465075](https://github.com/readmeio/fetch-har/commit/0465075))
|
|
35
|
+
* build: 8.0.2 release ([491eedf](https://github.com/readmeio/fetch-har/commit/491eedf))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## <small>8.0.2 (2022-05-20)</small>
|
|
40
|
+
|
|
41
|
+
* fix: don't look for a length on `postData.text` if it's undefined (#272) ([cd70c2f](https://github.com/readmeio/fetch-har/commit/cd70c2f)), closes [#272](https://github.com/readmeio/fetch-har/issues/272)
|
|
42
|
+
* fix: pin to node 18.0 (#273) ([9ab5991](https://github.com/readmeio/fetch-har/commit/9ab5991)), closes [#273](https://github.com/readmeio/fetch-har/issues/273)
|
|
43
|
+
* ci: pin ci to node 18.0 ([6ce62ac](https://github.com/readmeio/fetch-har/commit/6ce62ac))
|
|
44
|
+
* ci: pin to node 18.1 because 18.2 has broken cookie header support ([05748b4](https://github.com/readmeio/fetch-har/commit/05748b4))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
1
48
|
## <small>8.0.1 (2022-05-03)</small>
|
|
2
49
|
|
|
3
50
|
* fix: making har-format types a regular dep ([93dcb7d](https://github.com/readmeio/fetch-har/commit/93dcb7d))
|
package/dist/index.js
CHANGED
|
@@ -51,10 +51,12 @@ function isBuffer(value) {
|
|
|
51
51
|
}
|
|
52
52
|
function isFile(value) {
|
|
53
53
|
if (value instanceof File) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The `Blob` polyfill on Node comes back as being an instanceof `File`. Because passing a Blob
|
|
56
|
+
* into a File will end up with a corrupted file we want to prevent this.
|
|
57
|
+
*
|
|
58
|
+
* This object identity crisis does not happen in the browser.
|
|
59
|
+
*/
|
|
58
60
|
return value.constructor.name === 'File';
|
|
59
61
|
}
|
|
60
62
|
return false;
|
|
@@ -67,8 +69,9 @@ function isFunction(value) {
|
|
|
67
69
|
return typeof value === 'function';
|
|
68
70
|
}
|
|
69
71
|
/**
|
|
70
|
-
* We're
|
|
71
|
-
* regex in its main encoder that Safari doesn't support so it throws a fatal page
|
|
72
|
+
* We're using this library in here instead of loading it from `form-data-encoder` because that
|
|
73
|
+
* uses lookbehind regex in its main encoder that Safari doesn't support so it throws a fatal page
|
|
74
|
+
* exception.
|
|
72
75
|
*
|
|
73
76
|
* @license MIT
|
|
74
77
|
* @see {@link https://github.com/octet-stream/form-data-encoder/blob/master/lib/util/isFormData.ts}
|
|
@@ -83,6 +86,7 @@ function isFormData(value) {
|
|
|
83
86
|
isFunction(value[Symbol.iterator]));
|
|
84
87
|
}
|
|
85
88
|
function fetchHAR(har, opts) {
|
|
89
|
+
var _a;
|
|
86
90
|
if (opts === void 0) { opts = {}; }
|
|
87
91
|
if (!har)
|
|
88
92
|
throw new Error('Missing HAR definition');
|
|
@@ -106,16 +110,22 @@ function fetchHAR(har, opts) {
|
|
|
106
110
|
return headers.append(header.name, header.value);
|
|
107
111
|
}
|
|
108
112
|
catch (err) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
/**
|
|
114
|
+
* `Headers.append()` will throw errors if the header name is not a legal HTTP header name,
|
|
115
|
+
* like `X-API-KEY (Header)`. If that happens instead of tossing the error back out, we
|
|
116
|
+
* should silently just ignore
|
|
117
|
+
* it.
|
|
118
|
+
*/
|
|
112
119
|
}
|
|
113
120
|
});
|
|
114
121
|
}
|
|
115
122
|
if ('cookies' in request && request.cookies.length) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
/**
|
|
124
|
+
* As the browser fetch API can't set custom cookies for requests, they instead need to be
|
|
125
|
+
* defined on the document and passed into the request via `credentials: include`. Since this
|
|
126
|
+
* is a browser-specific quirk, that should only
|
|
127
|
+
* happen in browsers!
|
|
128
|
+
*/
|
|
119
129
|
if (isBrowser()) {
|
|
120
130
|
request.cookies.forEach(function (cookie) {
|
|
121
131
|
document.cookie = "".concat(encodeURIComponent(cookie.name), "=").concat(encodeURIComponent(cookie.value));
|
|
@@ -136,11 +146,14 @@ function fetchHAR(har, opts) {
|
|
|
136
146
|
}
|
|
137
147
|
switch (request.postData.mimeType) {
|
|
138
148
|
case 'application/x-www-form-urlencoded':
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Since the content we're handling here is to be encoded as
|
|
151
|
+
* `application/x-www-form-urlencoded`, this should override any other `Content-Type`
|
|
152
|
+
* headers that are present in the HAR. This is how Postman handles this case when
|
|
153
|
+
* building code snippets!
|
|
154
|
+
*
|
|
155
|
+
* @see {@link https://github.com/github/fetch/issues/263#issuecomment-209530977}
|
|
156
|
+
*/
|
|
144
157
|
headers.set('Content-Type', request.postData.mimeType);
|
|
145
158
|
var encodedParams_1 = new URLSearchParams();
|
|
146
159
|
request.postData.params.forEach(function (param) { return encodedParams_1.set(param.name, param.value); });
|
|
@@ -150,49 +163,57 @@ function fetchHAR(har, opts) {
|
|
|
150
163
|
case 'multipart/form-data':
|
|
151
164
|
case 'multipart/mixed':
|
|
152
165
|
case 'multipart/related':
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
/**
|
|
167
|
+
* If there's a `Content-Type` header set we need to remove it. We're doing this because
|
|
168
|
+
* when we pass the form data object into `fetch` that'll set a proper `Content-Type`
|
|
169
|
+
* header for this request that also includes the boundary used on the content.
|
|
170
|
+
*
|
|
171
|
+
* If we don't do this, then consumers won't be able to parse out the payload because
|
|
172
|
+
* they won't know what the boundary to split on it.
|
|
173
|
+
*/
|
|
159
174
|
if (headers.has('Content-Type')) {
|
|
160
175
|
headers["delete"]('Content-Type');
|
|
161
176
|
}
|
|
162
177
|
var form_1 = new FormData();
|
|
163
178
|
if (!isFormData(form_1)) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
179
|
+
/**
|
|
180
|
+
* The `form-data` NPM module returns one of two things: a native `FormData` API or its
|
|
181
|
+
* own polyfill. Unfortunately this polyfill does not support the full API of the native
|
|
182
|
+
* FormData object so when you load `form-data` within a browser environment you'll
|
|
183
|
+
* have two major differences in API:
|
|
184
|
+
*
|
|
185
|
+
* - The `.append()` API in `form-data` requires that the third argument is an object
|
|
186
|
+
* containing various, undocumented, options. In the browser, `.append()`'s third
|
|
187
|
+
* argument should only be present when the second is a `Blob` or `USVString`, and
|
|
188
|
+
* when it is present, it should be a filename string.
|
|
189
|
+
* - `form-data` does not expose an `.entries()` API, so the only way to retrieve data
|
|
190
|
+
* out of it for construction of boundary-separated payload content is to use its
|
|
191
|
+
* `.pipe()` API. Since the browser doesn't have this API, you'll be unable to
|
|
192
|
+
* retrieve data out of it.
|
|
193
|
+
*
|
|
194
|
+
* Now since the native `FormData` API is iterable, and has the `.entries()` iterator,
|
|
195
|
+
* we can easily detect if we have a native copy of the FormData API. It's for all of
|
|
196
|
+
* these reasons that we're opting to hard crash here because supporting this
|
|
197
|
+
* non-compliant API is more trouble than its worth.
|
|
198
|
+
*
|
|
199
|
+
* @see {@link https://github.com/form-data/form-data/issues/124}
|
|
200
|
+
*/
|
|
180
201
|
throw new Error("We've detected you're using a non-spec compliant FormData library. We recommend polyfilling FormData with https://npm.im/formdata-node");
|
|
181
202
|
}
|
|
182
203
|
request.postData.params.forEach(function (param) {
|
|
183
204
|
if ('fileName' in param) {
|
|
184
205
|
if (opts.files && param.fileName in opts.files) {
|
|
185
206
|
var fileContents = opts.files[param.fileName];
|
|
186
|
-
// If the file we've got available to us is a Buffer then we need to convert it so
|
|
187
|
-
// API can use it.
|
|
207
|
+
// If the file we've got available to us is a Buffer then we need to convert it so
|
|
208
|
+
// that the FormData API can use it.
|
|
188
209
|
if (isBuffer(fileContents)) {
|
|
189
|
-
form_1.
|
|
210
|
+
form_1.append(param.name, new File([fileContents], param.fileName, {
|
|
190
211
|
type: param.contentType || null
|
|
191
212
|
}), param.fileName);
|
|
192
213
|
return;
|
|
193
214
|
}
|
|
194
215
|
else if (isFile(fileContents)) {
|
|
195
|
-
form_1.
|
|
216
|
+
form_1.append(param.name, fileContents, param.fileName);
|
|
196
217
|
return;
|
|
197
218
|
}
|
|
198
219
|
throw new TypeError('An unknown object has been supplied into the `files` config for use. We only support instances of the File API and Node Buffer objects.');
|
|
@@ -201,8 +222,8 @@ function fetchHAR(har, opts) {
|
|
|
201
222
|
var paramBlob = void 0;
|
|
202
223
|
var parsed = (0, parse_data_url_1["default"])(param.value);
|
|
203
224
|
if (parsed) {
|
|
204
|
-
// If we were able to parse out this data URL we don't need to transform its data
|
|
205
|
-
// `Blob` because that supports data URLs already.
|
|
225
|
+
// If we were able to parse out this data URL we don't need to transform its data
|
|
226
|
+
// into a buffer for `Blob` because that supports data URLs already.
|
|
206
227
|
paramBlob = new Blob([param.value], { type: parsed.contentType || param.contentType || null });
|
|
207
228
|
}
|
|
208
229
|
else {
|
|
@@ -215,11 +236,14 @@ function fetchHAR(har, opts) {
|
|
|
215
236
|
}
|
|
216
237
|
form_1.append(param.name, param.value);
|
|
217
238
|
});
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
239
|
+
/**
|
|
240
|
+
* If a the `fetch` polyfill that's being used here doesn't have spec-compliant handling
|
|
241
|
+
* for the `FormData` API (like `node-fetch@2`), then you should pass in a handler (like
|
|
242
|
+
* the `form-data-encoder` library) to transform its contents into something that can be
|
|
243
|
+
* used with the `Request` object.
|
|
244
|
+
*
|
|
245
|
+
* @see {@link https://www.npmjs.com/package/formdata-node}
|
|
246
|
+
*/
|
|
223
247
|
if (opts.multipartEncoder) {
|
|
224
248
|
// eslint-disable-next-line new-cap
|
|
225
249
|
var encoder_1 = new opts.multipartEncoder(form_1);
|
|
@@ -247,9 +271,9 @@ function fetchHAR(har, opts) {
|
|
|
247
271
|
options.body = JSON.stringify(formBody_1);
|
|
248
272
|
}
|
|
249
273
|
}
|
|
250
|
-
else if (request.postData.text.length) {
|
|
251
|
-
// If we've got `files` map content present, and this post data content contains a valid data
|
|
252
|
-
// substitute the payload with that file instead of the using data URL.
|
|
274
|
+
else if ((_a = request.postData.text) === null || _a === void 0 ? void 0 : _a.length) {
|
|
275
|
+
// If we've got `files` map content present, and this post data content contains a valid data
|
|
276
|
+
// URL then we can substitute the payload with that file instead of the using data URL.
|
|
253
277
|
if (opts.files) {
|
|
254
278
|
var parsed = (0, parse_data_url_1["default"])(request.postData.text);
|
|
255
279
|
if (parsed) {
|
|
@@ -259,15 +283,16 @@ function fetchHAR(har, opts) {
|
|
|
259
283
|
options.body = fileContents;
|
|
260
284
|
}
|
|
261
285
|
else if (isFile(fileContents)) {
|
|
262
|
-
// `Readable.from` isn't available in browsers but the browser `Request` object can
|
|
263
|
-
// just fine without us having to mold it into shape.
|
|
286
|
+
// `Readable.from` isn't available in browsers but the browser `Request` object can
|
|
287
|
+
// handle `File` objects just fine without us having to mold it into shape.
|
|
264
288
|
if (isBrowser()) {
|
|
265
289
|
options.body = fileContents;
|
|
266
290
|
}
|
|
267
291
|
else {
|
|
268
292
|
// @ts-expect-error "Property 'from' does not exist on type 'typeof Readable'." but it does!
|
|
269
293
|
options.body = readable_stream_1.Readable.from(fileContents.stream());
|
|
270
|
-
// Supplying a polyfilled `File` stream into `Request.body` doesn't automatically
|
|
294
|
+
// Supplying a polyfilled `File` stream into `Request.body` doesn't automatically
|
|
295
|
+
// add `Content-Length`.
|
|
271
296
|
if (!headers.has('content-length')) {
|
|
272
297
|
headers.set('content-length', String(fileContents.size));
|
|
273
298
|
}
|
|
@@ -281,8 +306,8 @@ function fetchHAR(har, opts) {
|
|
|
281
306
|
}
|
|
282
307
|
}
|
|
283
308
|
}
|
|
284
|
-
// We automaticaly assume that the HAR that we have already has query parameters encoded within
|
|
285
|
-
// use the `URLSearchParams` API here for composing the query string.
|
|
309
|
+
// We automaticaly assume that the HAR that we have already has query parameters encoded within
|
|
310
|
+
// it so we do **not** use the `URLSearchParams` API here for composing the query string.
|
|
286
311
|
if ('queryString' in request && request.queryString.length) {
|
|
287
312
|
var urlObj = new URL(url);
|
|
288
313
|
var queryParams_1 = Array.from(urlObj.searchParams).map(function (_a) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fetch-har",
|
|
3
|
-
"version": "8.0
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"description": "Make a fetch request from a HAR definition",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -40,29 +40,36 @@
|
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@jsdevtools/host-environment": "^2.1.2",
|
|
42
42
|
"@jsdevtools/karma-config": "^3.1.7",
|
|
43
|
-
"@readme/eslint-config": "^
|
|
43
|
+
"@readme/eslint-config": "^9.0.0",
|
|
44
44
|
"@types/chai": "^4.3.1",
|
|
45
|
+
"@types/express": "^4.17.13",
|
|
45
46
|
"@types/mocha": "^9.1.1",
|
|
46
|
-
"@types/
|
|
47
|
+
"@types/multer": "^1.4.7",
|
|
48
|
+
"@types/node": "^18.0.0",
|
|
47
49
|
"@types/parse-data-url": "^3.0.0",
|
|
48
50
|
"@types/readable-stream": "^2.3.13",
|
|
49
51
|
"chai": "^4.3.4",
|
|
52
|
+
"datauri": "^4.1.0",
|
|
50
53
|
"eslint": "^8.14.0",
|
|
54
|
+
"express": "^4.18.1",
|
|
51
55
|
"fetch-mock": "^9.11.0",
|
|
52
56
|
"form-data": "^4.0.0",
|
|
53
57
|
"form-data-encoder": "^1.7.1",
|
|
54
58
|
"formdata-node": "^4.3.2",
|
|
55
59
|
"har-examples": "^3.1.1",
|
|
56
60
|
"isomorphic-fetch": "^3.0.0",
|
|
57
|
-
"mocha": "^
|
|
61
|
+
"mocha": "^10.0.0",
|
|
62
|
+
"multer": "^1.4.5-lts.1",
|
|
58
63
|
"nock": "^13.2.4",
|
|
59
64
|
"node-fetch": "^2.6.0",
|
|
60
65
|
"nyc": "^15.1.0",
|
|
61
66
|
"prettier": "^2.6.2",
|
|
62
|
-
"
|
|
67
|
+
"temp-dir": "^2.0.0",
|
|
68
|
+
"ts-loader": "^8.4.0",
|
|
63
69
|
"ts-node": "^10.7.0",
|
|
64
70
|
"typescript": "^4.6.3",
|
|
65
|
-
"undici": "^5.0.0"
|
|
71
|
+
"undici": "^5.0.0",
|
|
72
|
+
"webpack": "^4.46.0"
|
|
66
73
|
},
|
|
67
74
|
"browserslist": [
|
|
68
75
|
"last 2 versions"
|
package/src/index.ts
CHANGED
|
@@ -45,10 +45,12 @@ function isBuffer(value: any) {
|
|
|
45
45
|
|
|
46
46
|
function isFile(value: any) {
|
|
47
47
|
if (value instanceof File) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
/**
|
|
49
|
+
* The `Blob` polyfill on Node comes back as being an instanceof `File`. Because passing a Blob
|
|
50
|
+
* into a File will end up with a corrupted file we want to prevent this.
|
|
51
|
+
*
|
|
52
|
+
* This object identity crisis does not happen in the browser.
|
|
53
|
+
*/
|
|
52
54
|
return value.constructor.name === 'File';
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -64,8 +66,9 @@ function isFunction(value: any) {
|
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
/**
|
|
67
|
-
* We're
|
|
68
|
-
* regex in its main encoder that Safari doesn't support so it throws a fatal page
|
|
69
|
+
* We're using this library in here instead of loading it from `form-data-encoder` because that
|
|
70
|
+
* uses lookbehind regex in its main encoder that Safari doesn't support so it throws a fatal page
|
|
71
|
+
* exception.
|
|
69
72
|
*
|
|
70
73
|
* @license MIT
|
|
71
74
|
* @see {@link https://github.com/octet-stream/form-data-encoder/blob/master/lib/util/isFormData.ts}
|
|
@@ -123,17 +126,23 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
|
|
|
123
126
|
try {
|
|
124
127
|
return headers.append(header.name, header.value);
|
|
125
128
|
} catch (err) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
/**
|
|
130
|
+
* `Headers.append()` will throw errors if the header name is not a legal HTTP header name,
|
|
131
|
+
* like `X-API-KEY (Header)`. If that happens instead of tossing the error back out, we
|
|
132
|
+
* should silently just ignore
|
|
133
|
+
* it.
|
|
134
|
+
*/
|
|
129
135
|
}
|
|
130
136
|
});
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
if ('cookies' in request && request.cookies.length) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
140
|
+
/**
|
|
141
|
+
* As the browser fetch API can't set custom cookies for requests, they instead need to be
|
|
142
|
+
* defined on the document and passed into the request via `credentials: include`. Since this
|
|
143
|
+
* is a browser-specific quirk, that should only
|
|
144
|
+
* happen in browsers!
|
|
145
|
+
*/
|
|
137
146
|
if (isBrowser()) {
|
|
138
147
|
request.cookies.forEach(cookie => {
|
|
139
148
|
document.cookie = `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`;
|
|
@@ -159,11 +168,14 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
|
|
|
159
168
|
|
|
160
169
|
switch (request.postData.mimeType) {
|
|
161
170
|
case 'application/x-www-form-urlencoded':
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Since the content we're handling here is to be encoded as
|
|
173
|
+
* `application/x-www-form-urlencoded`, this should override any other `Content-Type`
|
|
174
|
+
* headers that are present in the HAR. This is how Postman handles this case when
|
|
175
|
+
* building code snippets!
|
|
176
|
+
*
|
|
177
|
+
* @see {@link https://github.com/github/fetch/issues/263#issuecomment-209530977}
|
|
178
|
+
*/
|
|
167
179
|
headers.set('Content-Type', request.postData.mimeType);
|
|
168
180
|
|
|
169
181
|
const encodedParams = new URLSearchParams();
|
|
@@ -176,34 +188,42 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
|
|
|
176
188
|
case 'multipart/form-data':
|
|
177
189
|
case 'multipart/mixed':
|
|
178
190
|
case 'multipart/related':
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
191
|
+
/**
|
|
192
|
+
* If there's a `Content-Type` header set we need to remove it. We're doing this because
|
|
193
|
+
* when we pass the form data object into `fetch` that'll set a proper `Content-Type`
|
|
194
|
+
* header for this request that also includes the boundary used on the content.
|
|
195
|
+
*
|
|
196
|
+
* If we don't do this, then consumers won't be able to parse out the payload because
|
|
197
|
+
* they won't know what the boundary to split on it.
|
|
198
|
+
*/
|
|
185
199
|
if (headers.has('Content-Type')) {
|
|
186
200
|
headers.delete('Content-Type');
|
|
187
201
|
}
|
|
188
202
|
|
|
189
203
|
const form = new FormData();
|
|
190
204
|
if (!isFormData(form)) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
/**
|
|
206
|
+
* The `form-data` NPM module returns one of two things: a native `FormData` API or its
|
|
207
|
+
* own polyfill. Unfortunately this polyfill does not support the full API of the native
|
|
208
|
+
* FormData object so when you load `form-data` within a browser environment you'll
|
|
209
|
+
* have two major differences in API:
|
|
210
|
+
*
|
|
211
|
+
* - The `.append()` API in `form-data` requires that the third argument is an object
|
|
212
|
+
* containing various, undocumented, options. In the browser, `.append()`'s third
|
|
213
|
+
* argument should only be present when the second is a `Blob` or `USVString`, and
|
|
214
|
+
* when it is present, it should be a filename string.
|
|
215
|
+
* - `form-data` does not expose an `.entries()` API, so the only way to retrieve data
|
|
216
|
+
* out of it for construction of boundary-separated payload content is to use its
|
|
217
|
+
* `.pipe()` API. Since the browser doesn't have this API, you'll be unable to
|
|
218
|
+
* retrieve data out of it.
|
|
219
|
+
*
|
|
220
|
+
* Now since the native `FormData` API is iterable, and has the `.entries()` iterator,
|
|
221
|
+
* we can easily detect if we have a native copy of the FormData API. It's for all of
|
|
222
|
+
* these reasons that we're opting to hard crash here because supporting this
|
|
223
|
+
* non-compliant API is more trouble than its worth.
|
|
224
|
+
*
|
|
225
|
+
* @see {@link https://github.com/form-data/form-data/issues/124}
|
|
226
|
+
*/
|
|
207
227
|
throw new Error(
|
|
208
228
|
"We've detected you're using a non-spec compliant FormData library. We recommend polyfilling FormData with https://npm.im/formdata-node"
|
|
209
229
|
);
|
|
@@ -214,10 +234,10 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
|
|
|
214
234
|
if (opts.files && param.fileName in opts.files) {
|
|
215
235
|
const fileContents = opts.files[param.fileName];
|
|
216
236
|
|
|
217
|
-
// If the file we've got available to us is a Buffer then we need to convert it so
|
|
218
|
-
// API can use it.
|
|
237
|
+
// If the file we've got available to us is a Buffer then we need to convert it so
|
|
238
|
+
// that the FormData API can use it.
|
|
219
239
|
if (isBuffer(fileContents)) {
|
|
220
|
-
form.
|
|
240
|
+
form.append(
|
|
221
241
|
param.name,
|
|
222
242
|
new File([fileContents], param.fileName, {
|
|
223
243
|
type: param.contentType || null,
|
|
@@ -227,7 +247,7 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
|
|
|
227
247
|
|
|
228
248
|
return;
|
|
229
249
|
} else if (isFile(fileContents)) {
|
|
230
|
-
form.
|
|
250
|
+
form.append(param.name, fileContents as Blob, param.fileName);
|
|
231
251
|
return;
|
|
232
252
|
}
|
|
233
253
|
|
|
@@ -238,8 +258,8 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
|
|
|
238
258
|
let paramBlob;
|
|
239
259
|
const parsed = parseDataUrl(param.value);
|
|
240
260
|
if (parsed) {
|
|
241
|
-
// If we were able to parse out this data URL we don't need to transform its data
|
|
242
|
-
// `Blob` because that supports data URLs already.
|
|
261
|
+
// If we were able to parse out this data URL we don't need to transform its data
|
|
262
|
+
// into a buffer for `Blob` because that supports data URLs already.
|
|
243
263
|
paramBlob = new Blob([param.value], { type: parsed.contentType || param.contentType || null });
|
|
244
264
|
} else {
|
|
245
265
|
paramBlob = new Blob([param.value], { type: param.contentType || null });
|
|
@@ -257,11 +277,14 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
|
|
|
257
277
|
form.append(param.name, param.value);
|
|
258
278
|
});
|
|
259
279
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
280
|
+
/**
|
|
281
|
+
* If a the `fetch` polyfill that's being used here doesn't have spec-compliant handling
|
|
282
|
+
* for the `FormData` API (like `node-fetch@2`), then you should pass in a handler (like
|
|
283
|
+
* the `form-data-encoder` library) to transform its contents into something that can be
|
|
284
|
+
* used with the `Request` object.
|
|
285
|
+
*
|
|
286
|
+
* @see {@link https://www.npmjs.com/package/formdata-node}
|
|
287
|
+
*/
|
|
265
288
|
if (opts.multipartEncoder) {
|
|
266
289
|
// eslint-disable-next-line new-cap
|
|
267
290
|
const encoder = new opts.multipartEncoder(form);
|
|
@@ -290,9 +313,9 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
|
|
|
290
313
|
|
|
291
314
|
options.body = JSON.stringify(formBody);
|
|
292
315
|
}
|
|
293
|
-
} else if (request.postData.text
|
|
294
|
-
// If we've got `files` map content present, and this post data content contains a valid data
|
|
295
|
-
// substitute the payload with that file instead of the using data URL.
|
|
316
|
+
} else if (request.postData.text?.length) {
|
|
317
|
+
// If we've got `files` map content present, and this post data content contains a valid data
|
|
318
|
+
// URL then we can substitute the payload with that file instead of the using data URL.
|
|
296
319
|
if (opts.files) {
|
|
297
320
|
const parsed = parseDataUrl(request.postData.text) as DataURL;
|
|
298
321
|
if (parsed) {
|
|
@@ -301,15 +324,16 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
|
|
|
301
324
|
if (isBuffer(fileContents)) {
|
|
302
325
|
options.body = fileContents;
|
|
303
326
|
} else if (isFile(fileContents)) {
|
|
304
|
-
// `Readable.from` isn't available in browsers but the browser `Request` object can
|
|
305
|
-
// just fine without us having to mold it into shape.
|
|
327
|
+
// `Readable.from` isn't available in browsers but the browser `Request` object can
|
|
328
|
+
// handle `File` objects just fine without us having to mold it into shape.
|
|
306
329
|
if (isBrowser()) {
|
|
307
330
|
options.body = fileContents;
|
|
308
331
|
} else {
|
|
309
332
|
// @ts-expect-error "Property 'from' does not exist on type 'typeof Readable'." but it does!
|
|
310
333
|
options.body = Readable.from((fileContents as File).stream());
|
|
311
334
|
|
|
312
|
-
// Supplying a polyfilled `File` stream into `Request.body` doesn't automatically
|
|
335
|
+
// Supplying a polyfilled `File` stream into `Request.body` doesn't automatically
|
|
336
|
+
// add `Content-Length`.
|
|
313
337
|
if (!headers.has('content-length')) {
|
|
314
338
|
headers.set('content-length', String((fileContents as File).size));
|
|
315
339
|
}
|
|
@@ -325,8 +349,8 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
|
|
|
325
349
|
}
|
|
326
350
|
}
|
|
327
351
|
|
|
328
|
-
// We automaticaly assume that the HAR that we have already has query parameters encoded within
|
|
329
|
-
// use the `URLSearchParams` API here for composing the query string.
|
|
352
|
+
// We automaticaly assume that the HAR that we have already has query parameters encoded within
|
|
353
|
+
// it so we do **not** use the `URLSearchParams` API here for composing the query string.
|
|
330
354
|
if ('queryString' in request && request.queryString.length) {
|
|
331
355
|
const urlObj = new URL(url);
|
|
332
356
|
|