Socket
Socket
Sign inDemoInstall

formidable

Package Overview
Dependencies
Maintainers
5
Versions
78
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

formidable - npm Package Compare versions

Comparing version 2.0.0-dev.20200131.2 to 3.1.1-canary.20211030

src/FormidableError.js

58

CHANGELOG.md

@@ -0,19 +1,43 @@

# Changelog
### Unreleased 3.1.1
* feat: handle top level json array, string and number
### Unreleased 3.1
* feat: add firstValues, readBooleans helpers
### Unreleased 3.0
* feat: remove options.multiples ([730](https://github.com/node-formidable/formidable/pull/730))
* use modern URLSearchParams https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams internally
* files and fields values are always arrays
* fields with [] in the name do not receive special treatment
* remove unused qs and querystring dependency
* feat: Use ES modules ([727](https://github.com/node-formidable/formidable/pull/727))
* options.enabledPlugins must contain the plugin themselves instead of the plugins names
### Unreleased (`canary` & `dev` dist-tags)
* Test only on Node.js >= v10. Support only Node LTS and latest ([#515](https://github.com/node-formidable/node-formidable/pull/515))
* stop using deprecated features ([#516](https://github.com/node-formidable/node-formidable/pull/516), [#472](https://github.com/node-formidable/node-formidable/issues/472), [#406](https://github.com/node-formidable/node-formidable/issues/406))
* throw error during data parsing ([#513](https://github.com/node-formidable/node-formidable/pull/513))
* Array support for fields and files ([#380](https://github.com/node-formidable/node-formidable/pull/380), [#340](https://github.com/node-formidable/node-formidable/pull/340), [#367](https://github.com/node-formidable/node-formidable/pull/367), [#33](https://github.com/node-formidable/node-formidable/issues/33), [#498](https://github.com/node-formidable/node-formidable/issues/498), [#280](https://github.com/node-formidable/node-formidable/issues/280), [#483](https://github.com/node-formidable/node-formidable/issues/483))
* feat: add options.filter ([#716](https://github.com/node-formidable/formidable/pull/716))
* feat: add code and httpCode to most errors ([#686](https://github.com/node-formidable/formidable/pull/686))
* rename: option.hash into option.hashAlgorithm ([#689](https://github.com/node-formidable/formidable/pull/689))
* rename: file.path into file.filepath ([#689](https://github.com/node-formidable/formidable/pull/689))
* rename: file.type into file.mimetype ([#689](https://github.com/node-formidable/formidable/pull/689))
* refactor: split file.name into file.newFilename and file.originalFilename ([#689](https://github.com/node-formidable/formidable/pull/689))
* feat: prevent directory traversal attacks by default ([#689](https://github.com/node-formidable/formidable/pull/689))
* meta: stop including test files in npm ([7003c](https://github.com/node-formidable/formidable/commit/7003cd6133f90c384081accb51743688d5e1f4be))
* fix: handle invalid filenames ([d0a34](https://github.com/node-formidable/formidable/commit/d0a3484b048b8c177e62d66aecb03f5928f7a857))
* feat: add fileWriteStreamHandler option
* feat: add allowEmptyFiles and minFileSize options
* feat: Array support for fields and files ([#380](https://github.com/node-formidable/node-formidable/pull/380), [#340](https://github.com/node-formidable/node-formidable/pull/340), [#367](https://github.com/node-formidable/node-formidable/pull/367), [#33](https://github.com/node-formidable/node-formidable/issues/33), [#498](https://github.com/node-formidable/node-formidable/issues/498), [#280](https://github.com/node-formidable/node-formidable/issues/280), [#483](https://github.com/node-formidable/node-formidable/issues/483))
* possible partial fix of [#386](https://github.com/node-formidable/node-formidable/pull/386) with #380 (need tests and better implementation)
* use hasOwnProperty in check against files/fields ([#522](https://github.com/node-formidable/node-formidable/pull/522))
* do not promote `IncomingForm` and add `exports.default` ([#529](https://github.com/node-formidable/node-formidable/pull/529))
* Improve examples and tests ([#523](https://github.com/node-formidable/node-formidable/pull/523))
* First step of Code quality improvements ([#525](https://github.com/node-formidable/node-formidable/pull/525))
* refactor: use hasOwnProperty in check against files/fields ([#522](https://github.com/node-formidable/node-formidable/pull/522))
* meta: do not promote `IncomingForm` and add `exports.default` ([#529](https://github.com/node-formidable/node-formidable/pull/529))
* meta: Improve examples and tests ([#523](https://github.com/node-formidable/node-formidable/pull/523))
* refactor: First step of Code quality improvements ([#525](https://github.com/node-formidable/node-formidable/pull/525))
* chore(funding): remove patreon & add npm funding field ([#525](https://github.com/node-formidable/node-formidable/pull/532)
* feat: use Modern Streams API ([#531](https://github.com/node-formidable/node-formidable/pull/531))
* fix: remove gently hijack and tests ([#539](https://github.com/node-formidable/node-formidable/pull/539))
* docs: Clarify supported hash algorithms ([#537](https://github.com/node-formidable/node-formidable/pull/537))
* feat: better tests, add Airbnb + Prettier ([#542](https://github.com/node-formidable/node-formidable/pull/542))
* fix(incomingForm): better detection of fields vs files
* fix: resolves [#128](https://github.com/node-formidable/node-formidable/pull/128)
* fix: urlencoded parsing to emit end [#543](https://github.com/node-formidable/node-formidable/pull/543), introduced in [#531](https://github.com/node-formidable/node-formidable/pull/531)

@@ -24,5 +48,7 @@ * fix(tests): include multipart and qs parser unit tests, part of [#415](https://github.com/node-formidable/node-formidable/issues/415)

* feat: introduce Plugins API, fix silent failing tests ([#545](https://github.com/node-formidable/node-formidable/pull/545), [#391](https://github.com/node-formidable/node-formidable/pull/391), [#407](https://github.com/node-formidable/node-formidable/pull/407), [#386](https://github.com/node-formidable/node-formidable/pull/386), [#374](https://github.com/node-formidable/node-formidable/pull/374), [#521](https://github.com/node-formidable/node-formidable/pull/521), [#267](https://github.com/node-formidable/node-formidable/pull/267))
* respect form hash option on incoming octect/stream requests ([#407](https://github.com/node-formidable/node-formidable/pull/407))
* fix: exposing file writable stream errors ([#520](https://github.com/node-formidable/node-formidable/pull/520), [#316](https://github.com/node-formidable/node-formidable/pull/316), [#469](https://github.com/node-formidable/node-formidable/pull/469), [#470](https://github.com/node-formidable/node-formidable/pull/470))
* feat: custom file (re)naming, thru options.filename ([#591](https://github.com/node-formidable/node-formidable/pull/591), [#84](https://github.com/node-formidable/node-formidable/issues/84), [#86](https://github.com/node-formidable/node-formidable/issues/86), [#94](https://github.com/node-formidable/node-formidable/issues/94), [#154](https://github.com/node-formidable/node-formidable/issues/154), [#158](https://github.com/node-formidable/node-formidable/issues/158), [#488](https://github.com/node-formidable/node-formidable/issues/488), [#595](https://github.com/node-formidable/node-formidable/issues/595))
### v1.2.1 (2018-03-20)

@@ -83,1 +109,5 @@

* Fix file handle leak on error (OrangeDog)
---
[First commit, #3270eb4b1f8b (May 4th, 2010)](https://github.com/node-formidable/formidable/commit/3270eb4b1f8bb667b8c12f64c36a4e7b854216d8)
{
"name": "formidable",
"version": "2.0.0-dev.20200131.2",
"version": "3.1.1-canary.20211030",
"license": "MIT",
"description": "A node.js module for parsing form data, especially file uploads.",
"homepage": "https://github.com/node-formidable/node-formidable",
"homepage": "https://github.com/node-formidable/formidable",
"funding": "https://ko-fi.com/tunnckoCore/commissions",
"repository": "node-formidable/node-formidable",
"repository": "node-formidable/formidable",
"type": "module",
"main": "./src/index.js",
"files": [
"src",
"test"
"CHANGELOG.md",
"LICENSE",
"README.md"
],
"publishConfig": {
"access": "public",
"tag": "dev"
"tag": "3.x"
},

@@ -24,37 +27,44 @@ "scripts": {

"lint:prepare": "eslint --cache --fix --quiet --format codeframe",
"reinstall": "rm -rf node_modules yarn.lock && yarn",
"pretest": "rm -rf test/tmp && mkdir test/tmp",
"test": "node test/run.js",
"pretest:ci": "yarn pretest",
"test:ci": "nyc node test/run.js"
"reinstall": "del-cli ./node_modules ./yarn.lock",
"postreinstall": "yarn setup",
"setup": "yarn",
"pretest": "del-cli ./test/tmp && make-dir ./test/tmp",
"test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --coverage",
"pretest:ci": "yarn run pretest",
"test:ci": "node --experimental-vm-modules node_modules/.bin/nyc jest --coverage"
},
"dependencies": {
"dezalgo": "^1.0.3",
"once": "^1.4.0"
"dezalgo": "1.0.3",
"hexoid": "1.0.0",
"once": "1.4.0"
},
"devDependencies": {
"@commitlint/cli": "^8.3.5",
"@commitlint/config-conventional": "^8.3.4",
"@tunnckocore/prettier-config": "^1.2.0",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-import": "^2.20.0",
"eslint-plugin-prettier": "^3.1.2",
"husky": "^4.2.1",
"jest": "^25.1.0",
"koa": "^2.11.0",
"lint-staged": "^10.0.6",
"nyc": "^15.0.0",
"prettier": "^1.19.1",
"prettier-plugin-pkgjson": "^0.2.0",
"request": "^2.88.0",
"supertest": "^4.0.2",
"urun": "^0.0.8",
"utest": "^0.0.8"
"@commitlint/cli": "8.3.5",
"@commitlint/config-conventional": "8.3.4",
"@tunnckocore/prettier-config": "1.3.8",
"del-cli": "3.0.0",
"eslint": "6.8.0",
"eslint-config-airbnb-base": "14.1.0",
"eslint-config-prettier": "6.11.0",
"eslint-plugin-import": "2.20.2",
"eslint-plugin-prettier": "3.1.3",
"express": "4.17.1",
"husky": "4.2.5",
"jest": "27.2.4",
"koa": "2.11.0",
"lint-staged": "10.2.7",
"make-dir-cli": "2.0.0",
"nyc": "15.1.0",
"prettier": "2.0.5",
"prettier-plugin-pkgjson": "0.2.8",
"request": "2.88.2",
"supertest": "6.1.6"
},
"jest": {
"verbose": true
},
"husky": {
"hooks": {
"pre-commit": "git status --porcelain && lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
"pre-commit": "git status --porcelain && yarn lint-staged",
"commit-msg": "yarn commitlint -E HUSKY_GIT_PARAMS"
}

@@ -74,3 +84,20 @@ },

]
}
},
"renovate": {
"extends": [
"@tunnckocore",
":pinAllExceptPeerDependencies"
]
},
"packageManager": "yarn@1.22.11",
"keywords": [
"multipart",
"form",
"data",
"querystring",
"www",
"json",
"ulpoad",
"file"
]
}
<p align="center">
<img alt="node formidable logo" src="./logo.png" />
<img alt="npm formidable package logo" src="https://raw.githubusercontent.com/node-formidable/formidable/master/logo.png" />
</p>
# formidable [![npm version][npmv-img]][npmv-url] [![MIT license][license-img]][license-url] [![Libera Manifesto][libera-manifesto-img]][libera-manifesto-url]
# formidable [![npm version][npmv-img]][npmv-url] [![MIT license][license-img]][license-url] [![Libera Manifesto][libera-manifesto-img]][libera-manifesto-url] [![Twitter][twitter-img]][twitter-url]

@@ -10,10 +10,36 @@ > A Node.js module for parsing form data, especially file uploads.

[![Code style][codestyle-img]][codestyle-url]
[![build status][build-img]][build-url]
[![codecoverage][codecov-img]][codecov-url]
[![linux build status][linux-build-img]][build-url]
[![windows build status][windows-build-img]][build-url]
[![macos build status][macos-build-img]][build-url]
If you have any _how-to_ kind of questions, please read the [Contributing
Guide][contributing-url] and [Code of Conduct][code_of_conduct-url]
documents.<br /> For bugs reports and feature requests, [please create an
issue][open-issue-url] or ping [@tunnckoCore](https://twitter.com/tunnckoCore)
at Twitter.
[![Conventional Commits][ccommits-img]][ccommits-url]
[![Minimum Required Nodejs][nodejs-img]][npmv-url]
[![Tidelift Subcsription][tidelift-img]][tidelift-url]
[![Buy me a Kofi][kofi-img]][kofi-url]
[![Renovate App Status][renovateapp-img]][renovateapp-url]
[![Make A Pull Request][prs-welcome-img]][prs-welcome-url]
[![Twitter][twitter-img]][twitter-url]
## Status: Maintained [![npm version][npmv-canary-img]][npmv-url] [![npm version][npmv-dev-img]][npmv-url]
This project is [semantically versioned](https://semver.org) and available as
part of the [Tidelift Subscription][tidelift-url] for professional grade
assurances, enhanced support and security.
[Learn more.](https://tidelift.com/subscription/pkg/npm-formidable?utm_source=npm-formidable&utm_medium=referral&utm_campaign=enterprise)
_The maintainers of `formidable` and thousands of other packages are working
with Tidelift to deliver commercial support and maintenance for the Open Source
dependencies you use to build your applications. Save time, reduce risk, and
improve code health, while paying the maintainers of the exact dependencies you
use._
[![][npm-weekly-img]][npmv-url] [![][npm-monthly-img]][npmv-url]
[![][npm-yearly-img]][npmv-url] [![][npm-alltime-img]][npmv-url]
## Status: Maintained [![npm version][npmv-canary-img]][npmv-url]
This module was initially developed by

@@ -28,21 +54,19 @@ [**@felixge**](https://github.com/felixge) for

are always welcome! :heart: Jump on
[issue #412](https://github.com/felixge/node-formidable/issues/412) if you are
interested.
[issue #412](https://github.com/felixge/node-formidable/issues/412) which is
closed, but if you are interested we can discuss it and add you after strict
rules, like enabling Two-Factor Auth in your npm and GitHub accounts.
_**Note:** Master is a "canary" branch - try it with `npm i formidable@canary`.
Do not expect (for now) things from it to be inside the`latest`"dist-tag" in the
Npm. The`formidable@latest`is the`v1.2.1` version and probably it will be the
last`v1` release!_
_**Note:** The github `master` branch is a "canary" branch - try it with
`npm i formidable@canary`. Do not expect (for now) things from it to be inside
the`latest` "dist-tag" in the Npm. The`formidable@latest`is the`v1.2.1` version
and probably it will be the last`v1` release!_
_**Note: v2 is coming soon!**_
You can try the
[Plugins API](https://github.com/felixge/node-formidable/tree/plugins-api)
([#545](https://github.com/felixge/node-formidable/pull/545)), which is
available through `formidable@dev`.
## Highlights
- Fast (~900-2500 mb/sec), streaming multipart parser
- Automatically writing file uploads to disk
- [Fast (~900-2500 mb/sec)](#benchmarks) & streaming multipart parser
- Automatically writing file uploads to disk (optional, see
[`options.fileWriteStreamHandler`](#options))
- [Plugins API](#useplugin-plugin) - allowing custom parsers and plugins
- Low memory footprint

@@ -54,2 +78,6 @@ - Graceful error handling

This project requires `Node.js >= 10.13`. Install it using
[yarn](https://yarnpkg.com) or [npm](https://npmjs.com).<br /> _We highly
recommend to use Yarn when you think to contribute to this project._
```sh

@@ -59,2 +87,4 @@ npm install formidable

npm install formidable@canary
## 3.x
npm install formidable@3.x
```

@@ -70,51 +100,165 @@

This is a low-level package, and if you're using a high-level framework it may
already be included.
This is a low-level package, and if you're using a high-level framework it _may_
already be included. Check the examples below and the `examples/` folder.
However, [Express v4](http://expressjs.com) does not include any multipart
handling, nor does [body-parser](https://github.com/expressjs/body-parser).
## Examples
For `koa` there is [koa-better-body](https://ghub.now.sh/koa-better-body) which
can handle ANY type of body / form-data - JSON, urlencoded, multpart and so on.
A new major release is coming there too.
For more examples look at the `examples/` directory.
## Example
### with Node.js http module
Parse an incoming file upload.
Parse an incoming file upload, with the
[Node.js's built-in `http` module](https://nodejs.org/api/http.html).
```js
const http = require('http');
const util = require('util');
const formidable = require('formidable');
import http from 'http';
import formidable from 'formidable';
http
.createServer((req, res) => {
if (req.url === '/upload' && req.method.toLowerCase() === 'post') {
// parse a file upload
const form = formidable();
const server = http.createServer((req, res) => {
if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
// parse a file upload
const form = formidable({});
form.parse(req, (err, fields, files) => {
res.writeHead(200, { 'content-type': 'text/plain' });
res.write('received upload:\n\n');
res.end(util.inspect({ fields: fields, files: files }));
});
form.parse(req, (err, fields, files) => {
if (err) {
res.writeHead(err.httpCode || 400, { 'Content-Type': 'text/plain' });
res.end(String(err));
return;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ fields, files }, null, 2));
});
return;
}
// show a file upload form
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<h2>With Node.js <code>"http"</code> module</h2>
<form action="/api/upload" enctype="multipart/form-data" method="post">
<div>Text field title: <input type="text" name="title" /></div>
<div>File: <input type="file" name="multipleFiles" multiple="multiple" /></div>
<input type="submit" value="Upload" />
</form>
`);
});
server.listen(8080, () => {
console.log('Server listening on http://localhost:8080/ ...');
});
```
### with Express.js
There are multiple variants to do this, but Formidable just need Node.js Request
stream, so something like the following example should work just fine, without
any third-party [Express.js](https://ghub.now.sh/express) middleware.
Or try the
[examples/with-express.js](https://github.com/node-formidable/formidable/blob/master/examples/with-express.js)
```js
import express from 'express';
import formidable from 'formidable';
const app = express();
app.get('/', (req, res) => {
res.send(`
<h2>With <code>"express"</code> npm package</h2>
<form action="/api/upload" enctype="multipart/form-data" method="post">
<div>Text field title: <input type="text" name="title" /></div>
<div>File: <input type="file" name="someExpressFiles" multiple="multiple" /></div>
<input type="submit" value="Upload" />
</form>
`);
});
app.post('/api/upload', (req, res, next) => {
const form = formidable({});
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
res.json({ fields, files });
});
});
// show a file upload form
res.writeHead(200, { 'content-type': 'text/html' });
res.end(`
<form action="/upload" enctype="multipart/form-data" method="post">
<input type="text" name="title" /><br/>
<input type="file" name="upload" multiple="multiple" /><br/>
<input type="submit" value="Upload" />
</form>
`);
})
.listen(8080, () => {
console.log('Server listening on http://localhost:8080/ ...');
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
```
### with Koa and Formidable
Of course, with [Koa v1, v2 or future v3](https://ghub.now.sh/koa) the things
are very similar. You can use `formidable` manually as shown below or through
the [koa-better-body](https://ghub.now.sh/koa-better-body) package which is
using `formidable` under the hood and support more features and different
request bodies, check its documentation for more info.
_Note: this example is assuming Koa v2. Be aware that you should pass `ctx.req`
which is Node.js's Request, and **NOT** the `ctx.request` which is Koa's Request
object - there is a difference._
```js
import Koa from 'Koa';
import formidable from 'formidable';
const app = new Koa();
app.on('error', (err) => {
console.error('server error', err);
});
app.use(async (ctx, next) => {
if (ctx.url === '/api/upload' && ctx.method.toLowerCase() === 'post') {
const form = formidable({});
// not very elegant, but that's for now if you don't want to use `koa-better-body`
// or other middlewares.
await new Promise((resolve, reject) => {
form.parse(ctx.req, (err, fields, files) => {
if (err) {
reject(err);
return;
}
ctx.set('Content-Type', 'application/json');
ctx.status = 200;
ctx.state = { fields, files };
ctx.body = JSON.stringify(ctx.state, null, 2);
resolve();
});
});
await next();
return;
}
// show a file upload form
ctx.set('Content-Type', 'text/html');
ctx.status = 200;
ctx.body = `
<h2>With <code>"koa"</code> npm package</h2>
<form action="/api/upload" enctype="multipart/form-data" method="post">
<div>Text field title: <input type="text" name="title" /></div>
<div>File: <input type="file" name="koaFiles" multiple="multiple" /></div>
<input type="submit" value="Upload" />
</form>
`;
});
app.use((ctx) => {
console.log('The next middleware is called');
console.log('Results:', ctx.state);
});
app.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});
```
## Benchmarks

@@ -160,20 +304,8 @@

_Please pass [`options`](#options) to the function/constructor, not by passing
assigning them to the instance `form`_
_Please pass [`options`](#options) to the function/constructor, not by assigning
them to the instance `form`_
```js
const formidable = require('formidable');
import formidable from 'formidable';
const form = formidable(options);
// or
const { formidable } = require('formidable');
const form = formidable(options);
// or
const { IncomingForm } = require('formidable');
const form = new IncomingForm(options);
// or
const { Formidable } = require('formidable');
const form = new Formidable(options);
```

@@ -183,4 +315,4 @@

See it's defaults in [src/Formidable.js](./src/Formidable.js#L14-L22) (the
`DEFAULT_OPTIONS` constant).
See it's defaults in [src/Formidable.js DEFAULT_OPTIONS](./src/Formidable.js)
(the `DEFAULT_OPTIONS` constant).

@@ -190,25 +322,48 @@ - `options.encoding` **{string}** - default `'utf-8'`; sets encoding for

- `options.uploadDir` **{string}** - default `os.tmpdir()`; the directory for
placing file uploads in. You can move them later by using `fs.rename()`
placing file uploads in. You can move them later by using `fs.rename()`.
- `options.keepExtensions` **{boolean}** - default `false`; to include the
extensions of the original files or not
- `options.allowEmptyFiles` **{boolean}** - default `true`; allow upload empty
files
- `options.minFileSize` **{number}** - default `1` (1byte); the minium size of
uploaded file.
- `options.maxFileSize` **{number}** - default `200 * 1024 * 1024` (200mb);
limit the size of uploaded file.
- `options.maxFields` **{number}** - default `1000`; limit the number of fields
that the Querystring parser will decode, set 0 for unlimited
- `options.maxFields` **{number}** - default `1000`; limit the number of fields, set 0 for unlimited
- `options.maxFieldsSize` **{number}** - default `20 * 1024 * 1024` (20mb);
limit the amount of memory all fields together (except files) can allocate in
bytes.
- `options.hash` **{boolean}** - default `false`; include checksums calculated
- `options.hashAlgorithm` **{string | false}** - default `false`; include checksums calculated
for incoming files, set this to some hash algorithm, see
[crypto.createHash](https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm_options)
for available algorithms
- `options.multiples` **{boolean}** - default `false`; when you call the
`.parse` method, the `files` argument (of the callback) will contain arrays of
files for inputs which submit multiple files using the HTML5 `multiple`
attribute. Also, the `fields` argument will contain arrays of values for
fields that have names ending with '[]'.
- `options.fileWriteStreamHandler` **{function}** - default `null`, which by
default writes to host machine file system every file parsed; The function
should return an instance of a
[Writable stream](https://nodejs.org/api/stream.html#stream_class_stream_writable)
that will receive the uploaded file data. With this option, you can have any
custom behavior regarding where the uploaded file data will be streamed for.
If you are looking to write the file uploaded in other types of cloud storages
(AWS S3, Azure blob storage, Google cloud storage) or private file storage,
this is the option you're looking for. When this option is defined the default
behavior of writing the file in the host machine file system is lost.
- `options.filename` **{function}** - default `undefined` Use it to control
newFilename. Must return a string. Will be joined with options.uploadDir.
_**Note:** If this value is exceeded, an `'error'` event is emitted._
- `options.filter` **{function}** - default function that always returns true.
Use it to filter files before they are uploaded. Must return a boolean.
#### `options.filename` **{function}** function (name, ext, part, form) -> string
where part can be decomposed as
```js
const { originalFilename, mimetype} = part;
```
_**Note:** If this size of combined fields, or size of some file is exceeded, an
`'error'` event is fired._
```js
// The amount of bytes received for this form so far.

@@ -223,2 +378,16 @@ form.bytesReceived;

#### `options.filter` **{function}** function ({name, originalFilename, mimetype}) -> boolean
**Note:** use an outside variable to cancel all uploads upon the first error
```js
const options = {
filter: function ({name, originalFilename, mimetype}) {
// keep only images
return mimetype && mimetype.includes("image");
}
};
```
### .parse(request, callback)

@@ -230,6 +399,4 @@

```js
const formidable = require('formidable');
const form = formidable({ uploadDir: __dirname });
const form = formidable({ multiples: true, uploadDir: __dirname });
form.parse(req, (err, fields, files) => {

@@ -246,2 +413,37 @@ console.log('fields:', fields);

About `uploadDir`, given the following directory structure
```
project-name
├── src
│ └── server.js
└── uploads
└── image.jpg
```
`__dirname` would be the same directory as the source file itself (src)
```js
`${__dirname}/../uploads`
```
to put files in uploads.
Omitting `__dirname` would make the path relative to the current working directory. This would be the same if server.js is launched from src but not project-name.
`null` will use default which is `os.tmpdir()`
Note: If the directory does not exist, the uploaded files are __silently discarded__. To make sure it exists:
```js
import {createNecessaryDirectoriesSync} from "filesac";
const uploadPath = `${__dirname}/../uploads`;
createNecessaryDirectoriesSync(`${uploadPath}/x`);
```
In the example below, we listen on couple of events and direct them to the

@@ -258,8 +460,8 @@ `data` listener, so you can do whatever you choose there, based on whether its

form.on('fileBegin', (filename, file) => {
form.emit('data', { name: 'fileBegin', filename, value: file });
form.on('fileBegin', (formname, file) => {
form.emit('data', { name: 'fileBegin', formname, value: file });
});
form.on('file', (filename, file) => {
form.emit('data', { name: 'file', key: filename, value: file });
form.on('file', (formname, file) => {
form.emit('data', { name: 'file', formname, value: file });
});

@@ -276,3 +478,3 @@

// If you want to customize whatever you want...
form.on('data', ({ name, key, value, buffer, start, end, ...more }) => {
form.on('data', ({ name, key, value, buffer, start, end, formname, ...more }) => {
if (name === 'partBegin') {

@@ -295,6 +497,6 @@ }

if (name === 'file') {
console.log('file:', key, value);
console.log('file:', formname, value);
}
if (name === 'fileBegin') {
console.log('fileBegin:', key, value);
console.log('fileBegin:', formname, value);
}

@@ -325,4 +527,2 @@ });

```js
const formidable = require('formidable');
const form = formidable({ keepExtensions: true });

@@ -352,7 +552,6 @@

```js
const { Formidable } = require('formidable');
const form = new Formidable({
hash: 'sha1',
enabledPlugins: ['octetstream', 'querystring', 'json'],
import formidable, {octetstream, querystring, json} from "formidable";
const form = formidable({
hashAlgorithm: 'sha1',
enabledPlugins: [octetstream, querystring, json],
});

@@ -379,3 +578,3 @@ ```

form.onPart = (part) => {
part.on('data', (buffer) {
part.on('data', (buffer) => {
// do whatever you want here

@@ -392,7 +591,7 @@ });

form.onPart = function(part) {
form.onPart = function (part) {
// let formidable handle only non-file parts
if (part.filename === '' || !part.mime) {
if (part.originalFilename === '' || !part.mimetype) {
// used internally, please do not override!
form.handlePart(part);
form._handlePart(part);
}

@@ -413,16 +612,20 @@ };

// case you are unhappy with the way formidable generates a temporary path for your files.
file.path: string;
file.filepath: string;
// The name this file had according to the uploading client.
file.name: string | null;
file.originalFilename: string | null;
// calculated based on options provided
file.newFilename: string | null;
// The mime type of this file, according to the uploading client.
file.type: string | null;
file.mimetype: string | null;
// A Date object (or `null`) containing the time this file was last written to.
// Mostly here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).
file.lastModifiedDate: Date | null;
file.mtime: Date | null;
// If `options.hash` calculation was set, you can read the hex digest out of this var.
file.hash: string | 'sha1' | 'md5' | 'sha256' | null;
file.hashAlgorithm: false | |'sha1' | 'md5' | 'sha256'
// If `options.hashAlgorithm` calculation was set, you can read the hex digest out of this var (at the end it will be a string)
file.hash: string | object | null;
}

@@ -442,3 +645,3 @@ ```

Emitted after each incoming chunk of data that has been parsed. Can be used to
roll your own progress bar.
roll your own progress bar. **Warning** Use this only for server side progress bar. On the client side better use `XMLHttpRequest` with `xhr.upload.onprogress =`

@@ -464,3 +667,10 @@ ```js

```js
form.on('fileBegin', (name, file) => {});
form.on('fileBegin', (formName, file) => {
// accessible here
// formName the name in the form (<input name="thisname" type="file">) or http filename for octetstream
// file.originalFilename http filename or null if there was a parsing error
// file.newFilename generated hexoid or what options.filename returned
// file.filepath default pathnme as per options.uploadDir and options.filename
// file.filepath = CUSTOM_PATH // to change the final path
});
```

@@ -474,3 +684,7 @@

```js
form.on('file', (name, file) => {});
form.on('file', (formname, file) => {
// same as fileBegin, except
// it is too late to change file.filepath
// file.hash is available if options.hash was used
});
```

@@ -484,2 +698,4 @@

May have `error.httpCode` and `error.code` attached.
```js

@@ -509,2 +725,42 @@ form.on('error', (err) => {});

### Helpers
#### firstValues
Gets first values of fields, like pre 3.0.0 without multiples pass in a list of optional exceptions where arrays of strings is still wanted (`<select multiple>` for example)
```js
import { firstValues } from 'formidable/src/helpers/firstValues.js';
// ...
form.parse(request, async (error, fieldsMultiple, files) => {
if (error) {
//...
}
const exceptions = ['thisshouldbeanarray'];
const fieldsSingle = firstValues(form, fieldsMultiple, exceptions);
// ...
```
#### readBooleans
Html form input type="checkbox" only send the value "on" if checked,
convert it to booleans for each input that is expected to be sent as a checkbox, only use after firstValues or similar was called.
```js
import { firstValues } from 'formidable/src/helpers/firstValues.js';
import { readBooleans } from 'formidable/src/helpers/readBooleans.js';
// ...
form.parse(request, async (error, fieldsMultiple, files) => {
if (error) {
//...
}
const fieldsSingle = firstValues(form, fieldsMultiple);
const expectedBooleans = ['checkbox1', 'wantsNewsLetter', 'hasACar'];
const fieldsWithBooleans = readBooleans(fieldsSingle, expectedBooleans);
// ...
```
## Ports & Credits

@@ -522,4 +778,4 @@

button (pencil icon) and suggest a correction. If you would like to help us fix
a bug or add a new feature, please check our
[Contributing Guide](./CONTRIBUTING.md). Pull requests are welcome!
a bug or add a new feature, please check our [Contributing
Guide][contributing-url]. Pull requests are welcome!

@@ -554,2 +810,3 @@ Thanks goes to these wonderful people

<td align="center"><a href="https://github.com/dmolim"><img src="https://avatars2.githubusercontent.com/u/7090374?v=4" width="100px;" alt=""/><br /><sub><b>Dmitry Ivonin</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/commits?author=dmolim" title="Documentation">📖</a></td>
<td align="center"><a href="https://audiobox.fm"><img src="https://avatars1.githubusercontent.com/u/12844?v=4" width="100px;" alt=""/><br /><sub><b>Claudio Poli</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/commits?author=masterkain" title="Code">💻</a></td>
</tr>

@@ -563,2 +820,9 @@ </table>

From a [Felix blog post](https://felixge.de/2013/03/11/the-pull-request-hack/):
- [Sven Lito](https://github.com/svnlto) for fixing bugs and merging patches
- [egirshov](https://github.com/egirshov) for contributing many improvements to the node-formidable multipart parser
- [Andrew Kelley](https://github.com/superjoe30) for also helping with fixing bugs and making improvements
- [Mike Frey](https://github.com/mikefrey) for contributing JSON support
## License

@@ -573,6 +837,4 @@

[codestyle-img]: https://badgen.net/badge/code%20style/airbnb%20%2B%20prettier/ff5a5f?icon=airbnb&cache=300
[codecov-url]: https://codecov.io/gh/node-formidable/node-formidable
[codecov-img]: https://badgen.net/codecov/c/github/node-formidable/node-formidable/master?icon=codecov
[build-img]: https://badgen.net/github/checks/node-formidable/node-formidable?label=build&icon=github
[build-url]: https://github.com/node-formidable/node-formidable/actions?query=workflow%3Anodejs
[codecov-url]: https://codecov.io/gh/node-formidable/formidable
[codecov-img]: https://badgen.net/codecov/c/github/node-formidable/formidable/master?icon=codecov
[npmv-canary-img]: https://badgen.net/npm/v/formidable/canary?icon=npm

@@ -583,3 +845,3 @@ [npmv-dev-img]: https://badgen.net/npm/v/formidable/dev?icon=npm

[license-img]: https://badgen.net/npm/license/formidable
[license-url]: https://github.com/node-formidable/node-formidable/blob/master/LICENSE
[license-url]: https://github.com/node-formidable/formidable/blob/master/LICENSE
[chat-img]: https://badgen.net/badge/chat/on%20gitter/46BC99?icon=gitter

@@ -596,2 +858,27 @@ [chat-url]: https://gitter.im/node-formidable/Lobby

[npm-weekly-img]: https://badgen.net/npm/dw/formidable?icon=npm&cache=300
[npm-monthly-img]: https://badgen.net/npm/dm/formidable?icon=npm&cache=300
[npm-yearly-img]: https://badgen.net/npm/dy/formidable?icon=npm&cache=300
[npm-alltime-img]: https://badgen.net/npm/dt/formidable?icon=npm&cache=300&label=total%20downloads
[nodejs-img]: https://badgen.net/badge/node/>=%2010.13/green?cache=300
[ccommits-url]: https://conventionalcommits.org/
[ccommits-img]: https://badgen.net/badge/conventional%20commits/v1.0.0/green?cache=300
[contributing-url]: https://github.com/node-formidable/.github/blob/master/CONTRIBUTING.md
[code_of_conduct-url]: https://github.com/node-formidable/.github/blob/master/CODE_OF_CONDUCT.md
[open-issue-url]: https://github.com/node-formidable/formidable/issues/new
[tidelift-url]: https://tidelift.com/subscription/pkg/npm-formidable?utm_source=npm-formidable&utm_medium=referral&utm_campaign=enterprise
[tidelift-img]: https://badgen.net/badge/tidelift/subscription/4B5168?labelColor=F6914D
[kofi-url]: https://ko-fi.com/tunnckoCore/commissions
[kofi-img]: https://badgen.net/badge/ko-fi/support/29abe0c2?cache=300&icon=https://rawcdn.githack.com/tunnckoCore/badgen-icons/f8264c6414e0bec449dd86f2241d50a9b89a1203/icons/kofi.svg
[linux-build-img]: https://badgen-net.charlike.now.sh/github/checks/node-formidable/formidable/master/ubuntu?cache=300&label=linux%20build&icon=github
[macos-build-img]: https://badgen-net.charlike.now.sh/github/checks/node-formidable/formidable/master/macos?cache=300&label=macos%20build&icon=github
[windows-build-img]: https://badgen-net.charlike.now.sh/github/checks/node-formidable/formidable/master/windows?cache=300&label=windows%20build&icon=github
[build-url]: https://github.com/node-formidable/formidable/actions?query=workflow%3Anodejs
<!-- prettier-ignore-end -->
/* eslint-disable class-methods-use-this */
/* eslint-disable no-underscore-dangle */
'use strict';
import os from 'os';
import path from 'path';
import hexoid from 'hexoid';
import once from 'once';
import dezalgo from 'dezalgo';
import { EventEmitter } from 'events';
import { StringDecoder } from 'string_decoder';
import { octetstream, querystring, multipart, json } from './plugins/index.js';
import PersistentFile from './PersistentFile.js';
import VolatileFile from './VolatileFile.js';
import DummyParser from './parsers/Dummy.js';
import MultipartParser from './parsers/Multipart.js';
import * as errors from './FormidableError.js';
import FormidableError from './FormidableError.js';
const os = require('os');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const once = require('once');
const dezalgo = require('dezalgo');
const { EventEmitter } = require('events');
const { StringDecoder } = require('string_decoder');
const toHexoId = hexoid(25);
const DEFAULT_OPTIONS = {

@@ -19,13 +24,16 @@ maxFields: 1000,

maxFileSize: 200 * 1024 * 1024,
minFileSize: 1,
allowEmptyFiles: true,
keepExtensions: false,
encoding: 'utf-8',
hash: false,
multiples: false,
enabledPlugins: ['octetstream', 'querystring', 'multipart', 'json'],
hashAlgorithm: false,
uploadDir: os.tmpdir(),
enabledPlugins: [octetstream, querystring, multipart, json],
fileWriteStreamHandler: null,
defaultInvalidName: 'invalid-name',
filter() {
return true;
},
};
const File = require('./File');
const DummyParser = require('./parsers/Dummy');
const MultipartParser = require('./parsers/Multipart');
function hasOwnProp(obj, key) {

@@ -38,15 +46,26 @@ return Object.prototype.hasOwnProperty.call(obj, key);

super();
this.error = null;
this.ended = false;
this.options = { ...DEFAULT_OPTIONS, ...options };
this.uploadDir = this.uploadDir || os.tmpdir();
this.headers = null;
this.type = null;
const dir = path.resolve(
this.options.uploadDir || this.options.uploaddir || os.tmpdir(),
);
this.bytesReceived = null;
this.bytesExpected = null;
this.uploaddir = dir;
this.uploadDir = dir;
this._parser = null;
// initialize with null
[
'error',
'headers',
'type',
'bytesExpected',
'bytesReceived',
'_parser',
].forEach((key) => {
this[key] = null;
});
this._setUpRename();
this._flushing = 0;

@@ -58,17 +77,18 @@ this._fieldsSize = 0;

const enabledPlugins = []
this.options.enabledPlugins = []
.concat(this.options.enabledPlugins)
.filter(Boolean);
if (enabledPlugins.length === 0) {
throw new Error(
if (this.options.enabledPlugins.length === 0) {
throw new FormidableError(
'expect at least 1 enabled builtin plugin, see options.enabledPlugins',
errors.missingPlugin,
);
}
this.options.enabledPlugins.forEach((pluginName) => {
const plgName = pluginName.toLowerCase();
// eslint-disable-next-line import/no-dynamic-require, global-require
this.use(require(path.join(__dirname, 'plugins', `${plgName}.js`)));
this.options.enabledPlugins.forEach((plugin) => {
this.use(plugin);
});
this._setUpMaxFields();
}

@@ -78,3 +98,6 @@

if (typeof plugin !== 'function') {
throw new Error('.use: expect `plugin` to be a function');
throw new FormidableError(
'.use: expect `plugin` to be a function',
errors.pluginFunction,
);
}

@@ -118,50 +141,29 @@ this._plugins.push(plugin.bind(this));

const callback = once(dezalgo(cb));
const fields = {};
this.fields = {};
let mockFields = '';
const files = {};
this.on('field', (name, value) => {
// TODO: too much nesting
if (this.options.multiples && name.slice(-2) === '[]') {
const realName = name.slice(0, name.length - 2);
if (hasOwnProp(fields, realName)) {
if (!Array.isArray(fields[realName])) {
fields[realName] = [fields[realName]];
}
if (this.type === 'multipart' || this.type === 'urlencoded') {
if (!hasOwnProp(this.fields, name)) {
this.fields[name] = [value];
} else {
fields[realName] = [];
this.fields[name].push(value);
}
fields[realName].push(value);
} else {
fields[name] = value;
this.fields[name] = value;
}
// if (name === 'simple') {
// console.log('fields name!!', name);
// console.log('fields value!!', value);
// }
});
this.on('file', (name, file) => {
// TODO: too much nesting
if (this.options.multiples) {
if (hasOwnProp(files, name)) {
if (!Array.isArray(files[name])) {
files[name] = [files[name]];
}
files[name].push(file);
} else {
files[name] = file;
}
if (!hasOwnProp(files, name)) {
files[name] = [file];
} else {
files[name] = file;
files[name].push(file);
}
// console.log('files!!', files);
// if (name === 'simple') {
// console.log('files name!!', name);
// console.log('files value!!', file);
// }
});
this.on('error', (err) => {
callback(err, fields, files);
callback(err, this.fields, files);
});
this.on('end', () => {
callback(null, fields, files);
callback(null, this.fields, files);
});

@@ -180,3 +182,3 @@ }

this.emit('aborted');
this._error(new Error('Request aborted'));
this._error(new FormidableError('Request aborted', errors.aborted));
})

@@ -209,3 +211,9 @@ .on('data', (buffer) => {

if (!this._parser) {
this._error(new Error('not parser found'));
this._error(
new FormidableError(
'no parser found',
errors.noParser,
415, // Unsupported Media Type
),
);
return;

@@ -224,3 +232,5 @@ }

if (!this._parser) {
this._error(new Error('uninitialized parser'));
this._error(
new FormidableError('uninitialized parser', errors.uninitializedParser),
);
return null;

@@ -253,8 +263,13 @@ }

_handlePart(part) {
if (part.filename && typeof part.filename !== 'string') {
this._error(new Error(`the part.filename should be string when exists`));
if (part.originalFilename && typeof part.originalFilename !== 'string') {
this._error(
new FormidableError(
`the part.originalFilename should be string when it exists`,
errors.filenameNotString,
),
);
return;
}
// This MUST check exactly for undefined. You can not change it to !part.filename.
// This MUST check exactly for undefined. You can not change it to !part.originalFilename.

@@ -267,5 +282,5 @@ // todo: uncomment when switch tests to Jest

// and such thing because code style
// ? NOTE(@tunnckocore): or even better, if there is no mime, then it's for sure a field
// ? NOTE(@tunnckocore): filename is an empty string when a field?
if (!part.mime) {
// ? NOTE(@tunnckocore): or even better, if there is no mimetype, then it's for sure a field
// ? NOTE(@tunnckocore): originalFilename is an empty string when a field?
if (!part.mimetype) {
let value = '';

@@ -280,4 +295,6 @@ const decoder = new StringDecoder(

this._error(
new Error(
`options.maxFieldsSize exceeded, received ${this._fieldsSize} bytes of field data`,
new FormidableError(
`options.maxFieldsSize (${this.options.maxFieldsSize} bytes) exceeded, received ${this._fieldsSize} bytes of field data`,
errors.maxFieldsSizeExceeded,
413, // Payload Too Large
),

@@ -296,9 +313,15 @@ );

if (!this.options.filter(part)) {
return;
}
this._flushing += 1;
const file = new File({
path: this._uploadPath(part.filename),
name: part.filename,
type: part.mime,
hash: this.options.hash,
const newFilename = this._getNewName(part);
const filepath = this._joinDirectoryName(newFilename);
const file = this._newFile({
newFilename,
filepath,
originalFilename: part.originalFilename,
mimetype: part.mimetype,
});

@@ -315,6 +338,18 @@ file.on('error', (err) => {

this._fileSize += buffer.length;
if (this._fileSize < this.options.minFileSize) {
this._error(
new FormidableError(
`options.minFileSize (${this.options.minFileSize} bytes) inferior, received ${this._fileSize} bytes of file data`,
errors.smallerThanMinFileSize,
400,
),
);
return;
}
if (this._fileSize > this.options.maxFileSize) {
this._error(
new Error(
`options.maxFileSize exceeded, received ${this._fileSize} bytes of file data`,
new FormidableError(
`options.maxFileSize (${this.options.maxFileSize} bytes) exceeded, received ${this._fileSize} bytes of file data`,
errors.biggerThanMaxFileSize,
413,
),

@@ -334,2 +369,13 @@ );

part.on('end', () => {
if (!this.options.allowEmptyFiles && this._fileSize === 0) {
this._error(
new FormidableError(
`options.allowEmptyFiles is false, file size should be greather than 0`,
errors.noEmptyFiles,
400,
),
);
return;
}
file.end(() => {

@@ -351,3 +397,9 @@ this._flushing -= 1;

if (!this.headers['content-type']) {
this._error(new Error('bad content-type header, no content-type'));
this._error(
new FormidableError(
'bad content-type header, no content-type',
errors.missingContentType,
400,
),
);
return;

@@ -370,4 +422,6 @@ }

// there is no other better way, except a handle through options
const error = new Error(
const error = new FormidableError(
`plugin on index ${idx} failed with: ${err.message}`,
errors.pluginFailed,
500,
);

@@ -411,4 +465,3 @@ error.idx = idx;

this.openedFiles.forEach((file) => {
file._writeStream.destroy();
setTimeout(fs.unlink, 0, file.path, () => {});
file.destroy();
});

@@ -435,7 +488,25 @@ }

_newFile({ filepath, originalFilename, mimetype, newFilename }) {
return this.options.fileWriteStreamHandler
? new VolatileFile({
newFilename,
filepath,
originalFilename,
mimetype,
createFileWriteStream: this.options.fileWriteStreamHandler,
hashAlgorithm: this.options.hashAlgorithm,
})
: new PersistentFile({
newFilename,
filepath,
originalFilename,
mimetype,
hashAlgorithm: this.options.hashAlgorithm,
});
}
_getFileName(headerValue) {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(
// eslint-disable-next-line no-useless-escape
/\bfilename=("(.*?)"|([^\(\)<>@,;:\\"\/\[\]\?=\{\}\s\t/]+))($|;\s)/i,
/\bfilename=("(.*?)"|([^()<>{}[\]@,;:"?=\s/\t]+))($|;\s)/i,
);

@@ -445,24 +516,87 @@ if (!m) return null;

const match = m[2] || m[3] || '';
let filename = match.substr(match.lastIndexOf('\\') + 1);
filename = filename.replace(/%22/g, '"');
filename = filename.replace(/&#([\d]{4});/g, (_, code) =>
let originalFilename = match.substr(match.lastIndexOf('\\') + 1);
originalFilename = originalFilename.replace(/%22/g, '"');
originalFilename = originalFilename.replace(/&#([\d]{4});/g, (_, code) =>
String.fromCharCode(code),
);
return filename;
return originalFilename;
}
_uploadPath(filename) {
const buf = crypto.randomBytes(16);
let name = `upload_${buf.toString('hex')}`;
_getExtension(str) {
if (!str) {
return '';
}
if (this.options.keepExtensions) {
let ext = path.extname(filename);
ext = ext.replace(/(\.[a-z0-9]+).*/i, '$1');
const basename = path.basename(str);
const firstDot = basename.indexOf('.');
const lastDot = basename.lastIndexOf('.');
const extname = path.extname(basename).replace(/(\.[a-z0-9]+).*/i, '$1');
name += ext;
if (firstDot === lastDot) {
return extname;
}
return path.join(this.uploadDir, name);
return basename.slice(firstDot, lastDot) + extname;
}
_joinDirectoryName(name) {
const newPath = path.join(this.uploadDir, name);
// prevent directory traversal attacks
if (!newPath.startsWith(this.uploadDir)) {
return path.join(this.uploadDir, this.options.defaultInvalidName);
}
return newPath;
}
_setUpRename() {
const hasRename = typeof this.options.filename === 'function';
if (hasRename) {
this._getNewName = (part) => {
let ext = '';
let name = this.options.defaultInvalidName;
if (part.originalFilename) {
// can be null
({ ext, name } = path.parse(part.originalFilename));
if (this.options.keepExtensions !== true) {
ext = '';
}
}
return this.options.filename.call(this, name, ext, part, this);
};
} else {
this._getNewName = (part) => {
const name = toHexoId();
if (part && this.options.keepExtensions) {
const originalFilename =
typeof part === 'string' ? part : part.originalFilename;
return `${name}${this._getExtension(originalFilename)}`;
}
return name;
};
}
}
_setUpMaxFields() {
if (this.options.maxFields !== 0) {
let fieldsCount = 0;
this.on('field', () => {
fieldsCount += 1;
if (fieldsCount > this.options.maxFields) {
this._error(
new FormidableError(
`options.maxFields (${this.options.maxFields}) exceeded`,
errors.maxFieldsExceeded,
413,
),
);
}
});
}
}
_maybeEnd() {

@@ -480,3 +614,3 @@ // console.log('ended', this.ended);

IncomingForm.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
module.exports = IncomingForm;
export default IncomingForm;
export { DEFAULT_OPTIONS };

@@ -1,8 +0,6 @@

'use strict';
import PersistentFile from './PersistentFile.js';
import VolatileFile from './VolatileFile.js';
import Formidable, { DEFAULT_OPTIONS } from './Formidable.js';
const File = require('./File');
const Formidable = require('./Formidable');
const plugins = require('./plugins/index');
const parsers = require('./parsers/index');

@@ -12,23 +10,24 @@ // make it available without requiring the `new` keyword

const formidable = (...args) => new Formidable(...args);
const {enabledPlugins} = DEFAULT_OPTIONS;
module.exports = Object.assign(formidable, {
File,
export default formidable;
export {
PersistentFile as File,
PersistentFile,
VolatileFile,
Formidable,
// alias
Formidable as IncomingForm,
// as named
formidable,
// alias
IncomingForm: Formidable,
// parsers
...parsers,
parsers,
// misc
defaultOptions: Formidable.DEFAULT_OPTIONS,
enabledPlugins: Formidable.DEFAULT_OPTIONS.enabledPlugins,
DEFAULT_OPTIONS as defaultOptions,
enabledPlugins,
};
// plugins
plugins: {
...plugins,
},
});
export * from './parsers/index.js';
export * from './plugins/index.js';
export * as errors from './FormidableError.js';
/* eslint-disable no-underscore-dangle */
'use strict';
import { Transform } from 'stream';
const { Transform } = require('stream');
class DummyParser extends Transform {

@@ -21,2 +19,2 @@ constructor(incomingForm, options = {}) {

module.exports = DummyParser;
export default DummyParser;

@@ -1,10 +0,8 @@

'use strict';
import JSONParser from './JSON.js';
import DummyParser from './Dummy.js';
import MultipartParser from './Multipart.js';
import OctetStreamParser from './OctetStream.js';
import QueryStringParser from './Querystring.js';
const JSONParser = require('./JSON');
const DummyParser = require('./Dummy');
const MultipartParser = require('./Multipart');
const OctetStreamParser = require('./OctetStream');
const QueryStringParser = require('./Querystring');
Object.assign(exports, {
export {
JSONParser,

@@ -14,5 +12,5 @@ DummyParser,

OctetStreamParser,
OctetstreamParser: OctetStreamParser,
OctetStreamParser as OctetstreamParser,
QueryStringParser,
QuerystringParser: QueryStringParser,
});
QueryStringParser as QuerystringParser,
};
/* eslint-disable no-underscore-dangle */
'use strict';
import { Transform } from 'stream';
const { Transform } = require('stream');
class JSONParser extends Transform {

@@ -22,6 +20,3 @@ constructor(options = {}) {

const fields = JSON.parse(this.chunks.join(''));
Object.keys(fields).forEach((key) => {
const value = fields[key];
this.push({ key, value });
});
this.push(fields);
} catch (e) {

@@ -36,2 +31,2 @@ callback(e);

module.exports = JSONParser;
export default JSONParser;

@@ -6,6 +6,6 @@ /* eslint-disable no-fallthrough */

'use strict';
import { Transform } from 'stream';
import * as errors from '../FormidableError.js';
import FormidableError from '../FormidableError.js';
const { Transform } = require('stream');
let s = 0;

@@ -43,6 +43,6 @@ const STATE = {

exports.STATES = {};
export const STATES = {};
Object.keys(STATE).forEach((stateName) => {
exports.STATES[stateName] = STATE[stateName];
STATES[stateName] = STATE[stateName];
});

@@ -64,3 +64,3 @@

_final(done) {
_flush(done) {
if (

@@ -75,4 +75,6 @@ (this.state === STATE.HEADER_FIELD_START && this.index === 0) ||

done(
new Error(
new FormidableError(
`MultipartParser.end(): stream ended unexpectedly: ${this.explain()}`,
errors.malformedMultipart,
400,
),

@@ -115,3 +117,3 @@ );

const setMark = (name, idx) => {
this[`${name}Mark`] = idx || i;
this[`${name}Mark`] = typeof idx === 'number' ? idx : i;
};

@@ -131,6 +133,6 @@

this._handleCallback(name, buffer, this[markSymbol], buffer.length);
setMark(markSymbol, 0);
setMark(name, 0);
} else {
this._handleCallback(name, buffer, this[markSymbol], i);
clearMarkSymbol(markSymbol);
clearMarkSymbol(name);
}

@@ -348,2 +350,2 @@ };

module.exports = Object.assign(MultipartParser, { STATES: exports.STATES });
export default Object.assign(MultipartParser, { STATES });

@@ -1,5 +0,3 @@

'use strict';
import { PassThrough } from 'stream';
const { PassThrough } = require('stream');
class OctetStreamParser extends PassThrough {

@@ -12,2 +10,2 @@ constructor(options = {}) {

module.exports = OctetStreamParser;
export default OctetStreamParser;
/* eslint-disable no-underscore-dangle */
'use strict';
import { Transform } from 'stream';
const { Transform } = require('stream');
const querystring = require('querystring');
// This is a buffering parser, not quite as nice as the multipart one.

@@ -14,3 +11,2 @@ // If I find time I'll rewrite this to be fully streaming as well

this.globalOptions = { ...options };
this.maxKeys = this.globalOptions.maxFields;
this.buffer = '';

@@ -27,10 +23,7 @@ this.bufferLength = 0;

_flush(callback) {
const fields = querystring.parse(this.buffer, '&', '=', {
maxKeys: this.maxKeys,
});
// eslint-disable-next-line no-restricted-syntax, guard-for-in
for (const key in fields) {
const fields = new URLSearchParams(this.buffer);
for (const [key, value] of fields) {
this.push({
key,
value: fields[key],
value,
});

@@ -43,2 +36,2 @@ }

module.exports = QuerystringParser;
export default QuerystringParser;

@@ -1,13 +0,6 @@

'use strict';
import octetstream from './octetstream.js';
import querystring from './querystring.js';
import multipart from './multipart.js';
import json from './json.js';
const octetstream = require('./octetstream');
const querystring = require('./querystring');
const multipart = require('./multipart');
const json = require('./json');
Object.assign(exports, {
octetstream,
querystring,
multipart,
json,
});
export { octetstream, querystring, multipart, json };
/* eslint-disable no-underscore-dangle */
'use strict';
import JSONParser from '../parsers/JSON.js';
const JSONParser = require('../parsers/JSON');
export const jsonType = 'json';
// the `options` is also available through the `this.options` / `formidable.options`
module.exports = function plugin(formidable, options) {
export default function plugin(formidable, options) {
// the `this` context is always formidable, as the first argument of a plugin

@@ -24,8 +23,8 @@ // but this allows us to customize/test each plugin

function init(_self, _opts) {
this.type = 'json';
this.type = jsonType;
const parser = new JSONParser(this.options);
parser.on('data', ({ key, value }) => {
this.emit('field', key, value);
parser.on('data', (fields) => {
this.fields = fields;
});

@@ -32,0 +31,0 @@

/* eslint-disable no-underscore-dangle */
'use strict';
import { Stream } from 'stream';
import MultipartParser from '../parsers/Multipart.js';
import * as errors from '../FormidableError.js';
import FormidableError from '../FormidableError.js';
const { Stream } = require('stream');
const MultipartParser = require('../parsers/Multipart');
export const multipartType = 'multipart';
// the `options` is also available through the `options` / `formidable.options`
module.exports = function plugin(formidable, options) {
export default function plugin(formidable, options) {
// the `this` context is always formidable, as the first argument of a plugin

@@ -16,3 +17,6 @@ // but this allows us to customize/test each plugin

if (/multipart\/form-data/i.test(self.headers['content-type'])) {
// NOTE: we (currently) support both multipart/form-data and multipart/related
const multipart = /multipart/i.test(self.headers['content-type']);
if (multipart) {
const m = self.headers['content-type'].match(

@@ -23,9 +27,13 @@ /boundary=(?:"([^"]+)"|([^;]+))/i,

const initMultipart = createInitMultipart(m[1] || m[2]);
initMultipart.call(self, self, options);
initMultipart.call(self, self, options); // lgtm [js/superfluous-trailing-arguments]
} else {
const err = new Error('bad content-type header, no multipart boundary');
const err = new FormidableError(
'bad content-type header, no multipart boundary',
errors.missingMultipartBoundary,
400,
);
self._error(err);
}
}
};
}

@@ -37,3 +45,3 @@ // Note that it's a good practice (but it's up to you) to use the `this.options` instead

return function initMultipart() {
this.type = 'multipart';
this.type = multipartType;

@@ -54,6 +62,6 @@ const parser = new MultipartParser(this.options);

part.name = null;
part.filename = null;
part.mime = null;
part.originalFilename = null;
part.mimetype = null;
part.transferEncoding = 'binary';
part.transferEncoding = this.options.encoding;
part.transferBuffer = '';

@@ -81,5 +89,5 @@

part.filename = this._getFileName(headerValue);
part.originalFilename = this._getFileName(headerValue);
} else if (headerField === 'content-type') {
part.mime = headerValue;
part.mimetype = headerValue;
} else if (headerField === 'content-transfer-encoding') {

@@ -95,3 +103,4 @@ part.transferEncoding = headerValue.toLowerCase();

case '7bit':
case '8bit': {
case '8bit':
case 'utf-8': {
const dataPropagation = (ctx) => {

@@ -150,3 +159,9 @@ if (ctx.name === 'partData') {

default:
return this._error(new Error('unknown transfer-encoding'));
return this._error(
new FormidableError(
'unknown transfer-encoding',
errors.unknownTransferEncoding,
501,
),
);
}

@@ -153,0 +168,0 @@

/* eslint-disable no-underscore-dangle */
'use strict';
import OctetStreamParser from '../parsers/OctetStream.js';
const File = require('../File');
const OctetStreamParser = require('../parsers/OctetStream');
export const octetStreamType = 'octet-stream';
// the `options` is also available through the `options` / `formidable.options`
module.exports = function plugin(formidable, options) {
export default function plugin(formidable, options) {
// the `this` context is always formidable, as the first argument of a plugin

@@ -21,3 +19,3 @@ // but this allows us to customize/test each plugin

return self;
};
}

@@ -28,14 +26,20 @@ // Note that it's a good practice (but it's up to you) to use the `this.options` instead

function init(_self, _opts) {
this.type = 'octet-stream';
const filename = this.headers['x-file-name'];
const mime = this.headers['content-type'];
this.type = octetStreamType;
const originalFilename = this.headers['x-file-name'];
const mimetype = this.headers['content-type'];
const file = new File({
path: this._uploadPath(filename),
name: filename,
type: mime,
hash: this.options.hash,
const thisPart = {
originalFilename,
mimetype,
};
const newFilename = this._getNewName(thisPart);
const filepath = this._joinDirectoryName(newFilename);
const file = this._newFile({
newFilename,
filepath,
originalFilename,
mimetype,
});
this.emit('fileBegin', filename, file);
this.emit('fileBegin', originalFilename, file);
file.open();

@@ -42,0 +46,0 @@ this.openedFiles.push(file);

/* eslint-disable no-underscore-dangle */
'use strict';
const QuerystringParser = require('../parsers/Querystring');
import QuerystringParser from '../parsers/Querystring.js';
export const querystringType = 'urlencoded';
// the `options` is also available through the `this.options` / `formidable.options`
module.exports = function plugin(formidable, options) {
export default function plugin(formidable, options) {
// the `this` context is always formidable, as the first argument of a plugin

@@ -26,3 +26,3 @@ // but this allows us to customize/test each plugin

function init(_self, _opts) {
this.type = 'urlencoded';
this.type = querystringType;

@@ -29,0 +29,0 @@ const parser = new QuerystringParser(this.options);

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc