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 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
- // The `Blob` polyfill on Node comes back as being an instanceof `File`. Because passing a Blob into
55
- // a File will end up with a corrupted file we want to prevent this.
56
- //
57
- // This object identity crisis does not happen in the browser.
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 loading this library in here instead of loading it from `form-data-encoder` because that uses lookbehind
71
- * regex in its main encoder that Safari doesn't support so it throws a fatal page exception.
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
- // `Headers.append()` will throw errors if the header name is not a legal HTTP header name, like
110
- // `X-API-KEY (Header)`. If that happens instead of tossing the error back out, we should silently just ignore
111
- // it.
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
- // As the browser fetch API can't set custom cookies for requests, they instead need to be defined on the document
117
- // and passed into the request via `credentials: include`. Since this is a browser-specific quirk, that should only
118
- // happen in browsers!
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
- // Since the content we're handling here is to be encoded as `application/x-www-form-urlencoded`, this should
140
- // override any other Content-Type headers that are present in the HAR. This is how Postman handles this case
141
- // when building code snippets!
142
- //
143
- // https://github.com/github/fetch/issues/263#issuecomment-209530977
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
- // If there's a Content-Type header set remove it. We're doing this because when we pass the form data object
154
- // into `fetch` that'll set a proper `Content-Type` header for this request that also includes the boundary
155
- // used on the content.
156
- //
157
- // If we don't do this, then consumers won't be able to parse out the payload because they won't know what
158
- // the boundary to split on it.
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
- // The `form-data` NPM module returns one of two things: a native `FormData` API or its own polyfill.
165
- // Unfortunately this polyfill does not support the full API of the native FormData object so when you load
166
- // `form-data` within a browser environment you'll have two major differences in API:
167
- //
168
- // * The `.append()` API in `form-data` requires that the third argument is an object containing various,
169
- // undocumented, options. In the browser, `.append()`'s third argument should only be present when the
170
- // second is a `Blob` or `USVString`, and when it is present, it should be a filename string.
171
- // * `form-data` does not expose an `.entries()` API, so the only way to retrieve data out of it for
172
- // construction of boundary-separated payload content is to use its `.pipe()` API. Since the browser
173
- // doesn't have this API, you'll be unable to retrieve data out of it.
174
- //
175
- // Now since the native `FormData` API is iterable, and has the `.entries()` iterator, we can easily detect
176
- // if we have a native copy of the FormData API. It's for all of these reasons that we're opting to hard
177
- // crash here because supporting this non-compliant API is more trouble than its worth.
178
- //
179
- // https://github.com/form-data/form-data/issues/124
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 that the FormData
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.set(param.name, new File([fileContents], param.fileName, {
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.set(param.name, fileContents, param.fileName);
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 into a buffer for
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
- // If a the `fetch` polyfill that's being used here doesn't have spec-compliant handling for the `FormData`
219
- // API (like `node-fetch@2`), then you should pass in a handler (like the `form-data-encoder` library) to
220
- // transform its contents into something that can be used with the `Request` object.
221
- //
222
- // https://www.npmjs.com/package/formdata-node
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 URL then we can
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 handle `File` objects
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 add `Content-Length`.
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 it so we do **not**
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.1",
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": "^8.7.3",
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/node": "^17.0.29",
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": "^9.2.2",
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
- "ts-loader": "^9.3.0",
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
- // The `Blob` polyfill on Node comes back as being an instanceof `File`. Because passing a Blob into
49
- // a File will end up with a corrupted file we want to prevent this.
50
- //
51
- // This object identity crisis does not happen in the browser.
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 loading this library in here instead of loading it from `form-data-encoder` because that uses lookbehind
68
- * regex in its main encoder that Safari doesn't support so it throws a fatal page exception.
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
- // `Headers.append()` will throw errors if the header name is not a legal HTTP header name, like
127
- // `X-API-KEY (Header)`. If that happens instead of tossing the error back out, we should silently just ignore
128
- // it.
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
- // As the browser fetch API can't set custom cookies for requests, they instead need to be defined on the document
135
- // and passed into the request via `credentials: include`. Since this is a browser-specific quirk, that should only
136
- // happen in browsers!
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
- // Since the content we're handling here is to be encoded as `application/x-www-form-urlencoded`, this should
163
- // override any other Content-Type headers that are present in the HAR. This is how Postman handles this case
164
- // when building code snippets!
165
- //
166
- // https://github.com/github/fetch/issues/263#issuecomment-209530977
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
- // If there's a Content-Type header set remove it. We're doing this because when we pass the form data object
180
- // into `fetch` that'll set a proper `Content-Type` header for this request that also includes the boundary
181
- // used on the content.
182
- //
183
- // If we don't do this, then consumers won't be able to parse out the payload because they won't know what
184
- // the boundary to split on it.
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
- // The `form-data` NPM module returns one of two things: a native `FormData` API or its own polyfill.
192
- // Unfortunately this polyfill does not support the full API of the native FormData object so when you load
193
- // `form-data` within a browser environment you'll have two major differences in API:
194
- //
195
- // * The `.append()` API in `form-data` requires that the third argument is an object containing various,
196
- // undocumented, options. In the browser, `.append()`'s third argument should only be present when the
197
- // second is a `Blob` or `USVString`, and when it is present, it should be a filename string.
198
- // * `form-data` does not expose an `.entries()` API, so the only way to retrieve data out of it for
199
- // construction of boundary-separated payload content is to use its `.pipe()` API. Since the browser
200
- // doesn't have this API, you'll be unable to retrieve data out of it.
201
- //
202
- // Now since the native `FormData` API is iterable, and has the `.entries()` iterator, we can easily detect
203
- // if we have a native copy of the FormData API. It's for all of these reasons that we're opting to hard
204
- // crash here because supporting this non-compliant API is more trouble than its worth.
205
- //
206
- // https://github.com/form-data/form-data/issues/124
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 that the FormData
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.set(
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.set(param.name, fileContents as Blob, param.fileName);
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 into a buffer for
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
- // If a the `fetch` polyfill that's being used here doesn't have spec-compliant handling for the `FormData`
261
- // API (like `node-fetch@2`), then you should pass in a handler (like the `form-data-encoder` library) to
262
- // transform its contents into something that can be used with the `Request` object.
263
- //
264
- // https://www.npmjs.com/package/formdata-node
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.length) {
294
- // If we've got `files` map content present, and this post data content contains a valid data URL then we can
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 handle `File` objects
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 add `Content-Length`.
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 it so we do **not**
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