fetch-har 8.1.4 → 9.0.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.
@@ -0,0 +1,5 @@
1
+ {
2
+ "editor.codeActionsOnSave": {
3
+ "source.fixAll": true
4
+ }
5
+ }
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## <small>8.1.5 (2023-01-23)</small>
2
+
3
+ * fix: support urls with hashes that also have query params (#363) ([7a51342](https://github.com/readmeio/fetch-har/commit/7a51342)), closes [#363](https://github.com/readmeio/fetch-har/issues/363)
4
+ * chore(deps-dev): bumping deps (#362) ([3279246](https://github.com/readmeio/fetch-har/commit/3279246)), closes [#362](https://github.com/readmeio/fetch-har/issues/362)
5
+
6
+
7
+
1
8
  ## <small>8.1.4 (2023-01-03)</small>
2
9
 
3
10
  * chore(deps-dev): bump @readme/eslint-config from 10.1.0 to 10.1.1 (#331) ([75de615](https://github.com/readmeio/fetch-har/commit/75de615)), closes [#331](https://github.com/readmeio/fetch-har/issues/331)
package/dist/index.d.ts CHANGED
@@ -1,9 +1,22 @@
1
1
  /// <reference types="node" />
2
2
  import type { Har } from 'har-format';
3
+ interface RequestInitWithDuplex extends RequestInit {
4
+ /**
5
+ * `RequestInit#duplex` does not yet exist in the TS `lib.dom.d.ts` definition yet the native
6
+ * fetch implementation in Node 18+, `undici`, requires it for certain POST payloads.
7
+ *
8
+ * @see {@link https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/1483}
9
+ * @see {@link https://github.com/nodejs/node/issues/46221}
10
+ * @see {@link https://fetch.spec.whatwg.org/#request-class}
11
+ * @see {@link https://github.com/microsoft/TypeScript/blob/main/lib/lib.dom.d.ts}
12
+ */
13
+ duplex?: 'half';
14
+ }
3
15
  export interface FetchHAROptions {
4
16
  userAgent?: string;
5
17
  files?: Record<string, Blob | Buffer>;
6
18
  multipartEncoder?: any;
7
- init?: RequestInit;
19
+ init?: RequestInitWithDuplex;
8
20
  }
9
21
  export default function fetchHAR(har: Har, opts?: FetchHAROptions): Promise<Response>;
22
+ export {};
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ var __assign = (this && this.__assign) || function () {
10
10
  };
11
11
  return __assign.apply(this, arguments);
12
12
  };
13
- exports.__esModule = true;
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
14
  var data_urls_1 = require("@readme/data-urls");
15
15
  var readable_stream_1 = require("readable-stream");
16
16
  if (!globalThis.Blob) {
@@ -101,6 +101,7 @@ function fetchHAR(har, opts) {
101
101
  var request = har.log.entries[0].request;
102
102
  var url = request.url;
103
103
  var querystring = '';
104
+ var shouldSetDuplex = false;
104
105
  var options = __assign(__assign({}, (opts.init ? opts.init : {})), { method: request.method });
105
106
  if (!options.headers) {
106
107
  options.headers = new Headers();
@@ -178,7 +179,7 @@ function fetchHAR(har, opts) {
178
179
  * they won't know what the boundary to split on it.
179
180
  */
180
181
  if (headers.has('Content-Type')) {
181
- headers["delete"]('Content-Type');
182
+ headers.delete('Content-Type');
182
183
  }
183
184
  var form_1 = new FormData();
184
185
  if (!isFormData(form_1)) {
@@ -215,7 +216,7 @@ function fetchHAR(har, opts) {
215
216
  // that the FormData API can use it.
216
217
  if (isBuffer(fileContents)) {
217
218
  form_1.append(param.name, new File([fileContents], param.fileName, {
218
- type: param.contentType || null
219
+ type: param.contentType || null,
219
220
  }), param.fileName);
220
221
  return;
221
222
  }
@@ -260,6 +261,7 @@ function fetchHAR(har, opts) {
260
261
  });
261
262
  // @ts-expect-error "Property 'from' does not exist on type 'typeof Readable'." but it does!
262
263
  options.body = readable_stream_1.Readable.from(encoder_1);
264
+ shouldSetDuplex = true;
263
265
  }
264
266
  else {
265
267
  options.body = form_1;
@@ -300,6 +302,7 @@ function fetchHAR(har, opts) {
300
302
  else {
301
303
  // @ts-expect-error "Property 'from' does not exist on type 'typeof Readable'." but it does!
302
304
  options.body = readable_stream_1.Readable.from(fileContents.stream());
305
+ shouldSetDuplex = true;
303
306
  // Supplying a polyfilled `File` stream into `Request.body` doesn't automatically
304
307
  // add `Content-Length`.
305
308
  if (!headers.has('content-length')) {
@@ -315,11 +318,27 @@ function fetchHAR(har, opts) {
315
318
  options.body = request.postData.text;
316
319
  }
317
320
  }
321
+ /**
322
+ * The fetch spec, which Node 18+ strictly abides by, now requires that `duplex` be sent with
323
+ * requests that have payloads.
324
+ *
325
+ * As `RequestInit#duplex` isn't supported by any browsers, or even mentioned on MDN, we aren't
326
+ * sending it in browser environments. This work is purely to support Node 18+ and `undici`
327
+ * environments.
328
+ *
329
+ * @see {@link https://github.com/nodejs/node/issues/46221}
330
+ * @see {@link https://github.com/whatwg/fetch/pull/1457}
331
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request}
332
+ */
333
+ if (shouldSetDuplex && !isBrowser()) {
334
+ options.duplex = 'half';
335
+ }
318
336
  }
319
337
  // We automaticaly assume that the HAR that we have already has query parameters encoded within
320
338
  // it so we do **not** use the `URLSearchParams` API here for composing the query string.
339
+ var requestURL = url;
321
340
  if ('queryString' in request && request.queryString.length) {
322
- var urlObj = new URL(url);
341
+ var urlObj = new URL(requestURL);
323
342
  var queryParams_1 = Array.from(urlObj.searchParams).map(function (_a) {
324
343
  var k = _a[0], v = _a[1];
325
344
  return "".concat(k, "=").concat(v);
@@ -328,11 +347,21 @@ function fetchHAR(har, opts) {
328
347
  queryParams_1.push("".concat(q.name, "=").concat(q.value));
329
348
  });
330
349
  querystring = queryParams_1.join('&');
350
+ // Because anchor hashes before query strings will prevent query strings from being delivered
351
+ // we need to pop them off and re-add them after.
352
+ if (urlObj.hash) {
353
+ var urlWithoutHashes = requestURL.replace(urlObj.hash, '');
354
+ requestURL = "".concat(urlWithoutHashes.split('?')[0]).concat(querystring ? "?".concat(querystring) : '');
355
+ requestURL += urlObj.hash;
356
+ }
357
+ else {
358
+ requestURL = "".concat(requestURL.split('?')[0]).concat(querystring ? "?".concat(querystring) : '');
359
+ }
331
360
  }
332
361
  if (opts.userAgent) {
333
362
  headers.append('User-Agent', opts.userAgent);
334
363
  }
335
364
  options.headers = headers;
336
- return fetch("".concat(url.split('?')[0]).concat(querystring ? "?".concat(querystring) : ''), options);
365
+ return fetch(requestURL, options);
337
366
  }
338
- exports["default"] = fetchHAR;
367
+ exports.default = fetchHAR;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "fetch-har",
3
- "version": "8.1.4",
3
+ "version": "9.0.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",
7
7
  "engines": {
8
- "node": ">=14"
8
+ "node": ">=16"
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsc",
@@ -40,16 +40,16 @@
40
40
  "devDependencies": {
41
41
  "@jsdevtools/host-environment": "^2.1.2",
42
42
  "@jsdevtools/karma-config": "^3.1.7",
43
- "@readme/eslint-config": "^10.3.2",
43
+ "@readme/eslint-config": "^10.5.3",
44
44
  "@types/chai": "^4.3.1",
45
- "@types/express": "^4.17.15",
45
+ "@types/express": "^4.17.17",
46
46
  "@types/mocha": "^10.0.0",
47
47
  "@types/multer": "^1.4.7",
48
- "@types/node": "^18.11.18",
48
+ "@types/node": "^20.2.5",
49
49
  "@types/readable-stream": "^2.3.15",
50
50
  "chai": "^4.3.4",
51
51
  "datauri": "^4.1.0",
52
- "eslint": "^8.31.0",
52
+ "eslint": "^8.41.0",
53
53
  "express": "^4.18.1",
54
54
  "fetch-mock": "^9.11.0",
55
55
  "form-data": "^4.0.0",
@@ -59,15 +59,15 @@
59
59
  "isomorphic-fetch": "^3.0.0",
60
60
  "mocha": "^10.2.0",
61
61
  "multer": "^1.4.5-lts.1",
62
- "nock": "^13.2.4",
62
+ "nock": "^13.3.0",
63
63
  "node-fetch": "^2.6.0",
64
64
  "nyc": "^15.1.0",
65
- "prettier": "^2.8.1",
65
+ "prettier": "^2.8.7",
66
66
  "temp-dir": "^2.0.0",
67
67
  "ts-loader": "^8.4.0",
68
68
  "ts-node": "^10.7.0",
69
- "typescript": "^4.9.4",
70
- "undici": "^5.14.0",
69
+ "typescript": "^5.0.4",
70
+ "undici": "^5.22.1",
71
71
  "webpack": "^4.46.0"
72
72
  },
73
73
  "browserslist": [
package/src/index.ts CHANGED
@@ -37,11 +37,24 @@ if (!globalThis.FormData) {
37
37
  }
38
38
  }
39
39
 
40
+ interface RequestInitWithDuplex extends RequestInit {
41
+ /**
42
+ * `RequestInit#duplex` does not yet exist in the TS `lib.dom.d.ts` definition yet the native
43
+ * fetch implementation in Node 18+, `undici`, requires it for certain POST payloads.
44
+ *
45
+ * @see {@link https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/1483}
46
+ * @see {@link https://github.com/nodejs/node/issues/46221}
47
+ * @see {@link https://fetch.spec.whatwg.org/#request-class}
48
+ * @see {@link https://github.com/microsoft/TypeScript/blob/main/lib/lib.dom.d.ts}
49
+ */
50
+ duplex?: 'half';
51
+ }
52
+
40
53
  export interface FetchHAROptions {
41
54
  userAgent?: string;
42
55
  files?: Record<string, Blob | Buffer>;
43
56
  multipartEncoder?: any; // form-data-encoder
44
- init?: RequestInit;
57
+ init?: RequestInitWithDuplex;
45
58
  }
46
59
 
47
60
  type DataURL = npmDataURL & {
@@ -117,8 +130,9 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
117
130
  const { request } = har.log.entries[0];
118
131
  const { url } = request;
119
132
  let querystring = '';
133
+ let shouldSetDuplex = false;
120
134
 
121
- const options: RequestInit = {
135
+ const options: RequestInitWithDuplex = {
122
136
  // If we have custom options for the `Request` API we need to add them in here now before we
123
137
  // fill it in with everything we need from the HAR.
124
138
  ...(opts.init ? opts.init : {}),
@@ -309,6 +323,7 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
309
323
 
310
324
  // @ts-expect-error "Property 'from' does not exist on type 'typeof Readable'." but it does!
311
325
  options.body = Readable.from(encoder);
326
+ shouldSetDuplex = true;
312
327
  } else {
313
328
  options.body = form;
314
329
  }
@@ -347,6 +362,7 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
347
362
  } else {
348
363
  // @ts-expect-error "Property 'from' does not exist on type 'typeof Readable'." but it does!
349
364
  options.body = Readable.from((fileContents as File).stream());
365
+ shouldSetDuplex = true;
350
366
 
351
367
  // Supplying a polyfilled `File` stream into `Request.body` doesn't automatically
352
368
  // add `Content-Length`.
@@ -364,12 +380,29 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
364
380
  options.body = request.postData.text;
365
381
  }
366
382
  }
383
+
384
+ /**
385
+ * The fetch spec, which Node 18+ strictly abides by, now requires that `duplex` be sent with
386
+ * requests that have payloads.
387
+ *
388
+ * As `RequestInit#duplex` isn't supported by any browsers, or even mentioned on MDN, we aren't
389
+ * sending it in browser environments. This work is purely to support Node 18+ and `undici`
390
+ * environments.
391
+ *
392
+ * @see {@link https://github.com/nodejs/node/issues/46221}
393
+ * @see {@link https://github.com/whatwg/fetch/pull/1457}
394
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request}
395
+ */
396
+ if (shouldSetDuplex && !isBrowser()) {
397
+ options.duplex = 'half';
398
+ }
367
399
  }
368
400
 
369
401
  // We automaticaly assume that the HAR that we have already has query parameters encoded within
370
402
  // it so we do **not** use the `URLSearchParams` API here for composing the query string.
403
+ let requestURL = url;
371
404
  if ('queryString' in request && request.queryString.length) {
372
- const urlObj = new URL(url);
405
+ const urlObj = new URL(requestURL);
373
406
 
374
407
  const queryParams = Array.from(urlObj.searchParams).map(([k, v]) => `${k}=${v}`);
375
408
  request.queryString.forEach(q => {
@@ -377,6 +410,16 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
377
410
  });
378
411
 
379
412
  querystring = queryParams.join('&');
413
+
414
+ // Because anchor hashes before query strings will prevent query strings from being delivered
415
+ // we need to pop them off and re-add them after.
416
+ if (urlObj.hash) {
417
+ const urlWithoutHashes = requestURL.replace(urlObj.hash, '');
418
+ requestURL = `${urlWithoutHashes.split('?')[0]}${querystring ? `?${querystring}` : ''}`;
419
+ requestURL += urlObj.hash;
420
+ } else {
421
+ requestURL = `${requestURL.split('?')[0]}${querystring ? `?${querystring}` : ''}`;
422
+ }
380
423
  }
381
424
 
382
425
  if (opts.userAgent) {
@@ -385,5 +428,5 @@ export default function fetchHAR(har: Har, opts: FetchHAROptions = {}) {
385
428
 
386
429
  options.headers = headers;
387
430
 
388
- return fetch(`${url.split('?')[0]}${querystring ? `?${querystring}` : ''}`, options);
431
+ return fetch(requestURL, options);
389
432
  }