Comparing version 3.0.2 to 4.0.0
@@ -0,1 +1,15 @@ | ||
## 4.0.0 (2020-08-13) | ||
* docs: updating the license ([934d7c0](https://github.com/readmeio/fetch-har/commit/934d7c0)) | ||
* feat: multipart/form-data support in node and browser environments (#77) ([187040c](https://github.com/readmeio/fetch-har/commit/187040c)), closes [#77](https://github.com/readmeio/fetch-har/issues/77) | ||
* ci: ci workflow cleanup ([6adc306](https://github.com/readmeio/fetch-har/commit/6adc306)) | ||
* ci: enabling codeql (#76) ([923556f](https://github.com/readmeio/fetch-har/commit/923556f)), closes [#76](https://github.com/readmeio/fetch-har/issues/76) | ||
* ci: minor fixes to our dependabot workflow ([07f7d03](https://github.com/readmeio/fetch-har/commit/07f7d03)) | ||
* chore(deps-dev): upgrading eslint, prettier, and @readme/eslint-config (#75) ([b456b17](https://github.com/readmeio/fetch-har/commit/b456b17)), closes [#75](https://github.com/readmeio/fetch-har/issues/75) | ||
* chore(deps): bump actions/checkout from v1 to v2.3.2 (#73) ([20c757e](https://github.com/readmeio/fetch-har/commit/20c757e)), closes [#73](https://github.com/readmeio/fetch-har/issues/73) | ||
* build(deps-dev): bump jest from 26.1.0 to 26.2.2 (#71) ([159f19a](https://github.com/readmeio/fetch-har/commit/159f19a)), closes [#71](https://github.com/readmeio/fetch-har/issues/71) | ||
* build(deps-dev): bump nock from 13.0.2 to 13.0.3 (#72) ([c9c8721](https://github.com/readmeio/fetch-har/commit/c9c8721)), closes [#72](https://github.com/readmeio/fetch-har/issues/72) | ||
## <small>3.0.2 (2020-07-16)</small> | ||
@@ -2,0 +16,0 @@ |
@@ -8,2 +8,3 @@ /* eslint-disable import/no-extraneous-dependencies, no-console */ | ||
global.Request = require('node-fetch').Request; | ||
global.FormData = require('form-data'); | ||
@@ -10,0 +11,0 @@ const har = { |
130
index.js
@@ -0,1 +1,2 @@ | ||
/* eslint-disable no-case-declarations */ | ||
function constructRequest(har, userAgent = false) { | ||
@@ -14,33 +15,116 @@ if (!har) throw new Error('Missing HAR file'); | ||
if (request.headers.length) { | ||
options.headers = request.headers.map(header => headers.append(header.name, header.value)); | ||
if ('headers' in request && request.headers.length) { | ||
request.headers.forEach(header => headers.append(header.name, header.value)); | ||
} | ||
if ('cookies' in request && request.cookies.length) { | ||
// As the browser fetch API can't set custom cookies for requests, they instead need to be defined on the document | ||
// and passed into the request via `credentials: include`. Since this is a browser-specific quirk, that should only | ||
// happen in browsers! | ||
if (typeof window !== 'undefined' && typeof document !== 'undefined') { | ||
request.cookies.forEach(cookie => { | ||
document.cookie = `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`; | ||
}); | ||
options.credentials = 'include'; | ||
} else { | ||
headers.append( | ||
'cookie', | ||
request.cookies | ||
.map(cookie => `${encodeURIComponent(cookie.name)}=${encodeURIComponent(cookie.value)}`) | ||
.join('; ') | ||
); | ||
} | ||
} | ||
if ('postData' in request) { | ||
if ('params' in request.postData) { | ||
if ('mimeType' in request.postData && request.postData.mimeType === 'application/x-www-form-urlencoded') { | ||
// Since the content we're handling here is to be encoded as application/x-www-form-urlencoded, this should | ||
// override any other Content-Type headers that are present in the HAR. This is how Postman handles this case | ||
// when building code snippets! | ||
// | ||
// https://github.com/github/fetch/issues/263#issuecomment-209530977 | ||
headers.set('Content-Type', request.postData.mimeType); | ||
if (!('mimeType' in request.postData)) { | ||
request.postData.mimeType = 'application/octet-stream'; | ||
} | ||
const encodedParams = new URLSearchParams(); | ||
request.postData.params.map(param => encodedParams.set(param.name, param.value)); | ||
switch (request.postData.mimeType) { | ||
case 'application/x-www-form-urlencoded': | ||
// Since the content we're handling here is to be encoded as `application/x-www-form-urlencoded`, this should | ||
// override any other Content-Type headers that are present in the HAR. This is how Postman handles this case | ||
// when building code snippets! | ||
// | ||
// https://github.com/github/fetch/issues/263#issuecomment-209530977 | ||
headers.set('Content-Type', request.postData.mimeType); | ||
options.body = encodedParams; | ||
} else { | ||
const formBody = {}; | ||
request.postData.params.map(param => { | ||
try { | ||
formBody[param.name] = JSON.parse(param.value); | ||
} catch (e) { | ||
formBody[param.name] = param.value; | ||
const encodedParams = new URLSearchParams(); | ||
request.postData.params.forEach(param => encodedParams.set(param.name, param.value)); | ||
options.body = encodedParams.toString(); | ||
break; | ||
case 'multipart/form-data': | ||
// If there's a Content-Type header set remove it. We're doing this because when we pass the form data object | ||
// into `fetch` that'll set a proper `Content-Type` header for this request that also includes the boundary | ||
// used on the content. | ||
// | ||
// If we don't do this, then consumers won't be able to parse out the payload because they won't know what | ||
// the boundary to split on it. | ||
if (headers.has('Content-Type')) { | ||
headers.delete('Content-Type'); | ||
} | ||
return true; | ||
}); | ||
// The `form-data` NPM module returns one of two things: a native `FormData` API, or its own polyfill. Since | ||
// the polyfill does not support the full API of the native FormData object, when this you load `form-data` | ||
// within a browser environment you'll have two major differences in API: | ||
// | ||
// * The `.append()` API in `form-data` requires that the third argument is an object containing various, | ||
// undocumented, options. In the browser, `.append()`'s third argument should only be present when the | ||
// second is a `Blob` or `USVString`, and when it is present, it should be a filename string. | ||
// * `form-data` does not expose an `.entries()` API, so the only way to retrieve data out of it for | ||
// construction of boundary-separated payload content is to use its `.pipe()` API. Since the browser | ||
// doesn't have this API, you'll be unable to retrieve data out of it. | ||
// | ||
// Now since the native `FormData` API is iterable, and has the `.entries()` iterator, we can easily detect | ||
// what version of the FormData API we have access to by looking for this and constructing a simple wrapper | ||
// to disconnect some of this logic so you can work against a single, consistent API. | ||
// | ||
// Having to do this isn't fun, but it's the only way you can write code to work with `multipart/form-data` | ||
// content under a server and browser. | ||
const form = new FormData(); | ||
const isNativeFormData = typeof form[Symbol.iterator] === 'function'; | ||
options.body = JSON.stringify(formBody); | ||
request.postData.params.forEach(param => { | ||
if ('fileName' in param && !('value' in param)) { | ||
throw new Error( | ||
"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." | ||
); | ||
} | ||
if (isNativeFormData) { | ||
if ('fileName' in param) { | ||
const paramBlob = new Blob([param.value], { type: param.contentType || null }); | ||
form.append(param.name, paramBlob, param.fileName); | ||
} else { | ||
form.append(param.name, param.value); | ||
} | ||
} else { | ||
form.append(param.name, param.value || '', { | ||
filename: param.fileName || null, | ||
contentType: param.contentType || null, | ||
}); | ||
} | ||
}); | ||
options.body = form; | ||
break; | ||
default: | ||
const formBody = {}; | ||
request.postData.params.map(param => { | ||
try { | ||
formBody[param.name] = JSON.parse(param.value); | ||
} catch (e) { | ||
formBody[param.name] = param.value; | ||
} | ||
return true; | ||
}); | ||
options.body = JSON.stringify(formBody); | ||
} | ||
@@ -52,3 +136,3 @@ } else { | ||
if (request.queryString.length) { | ||
if ('queryString' in request && request.queryString.length) { | ||
const query = request.queryString.map(q => `${q.name}=${q.value}`).join('&'); | ||
@@ -55,0 +139,0 @@ querystring = `?${query}`; |
{ | ||
"name": "fetch-har", | ||
"version": "3.0.2", | ||
"version": "4.0.0", | ||
"description": "Make a fetch request from a HAR file", | ||
@@ -15,3 +15,4 @@ "main": "index.js", | ||
"release": "conventional-changelog -i CHANGELOG.md -s", | ||
"test": "jest --coverage" | ||
"serve": "node __tests__/server.js", | ||
"test": "jest --runInBand --coverage" | ||
}, | ||
@@ -25,3 +26,2 @@ "publishConfig": { | ||
}, | ||
"author": "Sanjeet <sanjeet.uppal92@gmail.com>", | ||
"license": "ISC", | ||
@@ -33,12 +33,31 @@ "bugs": { | ||
"devDependencies": { | ||
"@readme/eslint-config": "^2.0.0", | ||
"@readme/eslint-config": "^3.4.1", | ||
"body-parser": "^1.19.0", | ||
"conventional-changelog-cli": "^2.0.31", | ||
"eslint": "^6.6.0", | ||
"cookie-parser": "^1.4.5", | ||
"eslint": "^7.6.0", | ||
"express": "^4.17.1", | ||
"form-data": "^3.0.0", | ||
"har-examples": "^1.0.5", | ||
"jest": "^26.0.1", | ||
"jest-puppeteer": "^4.4.0", | ||
"jsinspect": "^0.12.7", | ||
"multer": "^1.4.2", | ||
"nock": "^13.0.0", | ||
"node-fetch": "^2.6.0", | ||
"prettier": "^2.0.1" | ||
"prettier": "^2.0.5", | ||
"puppeteer": "^5.2.1", | ||
"webpack": "^4.44.1", | ||
"webpack-dev-middleware": "^3.7.2" | ||
}, | ||
"prettier": "@readme/eslint-config/prettier" | ||
"prettier": "@readme/eslint-config/prettier", | ||
"jest": { | ||
"preset": "jest-puppeteer", | ||
"globals": { | ||
"SERVER_URL": "http://localhost:4444" | ||
}, | ||
"testMatch": [ | ||
"<rootDir>/__tests__/*.test.js" | ||
] | ||
} | ||
} |
# fetch-har | ||
[![Build](https://github.com/readmeio/fetch-har/workflows/Node%20CI/badge.svg)](https://github.com/readmeio/fetch-har) | ||
[![CI](https://github.com/readmeio/fetch-har/workflows/CI/badge.svg)](https://github.com/readmeio/fetch-har) | ||
Make a [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) request from a HAR file | ||
Make a [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) request from a HAR file. | ||
@@ -18,6 +18,7 @@ [![](https://d3vv6lp55qjaqc.cloudfront.net/items/1M3C3j0I0s0j3T362344/Untitled-2.png)](https://readme.io) | ||
// If executing from an environment without `fetch`, you'll need to polyfill | ||
// If executing from an environment without `fetch`, you'll need to polyfill. | ||
global.fetch = require('node-fetch'); | ||
global.Headers = require('node-fetch').Headers; | ||
global.Request = require('node-fetch').Request; | ||
global.FormData = require('form-data'); | ||
@@ -69,12 +70,4 @@ const har = { | ||
We also export a 2nd function which is used to construct a [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object from your HAR. | ||
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. | ||
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. | ||
## Credits | ||
[Sanjeet](https://github.com/uppal101/) | ||
## License | ||
ISC | ||
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. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25474
7
177
18
1
72