Comparing version
@@ -0,2 +1,4 @@ | ||
'use strict'; | ||
/* eslint-env browser */ | ||
module.exports = typeof self == 'object' ? self.FormData : window.FormData; | ||
module.exports = typeof self === 'object' ? self.FormData : window.FormData; |
@@ -0,1 +1,3 @@ | ||
'use strict'; | ||
var CombinedStream = require('combined-stream'); | ||
@@ -8,14 +10,10 @@ var util = require('util'); | ||
var fs = require('fs'); | ||
var crypto = require('crypto'); | ||
var mime = require('mime-types'); | ||
var asynckit = require('asynckit'); | ||
var hasOwn = require('hasown'); | ||
var setToStringTag = require('es-set-tostringtag'); | ||
var populate = require('./populate.js'); | ||
var Buffer = require('safe-buffer').Buffer; // eslint-disable-line no-shadow | ||
var Buffer = require('safe-buffer').Buffer; | ||
// Public API | ||
module.exports = FormData; | ||
// make it a Stream | ||
util.inherits(FormData, CombinedStream); | ||
/** | ||
@@ -41,3 +39,3 @@ * Create readable "multipart/form-data" streams. | ||
options = options || {}; | ||
for (var option in options) { | ||
for (var option in options) { // eslint-disable-line no-restricted-syntax | ||
this[option] = options[option]; | ||
@@ -47,6 +45,9 @@ } | ||
// make it a Stream | ||
util.inherits(FormData, CombinedStream); | ||
FormData.LINE_BREAK = '\r\n'; | ||
FormData.DEFAULT_CONTENT_TYPE = 'application/octet-stream'; | ||
FormData.prototype.append = function(field, value, options) { | ||
FormData.prototype.append = function (field, value, options) { | ||
@@ -56,4 +57,4 @@ options = options || {}; | ||
// allow filename as single option | ||
if (typeof options == 'string') { | ||
options = {filename: options}; | ||
if (typeof options === 'string') { | ||
options = { filename: options }; | ||
} | ||
@@ -64,4 +65,4 @@ | ||
// all that streamy business can't handle numbers | ||
if (typeof value == 'number') { | ||
value = '' + value; | ||
if (typeof value === 'number' || value == null) { | ||
value = String(value); | ||
} | ||
@@ -71,4 +72,6 @@ | ||
if (Array.isArray(value)) { | ||
// Please convert your array into string | ||
// the way web server expects it | ||
/* | ||
* Please convert your array into string | ||
* the way web server expects it | ||
*/ | ||
this._error(new Error('Arrays are not supported.')); | ||
@@ -89,11 +92,13 @@ return; | ||
FormData.prototype._trackLength = function(header, value, options) { | ||
FormData.prototype._trackLength = function (header, value, options) { | ||
var valueLength = 0; | ||
// used w/ getLengthSync(), when length is known. | ||
// e.g. for streaming directly from a remote server, | ||
// w/ a known file a size, and not wanting to wait for | ||
// incoming file to finish to get its size. | ||
/* | ||
* used w/ getLengthSync(), when length is known. | ||
* e.g. for streaming directly from a remote server, | ||
* w/ a known file a size, and not wanting to wait for | ||
* incoming file to finish to get its size. | ||
*/ | ||
if (options.knownLength != null) { | ||
valueLength += +options.knownLength; | ||
valueLength += Number(options.knownLength); | ||
} else if (Buffer.isBuffer(value)) { | ||
@@ -108,8 +113,6 @@ valueLength = value.length; | ||
// @check why add CRLF? does this account for custom/multiple CRLFs? | ||
this._overheadLength += | ||
Buffer.byteLength(header) + | ||
FormData.LINE_BREAK.length; | ||
this._overheadLength += Buffer.byteLength(header) + FormData.LINE_BREAK.length; | ||
// empty or either doesn't have path or not an http response | ||
if (!value || ( !value.path && !(value.readable && Object.prototype.hasOwnProperty.call(value, 'httpVersion')) )) { | ||
if (!value || (!value.path && !(value.readable && hasOwn(value, 'httpVersion')))) { | ||
return; | ||
@@ -124,17 +127,21 @@ } | ||
FormData.prototype._lengthRetriever = function(value, callback) { | ||
if (Object.prototype.hasOwnProperty.call(value, 'fd')) { | ||
FormData.prototype._lengthRetriever = function (value, callback) { | ||
if (hasOwn(value, 'fd')) { | ||
// take read range into a account | ||
// `end` = Infinity –> read file till the end | ||
// | ||
// TODO: Looks like there is bug in Node fs.createReadStream | ||
// it doesn't respect `end` options without `start` options | ||
// Fix it when node fixes it. | ||
// https://github.com/joyent/node/issues/7819 | ||
if (value.end != undefined && value.end != Infinity && value.start != undefined) { | ||
/* | ||
* take read range into a account | ||
* `end` = Infinity –> read file till the end | ||
* | ||
* TODO: Looks like there is bug in Node fs.createReadStream | ||
* it doesn't respect `end` options without `start` options | ||
* Fix it when node fixes it. | ||
* https://github.com/joyent/node/issues/7819 | ||
*/ | ||
if (value.end != null && value.end !== Infinity && value.start != null) { | ||
// when end specified | ||
// no need to calculate range | ||
// inclusive, starts with 0 | ||
/* | ||
* when end specified | ||
* no need to calculate range | ||
* inclusive, starts with 0 | ||
*/ | ||
callback(null, value.end + 1 - (value.start ? value.start : 0)); | ||
@@ -145,3 +152,3 @@ | ||
// still need to fetch file size from fs | ||
fs.stat(value.path, function(err, stat) { | ||
fs.stat(value.path, function (err, stat) { | ||
@@ -162,11 +169,11 @@ var fileSize; | ||
// or http response | ||
} else if (Object.prototype.hasOwnProperty.call(value, 'httpVersion')) { | ||
callback(null, +value.headers['content-length']); | ||
} else if (hasOwn(value, 'httpVersion')) { | ||
callback(null, Number(value.headers['content-length'])); | ||
// or request stream http://github.com/mikeal/request | ||
} else if (Object.prototype.hasOwnProperty.call(value, 'httpModule')) { | ||
} else if (hasOwn(value, 'httpModule')) { | ||
// wait till response come back | ||
value.on('response', function(response) { | ||
value.on('response', function (response) { | ||
value.pause(); | ||
callback(null, +response.headers['content-length']); | ||
callback(null, Number(response.headers['content-length'])); | ||
}); | ||
@@ -181,7 +188,9 @@ value.resume(); | ||
FormData.prototype._multiPartHeader = function(field, value, options) { | ||
// custom header specified (as string)? | ||
// it becomes responsible for boundary | ||
// (e.g. to handle extra CRLFs on .NET servers) | ||
if (typeof options.header == 'string') { | ||
FormData.prototype._multiPartHeader = function (field, value, options) { | ||
/* | ||
* custom header specified (as string)? | ||
* it becomes responsible for boundary | ||
* (e.g. to handle extra CRLFs on .NET servers) | ||
*/ | ||
if (typeof options.header === 'string') { | ||
return options.header; | ||
@@ -194,11 +203,11 @@ } | ||
var contents = ''; | ||
var headers = { | ||
var headers = { | ||
// add custom disposition as third element or keep it two elements if not | ||
'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), | ||
// if no content type. allow it to be empty array | ||
'Content-Type': [].concat(contentType || []) | ||
'Content-Type': [].concat(contentType || []), | ||
}; | ||
// allow custom headers. | ||
if (typeof options.header == 'object') { | ||
if (typeof options.header === 'object') { | ||
populate(headers, options.header); | ||
@@ -208,4 +217,4 @@ } | ||
var header; | ||
for (var prop in headers) { | ||
if (Object.prototype.hasOwnProperty.call(headers, prop)) { | ||
for (var prop in headers) { // eslint-disable-line no-restricted-syntax | ||
if (hasOwn(headers, prop)) { | ||
header = headers[prop]; | ||
@@ -215,3 +224,3 @@ | ||
if (header == null) { | ||
continue; | ||
continue; // eslint-disable-line no-continue, no-restricted-syntax | ||
} | ||
@@ -234,17 +243,17 @@ | ||
FormData.prototype._getContentDisposition = function(value, options) { | ||
FormData.prototype._getContentDisposition = function (value, options) { | ||
var filename | ||
, contentDisposition | ||
; | ||
var filename, | ||
contentDisposition; | ||
if (typeof options.filepath === 'string') { | ||
// custom filepath for relative paths | ||
filename = path.normalize(options.filepath).replace(/\\/g, '/'); | ||
} else if (options.filename || value.name || value.path) { | ||
// custom filename take precedence | ||
// formidable and the browser add a name property | ||
// fs- and request- streams have path property | ||
filename = path.basename(options.filename || value.name || value.path); | ||
} else if (value.readable && Object.prototype.hasOwnProperty.call(value, 'httpVersion')) { | ||
} else if (options.filename || (value && (value.name || value.path))) { | ||
/* | ||
* custom filename take precedence | ||
* formidable and the browser add a name property | ||
* fs- and request- streams have path property | ||
*/ | ||
filename = path.basename(options.filename || (value && (value.name || value.path))); | ||
} else if (value && value.readable && hasOwn(value, 'httpVersion')) { | ||
// or try http response | ||
@@ -261,3 +270,3 @@ filename = path.basename(value.client._httpMessage.path || ''); | ||
FormData.prototype._getContentType = function(value, options) { | ||
FormData.prototype._getContentType = function (value, options) { | ||
@@ -268,3 +277,3 @@ // use custom content-type above all | ||
// or try `name` from formidable, browser | ||
if (!contentType && value.name) { | ||
if (!contentType && value && value.name) { | ||
contentType = mime.lookup(value.name); | ||
@@ -274,3 +283,3 @@ } | ||
// or try `path` from fs-, request- streams | ||
if (!contentType && value.path) { | ||
if (!contentType && value && value.path) { | ||
contentType = mime.lookup(value.path); | ||
@@ -280,3 +289,3 @@ } | ||
// or if it's http-reponse | ||
if (!contentType && value.readable && Object.prototype.hasOwnProperty.call(value, 'httpVersion')) { | ||
if (!contentType && value && value.readable && hasOwn(value, 'httpVersion')) { | ||
contentType = value.headers['content-type']; | ||
@@ -291,3 +300,3 @@ } | ||
// fallback to the default content type if `value` is not simple value | ||
if (!contentType && typeof value == 'object') { | ||
if (!contentType && value && typeof value === 'object') { | ||
contentType = FormData.DEFAULT_CONTENT_TYPE; | ||
@@ -299,7 +308,7 @@ } | ||
FormData.prototype._multiPartFooter = function() { | ||
return function(next) { | ||
FormData.prototype._multiPartFooter = function () { | ||
return function (next) { | ||
var footer = FormData.LINE_BREAK; | ||
var lastPart = (this._streams.length === 0); | ||
var lastPart = this._streams.length === 0; | ||
if (lastPart) { | ||
@@ -313,14 +322,14 @@ footer += this._lastBoundary(); | ||
FormData.prototype._lastBoundary = function() { | ||
FormData.prototype._lastBoundary = function () { | ||
return '--' + this.getBoundary() + '--' + FormData.LINE_BREAK; | ||
}; | ||
FormData.prototype.getHeaders = function(userHeaders) { | ||
FormData.prototype.getHeaders = function (userHeaders) { | ||
var header; | ||
var formHeaders = { | ||
'content-type': 'multipart/form-data; boundary=' + this.getBoundary() | ||
'content-type': 'multipart/form-data; boundary=' + this.getBoundary(), | ||
}; | ||
for (header in userHeaders) { | ||
if (Object.prototype.hasOwnProperty.call(userHeaders, header)) { | ||
for (header in userHeaders) { // eslint-disable-line no-restricted-syntax | ||
if (hasOwn(userHeaders, header)) { | ||
formHeaders[header.toLowerCase()] = userHeaders[header]; | ||
@@ -333,3 +342,10 @@ } | ||
FormData.prototype.getBoundary = function() { | ||
FormData.prototype.setBoundary = function (boundary) { | ||
if (typeof boundary !== 'string') { | ||
throw new TypeError('FormData boundary must be a string'); | ||
} | ||
this._boundary = boundary; | ||
}; | ||
FormData.prototype.getBoundary = function () { | ||
if (!this._boundary) { | ||
@@ -342,4 +358,4 @@ this._generateBoundary(); | ||
FormData.prototype.getBuffer = function() { | ||
var dataBuffer = new Buffer.alloc(0); | ||
FormData.prototype.getBuffer = function () { | ||
var dataBuffer = Buffer.alloc(0); | ||
var boundary = this.getBoundary(); | ||
@@ -352,11 +368,11 @@ | ||
// Add content to the buffer. | ||
if(Buffer.isBuffer(this._streams[i])) { | ||
dataBuffer = Buffer.concat( [dataBuffer, this._streams[i]]); | ||
}else { | ||
dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(this._streams[i])]); | ||
if (Buffer.isBuffer(this._streams[i])) { | ||
dataBuffer = Buffer.concat([dataBuffer, this._streams[i]]); | ||
} else { | ||
dataBuffer = Buffer.concat([dataBuffer, Buffer.from(this._streams[i])]); | ||
} | ||
// Add break after content. | ||
if (typeof this._streams[i] !== 'string' || this._streams[i].substring( 2, boundary.length + 2 ) !== boundary) { | ||
dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(FormData.LINE_BREAK)] ); | ||
if (typeof this._streams[i] !== 'string' || this._streams[i].substring(2, boundary.length + 2) !== boundary) { | ||
dataBuffer = Buffer.concat([dataBuffer, Buffer.from(FormData.LINE_BREAK)]); | ||
} | ||
@@ -367,24 +383,24 @@ } | ||
// Add the footer and return the Buffer object. | ||
return Buffer.concat( [dataBuffer, Buffer.from(this._lastBoundary())] ); | ||
return Buffer.concat([dataBuffer, Buffer.from(this._lastBoundary())]); | ||
}; | ||
FormData.prototype._generateBoundary = function() { | ||
FormData.prototype._generateBoundary = function () { | ||
// This generates a 50 character boundary similar to those used by Firefox. | ||
// They are optimized for boyer-moore parsing. | ||
var boundary = '--------------------------'; | ||
for (var i = 0; i < 24; i++) { | ||
boundary += Math.floor(Math.random() * 10).toString(16); | ||
} | ||
this._boundary = boundary; | ||
this._boundary = '--------------------------' + crypto.randomBytes(12).toString('hex'); | ||
}; | ||
// Note: getLengthSync DOESN'T calculate streams length | ||
// As workaround one can calculate file size manually | ||
// and add it as knownLength option | ||
FormData.prototype.getLengthSync = function() { | ||
/* | ||
* Note: getLengthSync DOESN'T calculate streams length | ||
* As workaround one can calculate file size manually | ||
* and add it as knownLength option | ||
*/ | ||
FormData.prototype.getLengthSync = function () { | ||
var knownLength = this._overheadLength + this._valueLength; | ||
// Don't get confused, there are 3 "internal" streams for each keyval pair | ||
// so it basically checks if there is any value added to the form | ||
/* | ||
* Don't get confused, there are 3 "internal" streams for each keyval pair | ||
* so it basically checks if there is any value added to the form | ||
*/ | ||
if (this._streams.length) { | ||
@@ -396,5 +412,7 @@ knownLength += this._lastBoundary().length; | ||
if (!this.hasKnownLength()) { | ||
// Some async length retrievers are present | ||
// therefore synchronous length calculation is false. | ||
// Please use getLength(callback) to get proper length | ||
/* | ||
* Some async length retrievers are present | ||
* therefore synchronous length calculation is false. | ||
* Please use getLength(callback) to get proper length | ||
*/ | ||
this._error(new Error('Cannot calculate proper length in synchronous way.')); | ||
@@ -406,6 +424,8 @@ } | ||
// Public API to check if length of added values is known | ||
// https://github.com/form-data/form-data/issues/196 | ||
// https://github.com/form-data/form-data/issues/262 | ||
FormData.prototype.hasKnownLength = function() { | ||
/* | ||
* Public API to check if length of added values is known | ||
* https://github.com/form-data/form-data/issues/196 | ||
* https://github.com/form-data/form-data/issues/262 | ||
*/ | ||
FormData.prototype.hasKnownLength = function () { | ||
var hasKnownLength = true; | ||
@@ -420,3 +440,3 @@ | ||
FormData.prototype.getLength = function(cb) { | ||
FormData.prototype.getLength = function (cb) { | ||
var knownLength = this._overheadLength + this._valueLength; | ||
@@ -433,3 +453,3 @@ | ||
asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function(err, values) { | ||
asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function (err, values) { | ||
if (err) { | ||
@@ -440,3 +460,3 @@ cb(err); | ||
values.forEach(function(length) { | ||
values.forEach(function (length) { | ||
knownLength += length; | ||
@@ -449,11 +469,12 @@ }); | ||
FormData.prototype.submit = function(params, cb) { | ||
var request | ||
, options | ||
, defaults = {method: 'post'} | ||
; | ||
FormData.prototype.submit = function (params, cb) { | ||
var request; | ||
var options; | ||
var defaults = { method: 'post' }; | ||
// parse provided url if it's string | ||
// or treat it as options object | ||
if (typeof params == 'string') { | ||
/* | ||
* parse provided url if it's string | ||
* or treat it as options object | ||
*/ | ||
if (typeof params === 'string') { | ||
@@ -465,3 +486,3 @@ params = parseUrl(params); | ||
host: params.hostname, | ||
protocol: params.protocol | ||
protocol: params.protocol, | ||
}, defaults); | ||
@@ -475,3 +496,3 @@ | ||
if (!options.port) { | ||
options.port = options.protocol == 'https:' ? 443 : 80; | ||
options.port = options.protocol === 'https:' ? 443 : 80; | ||
} | ||
@@ -484,3 +505,3 @@ } | ||
// https if specified, fallback to http in any other case | ||
if (options.protocol == 'https:') { | ||
if (options.protocol === 'https:') { | ||
request = https.request(options); | ||
@@ -492,3 +513,3 @@ } else { | ||
// get content length and fire away | ||
this.getLength(function(err, length) { | ||
this.getLength(function (err, length) { | ||
if (err) { | ||
@@ -512,3 +533,3 @@ this._error(err); | ||
FormData.prototype._error = function(err) { | ||
FormData.prototype._error = function (err) { | ||
if (!this.error) { | ||
@@ -525,1 +546,3 @@ this.error = err; | ||
setToStringTag(FormData, 'FormData'); | ||
module.exports = FormData; |
@@ -0,6 +1,6 @@ | ||
'use strict'; | ||
// populates missing values | ||
module.exports = function(dst, src) { | ||
Object.keys(src).forEach(function(prop) | ||
{ | ||
module.exports = function (dst, src) { | ||
Object.keys(src).forEach(function (prop) { | ||
dst[prop] = dst[prop] || src[prop]; | ||
@@ -7,0 +7,0 @@ }); |
@@ -5,3 +5,3 @@ { | ||
"description": "A library to create readable \"multipart/form-data\" streams. Can be used to submit forms and file uploads to other web applications.", | ||
"version": "2.5.3", | ||
"version": "2.5.4", | ||
"repository": { | ||
@@ -23,3 +23,2 @@ "type": "git", | ||
"report": "istanbul report lcov text", | ||
"ci-lint": "is-node-modern 8 && npm run lint || is-node-not-modern 8", | ||
"ci-test": "npm run tests-only && npm run browser && npm run report", | ||
@@ -34,10 +33,8 @@ "predebug": "rimraf coverage test/tmp", | ||
"restore-readme": "mv README.md.bak README.md", | ||
"prepublish": "in-publish && npm run update-readme || not-in-publish", | ||
"postpublish": "npm run restore-readme" | ||
"prepublish": "not-in-publish || npm run prepublishOnly", | ||
"prepublishOnly": "npm run update-readme", | ||
"postpublish": "npm run restore-readme", | ||
"version": "auto-changelog && git add CHANGELOG.md", | ||
"postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\"" | ||
}, | ||
"pre-commit": [ | ||
"lint", | ||
"ci-test", | ||
"check" | ||
], | ||
"engines": { | ||
@@ -50,2 +47,3 @@ "node": ">= 0.12" | ||
"es-set-tostringtag": "^2.1.0", | ||
"has-own": "^1.0.1", | ||
"mime-types": "^2.1.35", | ||
@@ -55,5 +53,4 @@ "safe-buffer": "^5.2.1" | ||
"devDependencies": { | ||
"@types/combined-stream": "^1.0.6", | ||
"@types/mime-types": "^2.1.4", | ||
"@types/node": "^12.20.55", | ||
"@ljharb/eslint-config": "^21.2.0", | ||
"auto-changelog": "^2.5.0", | ||
"browserify": "^13.3.0", | ||
@@ -63,3 +60,4 @@ "browserify-istanbul": "^2.0.0", | ||
"cross-spawn": "^4.0.2", | ||
"eslint": "^6.8.0", | ||
"encoding": "^0.1.13", | ||
"eslint": "=8.8.0", | ||
"fake": "^0.2.2", | ||
@@ -69,4 +67,4 @@ "far": "^0.0.7", | ||
"in-publish": "^2.0.1", | ||
"is-node-modern": "^1.0.0", | ||
"istanbul": "^0.4.5", | ||
"js-randomness-predictor": "^1.5.5", | ||
"obake": "^0.1.2", | ||
@@ -76,8 +74,17 @@ "phantomjs-prebuilt": "^2.1.16", | ||
"pre-commit": "^1.2.2", | ||
"puppeteer": "^1.20.0", | ||
"request": "~2.87.0", | ||
"rimraf": "^2.7.1", | ||
"tape": "^5.9.0", | ||
"typescript": "^3.9.10" | ||
"semver": "^6.3.1", | ||
"tape": "^5.9.0" | ||
}, | ||
"license": "MIT" | ||
"license": "MIT", | ||
"auto-changelog": { | ||
"output": "CHANGELOG.md", | ||
"template": "keepachangelog", | ||
"unreleased": false, | ||
"commitLimit": false, | ||
"backfillLimit": false, | ||
"hideCredit": true | ||
} | ||
} |
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
90032
195.63%9
28.57%467
5.18%6
20%23
4.55%1
Infinity%+ Added
+ Added