fetch-har 5.0.5 → 6.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,37 @@
1
+ ## 6.1.0 (2022-01-27)
2
+
3
+ * feat: automatically polyfilling FormData with formdata-node if present (#241) ([da94dfc](https://github.com/readmeio/fetch-har/commit/da94dfc)), closes [#241](https://github.com/readmeio/fetch-har/issues/241)
4
+
5
+
6
+
7
+ ## <small>6.0.2 (2022-01-27)</small>
8
+
9
+ * fix: typo in the optional formdata-node package ([d05bcda](https://github.com/readmeio/fetch-har/commit/d05bcda))
10
+
11
+
12
+
13
+ ## <small>6.0.1 (2022-01-26)</small>
14
+
15
+ * fix: regression in query parameter handling (#242) ([6c6a147](https://github.com/readmeio/fetch-har/commit/6c6a147)), closes [#242](https://github.com/readmeio/fetch-har/issues/242)
16
+
17
+
18
+
19
+ ## <small>6.0.0 (2022-01-25)</small>
20
+
21
+ > **BREAKING CHANGE**
22
+ >
23
+ > With this release we no longer support the [form-data](https://npm.im/form-data) package as it's handling of `multipart/form-data` is non-compliant with the browser `FormData` API. Please use [formdata-node](https://npm.im/formdata-node) or [formdata-polyfill](https://npm.im/formdata-polyfill) instead.
24
+ >
25
+ > Additionally the optional `userAgent` option has been reworked into a new options object that also supports a new `files` mapping array for augmenting binaries or file uploads within a HAR with a file buffers or the `File` API. See the readme for documentation.
26
+
27
+ * fix: handling of multipart/form-data requests and how we interact with FormData ([#237](https://github.com/readmeio/fetch-har/pull/237))
28
+ * chore(deps-dev): bumping node-fetch ([#240]((https://github.com/readmeio/fetch-har/pull/240))
29
+ * fix: improper handling of query strings ([#239](https://github.com/readmeio/fetch-har/pull/239))
30
+ * feat: extending the new files option to allow overriding raw binary payloads ([#238](https://github.com/readmeio/fetch-har/pull/238))
31
+
32
+
33
+
34
+
1
35
  ## <small>5.0.5 (2022-01-03)</small>
2
36
 
3
37
  * chore: removing the publishConfig from the package file ([05df1e7](https://github.com/readmeio/fetch-har/commit/05df1e7))
package/README.md CHANGED
@@ -13,13 +13,12 @@ npm install --save fetch-har
13
13
 
14
14
  ## Usage
15
15
  ```js
16
- const fetchHar = require('fetch-har');
16
+ require('isomorphic-fetch');
17
17
 
18
- // If executing from an environment without `fetch`, you'll need to polyfill.
19
- global.fetch = require('node-fetch');
20
- global.Headers = require('node-fetch').Headers;
21
- global.Request = require('node-fetch').Request;
22
- global.FormData = require('form-data');
18
+ // If executing from an environment that dodoesn't normally provide `fetch()`
19
+ // we'll automatically polyfill in the `Blob`, `File`, and `FormData` APIs
20
+ // with the optional `formdata-node` package (provided you've installed it).
21
+ const fetchHar = require('.');
23
22
 
24
23
  const har = {
25
24
  log: {
@@ -36,7 +35,10 @@ const har = {
36
35
  value: 'application/json',
37
36
  },
38
37
  ],
39
- queryString: [{ name: 'a', value: 1 }, { name: 'b', value: 2 }],
38
+ queryString: [
39
+ { name: 'a', value: 1 },
40
+ { name: 'b', value: 2 },
41
+ ],
40
42
  postData: {
41
43
  mimeType: 'application/json',
42
44
  text: '{"id":8,"category":{"id":6,"name":"name"},"name":"name"}',
@@ -50,22 +52,48 @@ const har = {
50
52
  };
51
53
 
52
54
  fetchHar(har)
53
- .then(request => request.json())
55
+ .then(res => res.json())
54
56
  .then(console.log);
55
57
  ```
56
58
 
57
- ### `fetchHar(har, userAgent) => Promise`
59
+ ### API
60
+ If you are executing `fetch-har` in a browser environment that supports the [FormData API](https://developer.mozilla.org/en-US/docs/Web/API/FormData) then you don't need to do anything. If you arent, however, you'll need to polyfill it.
61
+
62
+ Unfortunately the most popular NPM package [form-data](https://npm.im/form-data) ships with a [non-spec compliant API](https://github.com/form-data/form-data/issues/124), and for this we don't recommend you use it, as if you use `fetch-har` to upload files it may not work.
63
+
64
+ Though we recommend either [formdata-node](https://npm.im/formdata-node) or [formdata-polyfill](https://npm.im/formdata-polyfill) we prefer [formdata-node](https://npm.im/formdata-node) right now as it's CJS-compatible.
65
+
66
+ #### Options
67
+ ##### userAgent
68
+ A custom `User-Agent` header to apply to your request. Please note that browsers have their own handling for these headers in `fetch()` calls so it may not work everywhere; it will always be sent in Node however.
69
+
70
+ ```js
71
+ await fetchHar(har, { userAgent: 'my-client/1.0' });
72
+ ```
73
+
74
+ ##### files
75
+ An optional object map you can supply to use for `multipart/form-data` file uploads in leu of relying on if the HAR you have has [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs). It supports Node file buffers and the [File](https://developer.mozilla.org/en-US/docs/Web/API/File) API.
76
+
77
+ ```js
78
+ await fetchHar(har, { files: {
79
+ 'owlbert.png': await fs.readFile('./owlbert.png'),
80
+ 'file.txt': document.querySelector('#some-file-input').files[0],
81
+ } });
82
+ ```
58
83
 
59
- - `har` is a [har](https://en.wikipedia.org/wiki/.har) file format.
60
- - `userAgent` is an optional user agent string to let you declare where the request is coming from.
84
+ If you don't supply this option `fetch-har` will fallback to the data URL present within the supplied HAR. If no `files` option is present, and no data URL (via `param.value`) is present in the HAR, a fatal exception will be thrown.
61
85
 
62
- Performs a fetch request from a given HAR definition. HAR definitions can be used to list lots of requests but we only use the first from the `log.entries` array.
86
+ ##### multipartEncoder
87
+ > ❗ If you are using `fetch-har` in Node you may need this option to execute `multipart/form-data` requests!
63
88
 
64
- ### `fetchHar.constructRequest(har, userAgent) => Request`
89
+ If you are running `fetch-har` within a Node environment and you're using `node-fetch@2`, or another `fetch` polyfill that does not support a spec-compliant `FormData` API, you will need to specify an encoder that will transform your `FormData` object into something that can be used with [Request.body](https://developer.mozilla.org/en-US/docs/Web/API/Request/body).
65
90
 
66
- - `har` is a [har](https://en.wikipedia.org/wiki/.har) file format.
67
- - `userAgent` is an optional user agent string to let you declare where the request is coming from.
91
+ We recommend [form-data-encoder](https://npm.im/form-data-encoder).
68
92
 
69
- We also export a second function which is used to construct a [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object from your HAR.
93
+ ```js
94
+ const { FormDataEncoder } = require('form-data-encoder');
95
+
96
+ await fetchHar(har, { multipartEncoder: FormDataEncoder });
97
+ ```
70
98
 
71
- This function is mainly exported for testing purposes but could be useful if you want to construct a request but do not want to execute it right away.
99
+ You do **not**, and shouldn't, need to use this option in browser environments.
package/example.js CHANGED
@@ -1,11 +1,10 @@
1
1
  /* eslint-disable import/no-extraneous-dependencies, no-console */
2
- const fetchHar = require('.');
2
+ require('isomorphic-fetch');
3
3
 
4
- // If executing from an environment without `fetch`, you'll need to polyfill
5
- global.fetch = require('node-fetch');
6
- global.Headers = require('node-fetch').Headers;
7
- global.Request = require('node-fetch').Request;
8
- global.FormData = require('form-data');
4
+ // If executing from an environment that dodoesn't normally provide `fetch()`
5
+ // we'll automatically polyfill in the `Blob`, `File`, and `FormData` APIs
6
+ // with the optional `formdata-node` package (provided you've installed it).
7
+ const fetchHar = require('.');
9
8
 
10
9
  const har = {
11
10
  log: {
@@ -39,5 +38,5 @@ const har = {
39
38
  };
40
39
 
41
40
  fetchHar(har)
42
- .then(request => request.json())
41
+ .then(res => res.json())
43
42
  .then(console.log);
package/index.js CHANGED
@@ -1,7 +1,87 @@
1
- /* eslint-disable no-case-declarations */
1
+ const { Readable } = require('readable-stream');
2
2
  const parseDataUrl = require('parse-data-url');
3
3
 
4
- function constructRequest(har, userAgent = false) {
4
+ if (!globalThis.Blob) {
5
+ try {
6
+ // eslint-disable-next-line import/no-extraneous-dependencies
7
+ globalThis.Blob = require('formdata-node').Blob;
8
+ } catch (e) {
9
+ throw new Error(
10
+ 'Since you do not have the Blob API available in this environment you must install the optional `formdata-node` dependency.'
11
+ );
12
+ }
13
+ }
14
+
15
+ if (!globalThis.File) {
16
+ try {
17
+ // eslint-disable-next-line import/no-extraneous-dependencies
18
+ globalThis.File = require('formdata-node').File;
19
+ } catch (e) {
20
+ throw new Error(
21
+ 'Since you do not have the File API available in this environment you must install the optional `formdata-node` dependency.'
22
+ );
23
+ }
24
+ }
25
+
26
+ if (!globalThis.FormData) {
27
+ try {
28
+ // eslint-disable-next-line import/no-extraneous-dependencies
29
+ globalThis.FormData = require('formdata-node').FormData;
30
+ } catch (e) {
31
+ throw new Error(
32
+ 'Since you do not have the FormData API available in this environment you must install the optional `formdata-node` dependency.'
33
+ );
34
+ }
35
+ }
36
+
37
+ function isBrowser() {
38
+ return typeof window !== 'undefined' && typeof document !== 'undefined';
39
+ }
40
+
41
+ function isBuffer(value) {
42
+ return typeof Buffer !== 'undefined' && Buffer.isBuffer(value);
43
+ }
44
+
45
+ function isFile(value) {
46
+ if (value instanceof File) {
47
+ // The `Blob` polyfill on Node comes back as being an instanceof `File`. Because passing a Blob into
48
+ // a File will end up with a corrupted file we want to prevent this.
49
+ //
50
+ // This object identity crisis does not happen in the browser.
51
+ return value.constructor.name === 'File';
52
+ }
53
+
54
+ return false;
55
+ }
56
+
57
+ /**
58
+ * @license MIT
59
+ * @see {@link https://github.com/octet-stream/form-data-encoder/blob/master/lib/util/isFunction.ts}
60
+ */
61
+ function isFunction(value) {
62
+ return typeof value === 'function';
63
+ }
64
+
65
+ /**
66
+ * We're loading this library in here instead of loading it from `form-data-encoder` because that uses lookbehind
67
+ * regex in its main encoder that Safari doesn't support so it throws a fatal page exception.
68
+ *
69
+ * @license MIT
70
+ * @see {@link https://github.com/octet-stream/form-data-encoder/blob/master/lib/util/isFormData.ts}
71
+ */
72
+ function isFormData(value) {
73
+ return (
74
+ value &&
75
+ isFunction(value.constructor) &&
76
+ value[Symbol.toStringTag] === 'FormData' && // eslint-disable-line compat/compat
77
+ isFunction(value.append) &&
78
+ isFunction(value.getAll) &&
79
+ isFunction(value.entries) &&
80
+ isFunction(value[Symbol.iterator]) // eslint-disable-line compat/compat
81
+ );
82
+ }
83
+
84
+ function constructRequest(har, opts = { userAgent: false, files: false, multipartEncoder: false }) {
5
85
  if (!har) throw new Error('Missing HAR definition');
6
86
  if (!har.log || !har.log.entries || !har.log.entries.length) throw new Error('Missing log.entries array');
7
87
 
@@ -31,7 +111,7 @@ function constructRequest(har, userAgent = false) {
31
111
  // As the browser fetch API can't set custom cookies for requests, they instead need to be defined on the document
32
112
  // and passed into the request via `credentials: include`. Since this is a browser-specific quirk, that should only
33
113
  // happen in browsers!
34
- if (typeof window !== 'undefined' && typeof document !== 'undefined') {
114
+ if (isBrowser()) {
35
115
  request.cookies.forEach(cookie => {
36
116
  document.cookie = `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`;
37
117
  });
@@ -68,7 +148,10 @@ function constructRequest(har, userAgent = false) {
68
148
  options.body = encodedParams.toString();
69
149
  break;
70
150
 
151
+ case 'multipart/alternative':
71
152
  case 'multipart/form-data':
153
+ case 'multipart/mixed':
154
+ case 'multipart/related':
72
155
  // If there's a Content-Type header set remove it. We're doing this because when we pass the form data object
73
156
  // into `fetch` that'll set a proper `Content-Type` header for this request that also includes the boundary
74
157
  // used on the content.
@@ -79,59 +162,93 @@ function constructRequest(har, userAgent = false) {
79
162
  headers.delete('Content-Type');
80
163
  }
81
164
 
82
- // The `form-data` NPM module returns one of two things: a native `FormData` API, or its own polyfill. Since
83
- // the polyfill does not support the full API of the native FormData object, when you load `form-data` within
84
- // a browser environment you'll have two major differences in API:
85
- //
86
- // * The `.append()` API in `form-data` requires that the third argument is an object containing various,
87
- // undocumented, options. In the browser, `.append()`'s third argument should only be present when the
88
- // second is a `Blob` or `USVString`, and when it is present, it should be a filename string.
89
- // * `form-data` does not expose an `.entries()` API, so the only way to retrieve data out of it for
90
- // construction of boundary-separated payload content is to use its `.pipe()` API. Since the browser
91
- // doesn't have this API, you'll be unable to retrieve data out of it.
92
- //
93
- // Now since the native `FormData` API is iterable, and has the `.entries()` iterator, we can easily detect
94
- // what version of the FormData API we have access to by looking for this and constructing a simple wrapper
95
- // to disconnect some of this logic so you can work against a single, consistent API.
96
- //
97
- // Having to do this isn't fun, but it's the only way you can write code to work with `multipart/form-data`
98
- // content under a server and browser.
99
165
  const form = new FormData();
100
- const isNativeFormData = typeof form[Symbol.iterator] === 'function';
166
+ if (!isFormData(form)) {
167
+ // The `form-data` NPM module returns one of two things: a native `FormData` API or its own polyfill.
168
+ // Unfortunately this polyfill does not support the full API of the native FormData object so when you load
169
+ // `form-data` within a browser environment you'll have two major differences in API:
170
+ //
171
+ // * The `.append()` API in `form-data` requires that the third argument is an object containing various,
172
+ // undocumented, options. In the browser, `.append()`'s third argument should only be present when the
173
+ // second is a `Blob` or `USVString`, and when it is present, it should be a filename string.
174
+ // * `form-data` does not expose an `.entries()` API, so the only way to retrieve data out of it for
175
+ // construction of boundary-separated payload content is to use its `.pipe()` API. Since the browser
176
+ // doesn't have this API, you'll be unable to retrieve data out of it.
177
+ //
178
+ // Now since the native `FormData` API is iterable, and has the `.entries()` iterator, we can easily detect
179
+ // if we have a native copy of the FormData API. It's for all of these reasons that we're opting to hard
180
+ // crash here because supporting this non-compliant API is more trouble than its worth.
181
+ //
182
+ // https://github.com/form-data/form-data/issues/124
183
+ throw new Error(
184
+ "We've detected you're using a non-spec compliant FormData library. We recommend polyfilling FormData with https://npm.im/formdata-node"
185
+ );
186
+ }
101
187
 
102
188
  request.postData.params.forEach(param => {
103
- if ('fileName' in param && !('value' in param)) {
104
- throw new Error(
105
- "The supplied HAR has a postData parameter with `fileName`, but no `value` content. Since this library doesn't have access to the filesystem, it can't fetch that file."
106
- );
107
- }
108
-
109
- // If the incoming parameter is a file, and that files value is a data URL, we should decode that and set
110
- // the contents of the value in the HAR to the actual contents of the file.
111
189
  if ('fileName' in param) {
112
- const parsed = parseDataUrl(param.value);
113
- if (parsed) {
114
- // eslint-disable-next-line no-param-reassign
115
- param.value = parsed.toBuffer().toString();
116
- }
117
- }
190
+ if (opts.files && param.fileName in opts.files) {
191
+ const fileContents = opts.files[param.fileName];
192
+
193
+ // If the file we've got available to us is a Buffer then we need to convert it so that the FormData
194
+ // API can use it.
195
+ if (isBuffer(fileContents)) {
196
+ form.set(
197
+ param.name,
198
+ new File([fileContents], param.fileName, {
199
+ type: param.contentType || null,
200
+ }),
201
+ param.fileName
202
+ );
203
+
204
+ return;
205
+ } else if (isFile(fileContents)) {
206
+ form.set(param.name, fileContents, param.fileName);
207
+ return;
208
+ }
209
+
210
+ throw new TypeError(
211
+ 'An unknown object has been supplied into the `files` config for use. We only support instances of the File API and Node Buffer objects.'
212
+ );
213
+ } else if ('value' in param) {
214
+ let paramBlob;
215
+ const parsed = parseDataUrl(param.value);
216
+ if (parsed) {
217
+ // If we were able to parse out this data URL we don't need to transform its data into a buffer for
218
+ // `Blob` because that supports data URLs already.
219
+ paramBlob = new Blob([param.value], { type: parsed.contentType || param.contentType || null });
220
+ } else {
221
+ paramBlob = new Blob([param.value], { type: param.contentType || null });
222
+ }
118
223
 
119
- if (isNativeFormData) {
120
- if ('fileName' in param) {
121
- const paramBlob = new Blob([param.value], { type: param.contentType || null });
122
224
  form.append(param.name, paramBlob, param.fileName);
123
- } else {
124
- form.append(param.name, param.value);
225
+ return;
125
226
  }
126
- } else {
127
- form.append(param.name, param.value || '', {
128
- filename: param.fileName || null,
129
- contentType: param.contentType || null,
130
- });
227
+
228
+ throw new Error(
229
+ "The supplied HAR has a postData parameter with `fileName`, but neither `value` content within the HAR or any file buffers were supplied with the `files` option. Since this library doesn't have access to the filesystem, it can't fetch that file."
230
+ );
131
231
  }
232
+
233
+ form.append(param.name, param.value);
132
234
  });
133
235
 
134
- options.body = form;
236
+ // If a the `fetch` polyfill that's being used here doesn't have spec-compliant handling for the `FormData`
237
+ // API (like `node-fetch@2`), then you should pass in a handler (like the `form-data-encoder` library) to
238
+ // transform its contents into something that can be used with the `Request` object.
239
+ //
240
+ // https://www.npmjs.com/package/formdata-node
241
+ if (opts.multipartEncoder) {
242
+ // eslint-disable-next-line new-cap
243
+ const encoder = new opts.multipartEncoder(form);
244
+ Object.keys(encoder.headers).forEach(header => {
245
+ headers.set(header, encoder.headers[header]);
246
+ });
247
+
248
+ options.body = Readable.from(encoder);
249
+ } else {
250
+ options.body = form;
251
+ }
135
252
  break;
136
253
 
137
254
  default:
@@ -148,27 +265,62 @@ function constructRequest(har, userAgent = false) {
148
265
 
149
266
  options.body = JSON.stringify(formBody);
150
267
  }
151
- } else {
152
- options.body = request.postData.text;
268
+ } else if (request.postData.text.length) {
269
+ // If we've got `files` map content present, and this post data content contains a valid data URL then we can
270
+ // substitute the payload with that file instead of the using data URL.
271
+ if (opts.files) {
272
+ const parsed = parseDataUrl(request.postData.text);
273
+ if (parsed && 'name' in parsed && parsed.name in opts.files) {
274
+ const fileContents = opts.files[parsed.name];
275
+ if (isBuffer(fileContents)) {
276
+ options.body = fileContents;
277
+ } else if (isFile(fileContents)) {
278
+ // `Readable.from` isn't available in browsers but the browser `Request` object can handle `File` objects
279
+ // just fine without us having to mold it into shape.
280
+ if (isBrowser()) {
281
+ options.body = fileContents;
282
+ } else {
283
+ options.body = Readable.from(fileContents.stream());
284
+
285
+ // Supplying a polyfilled `File` stream into `Request.body` doesn't automatically add `Content-Length`.
286
+ if (!headers.has('content-length')) {
287
+ headers.set('content-length', fileContents.size);
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+
294
+ if (typeof options.body === 'undefined') {
295
+ options.body = request.postData.text;
296
+ }
153
297
  }
154
298
  }
155
299
 
300
+ // We automaticaly assume that the HAR that we have already has query parameters encoded within it so we do **not**
301
+ // use the `URLSearchParams` API here for composing the query string.
156
302
  if ('queryString' in request && request.queryString.length) {
157
- const query = request.queryString.map(q => `${q.name}=${q.value}`).join('&');
158
- querystring = `?${query}`;
303
+ const urlObj = new URL(url);
304
+
305
+ const queryParams = Array.from(urlObj.searchParams).map(([k, v]) => `${k}=${v}`);
306
+ request.queryString.forEach(q => {
307
+ queryParams.push(`${q.name}=${q.value}`);
308
+ });
309
+
310
+ querystring = queryParams.join('&');
159
311
  }
160
312
 
161
- if (userAgent) {
162
- headers.append('User-Agent', userAgent);
313
+ if (opts.userAgent) {
314
+ headers.append('User-Agent', opts.userAgent);
163
315
  }
164
316
 
165
317
  options.headers = headers;
166
318
 
167
- return new Request(`${url}${querystring}`, options);
319
+ return new Request(`${url.split('?')[0]}${querystring ? `?${querystring}` : ''}`, options);
168
320
  }
169
321
 
170
- function fetchHar(har, userAgent) {
171
- return fetch(constructRequest(har, userAgent));
322
+ function fetchHar(har, opts = { userAgent: false, files: false, multipartEncoder: false }) {
323
+ return fetch(constructRequest(har, opts));
172
324
  }
173
325
 
174
326
  module.exports = fetchHar;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fetch-har",
3
- "version": "5.0.5",
3
+ "version": "6.1.0",
4
4
  "description": "Make a fetch request from a HAR definition",
5
5
  "main": "index.js",
6
6
  "engines": {
@@ -12,7 +12,10 @@
12
12
  "prettier": "prettier --list-different --write \"./**.js\"",
13
13
  "release": "npx conventional-changelog-cli -i CHANGELOG.md -s",
14
14
  "serve": "node __tests__/server.js",
15
- "test": "jest --coverage"
15
+ "test:browser": "karma start --single-run",
16
+ "test:browser:chrome": "karma start --browsers=Chrome --single-run=false",
17
+ "test:browser:debug": "karma start --single-run=false",
18
+ "test": "nyc mocha"
16
19
  },
17
20
  "repository": {
18
21
  "type": "git",
@@ -24,37 +27,32 @@
24
27
  },
25
28
  "homepage": "https://github.com/readmeio/fetch-har#readme",
26
29
  "dependencies": {
27
- "parse-data-url": "^4.0.1"
30
+ "parse-data-url": "^4.0.1",
31
+ "readable-stream": "^3.6.0"
32
+ },
33
+ "optionalDependencies": {
34
+ "formdata-node": "^4.3.2"
28
35
  },
29
36
  "devDependencies": {
30
- "@readme/eslint-config": "^8.0.2",
31
- "body-parser": "^1.19.0",
32
- "cookie-parser": "^1.4.5",
33
- "eslint": "^8.2.0",
34
- "eslint-plugin-compat": "^4.0.0",
35
- "express": "^4.17.1",
37
+ "@jsdevtools/host-environment": "^2.1.2",
38
+ "@jsdevtools/karma-config": "^3.1.7",
39
+ "@readme/eslint-config": "^8.1.2",
40
+ "chai": "^4.3.4",
41
+ "eslint": "^8.7.0",
42
+ "eslint-plugin-compat": "^4.0.1",
43
+ "eslint-plugin-mocha": "^10.0.3",
36
44
  "form-data": "^4.0.0",
37
- "har-examples": "^2.0.1",
38
- "jest": "^27.2.0",
39
- "jest-puppeteer": "^6.0.0",
40
- "multer": "^1.4.2",
41
- "nock": "^13.1.1",
45
+ "form-data-encoder": "^1.7.1",
46
+ "formdata-node": "^4.3.2",
47
+ "har-examples": "^3.1.0",
48
+ "isomorphic-fetch": "^3.0.0",
49
+ "mocha": "^9.1.4",
42
50
  "node-fetch": "^2.6.0",
43
- "prettier": "^2.4.1",
44
- "webpack": "^5.53.0",
45
- "webpack-dev-middleware": "^5.1.0"
51
+ "nyc": "^15.1.0",
52
+ "prettier": "^2.5.1"
46
53
  },
47
54
  "browserslist": [
48
55
  "last 2 versions"
49
56
  ],
50
- "prettier": "@readme/eslint-config/prettier",
51
- "jest": {
52
- "preset": "jest-puppeteer",
53
- "globals": {
54
- "SERVER_URL": "http://localhost:4444"
55
- },
56
- "testMatch": [
57
- "<rootDir>/__tests__/*.test.js"
58
- ]
59
- }
57
+ "prettier": "@readme/eslint-config/prettier"
60
58
  }
@@ -1,6 +0,0 @@
1
- module.exports = {
2
- server: {
3
- command: 'npm run serve',
4
- port: 4444,
5
- },
6
- };