fetch-har 8.0.3 → 8.1.2

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