then-busboy
Advanced tools
Comparing version 2.3.4 to 3.0.0
@@ -24,2 +24,4 @@ "use strict"; | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
class File { | ||
@@ -36,39 +38,31 @@ /** | ||
constructor(options) { | ||
Object.defineProperty(this, "read", { | ||
configurable: true, | ||
enumerable: true, | ||
writable: true, | ||
value: () => new Promise((resolve, reject) => { | ||
const data = []; | ||
_defineProperty(this, "read", () => new Promise((resolve, reject) => { | ||
const data = []; | ||
const onReadable = () => { | ||
const ch = this.contents.read(); | ||
const onReadable = () => { | ||
const ch = this.contents.read(); | ||
if (ch != null) { | ||
data.push(ch); | ||
} | ||
}; | ||
if (ch != null) { | ||
data.push(ch); | ||
} | ||
}; | ||
const onEnd = () => resolve(Buffer.concat(data)); | ||
const onEnd = () => resolve(Buffer.concat(data)); | ||
this.contents.on("error", reject).on("readable", onReadable).on("end", onEnd); | ||
}) | ||
}); | ||
Object.defineProperty(this, "write", { | ||
configurable: true, | ||
enumerable: true, | ||
writable: true, | ||
value: path => new Promise((resolve, reject) => { | ||
if (!path || !(0, _isString.default)(path)) { | ||
path = this.path; | ||
} // Prevent writing file to its source | ||
this.contents.on("error", reject).on("readable", onReadable).on("end", onEnd); | ||
})); | ||
_defineProperty(this, "write", path => new Promise((resolve, reject) => { | ||
if (!path || !(0, _isString.default)(path)) { | ||
path = this.path; | ||
} // Prevent writing file to its source | ||
if (this.contents.path === path) { | ||
return resolve(); | ||
} | ||
this.contents.on("error", reject).on("end", resolve).pipe((0, _fs.createWriteStream)(path)); | ||
}) | ||
}); | ||
if (this.contents.path === path) { | ||
return resolve(); | ||
} | ||
this.contents.on("error", reject).on("end", resolve).pipe((0, _fs.createWriteStream)(path)); | ||
})); | ||
(0, _invariant.default)(!(0, _isPlainObject.default)(options), TypeError, "File options should be a plain object. Received", (0, _getType.default)(options)); | ||
@@ -75,0 +69,0 @@ const { |
@@ -97,3 +97,3 @@ "use strict"; | ||
busboy.on("error", reject).on("finish", onFinish); | ||
busboy.once("error", reject).once("finish", onFinish); | ||
request.pipe(busboy); | ||
@@ -100,0 +100,0 @@ }); |
@@ -18,3 +18,21 @@ "use strict"; | ||
const format = /^([^[\]\n]+)(\[[^[\]]+\])*$/; | ||
// Matched square braces notation paths | ||
const BRACKET_EXPR = /^([^0-9[\]\n\r\t]+|\[[0-9]+\])(\[[^[\]]+\])*$/; // Matches dot notation paths | ||
const DOT_EXPR = /^([a-z0-9_$]*)(?:\.([a-z0-9_$]*))*?$/i; | ||
const fromDotNotation = string => string.split(".").map(key => (0, _isNaN.default)(key) ? key : Number(key)); | ||
function fromSquareBracesNotation(string) { | ||
const res = []; | ||
for (const element of string.split("[")) { | ||
if (element) { | ||
const key = element.endsWith("]") ? element.slice(0, -1) : element; | ||
res.push((0, _isNaN.default)(key) ? key : Number(key)); | ||
} | ||
} | ||
return res; | ||
} | ||
/** | ||
@@ -36,17 +54,24 @@ * Get a fild path | ||
* | ||
* getFieldPath("42") // -> [42] | ||
* getFieldPath("[42]") // -> [42] | ||
* | ||
* getFieldPath("root.nested") // -> ["root", "nested"] | ||
* | ||
* getFieldPath("someCollection.0.name") // -> ["someCollection", 0, "name"] | ||
*/ | ||
function getFieldPath(fieldname) { | ||
const res = []; | ||
(0, _invariant.default)(!(0, _isString.default)(fieldname), TypeError, "Field name should be a string. Received %s", (0, _getType.default)(fieldname)); | ||
(0, _invariant.default)(!fieldname, "Field name cannot be empty."); | ||
(0, _invariant.default)(!format.test(fieldname), "Unexpected name format of the field: %s", fieldname); | ||
for (const element of fieldname.split("[")) { | ||
const key = element.endsWith("]") ? element.slice(0, -1) : element; | ||
res.push((0, _isNaN.default)(key) ? key : Number(key)); | ||
switch (true) { | ||
case DOT_EXPR.test(fieldname): | ||
return fromDotNotation(fieldname); | ||
case BRACKET_EXPR.test(fieldname): | ||
return fromSquareBracesNotation(fieldname); | ||
default: | ||
return (0, _invariant.default)(true, "Unexpected field name format: %s", fieldname); | ||
} | ||
return res; | ||
} | ||
@@ -53,0 +78,0 @@ |
{ | ||
"name": "then-busboy", | ||
"description": "Promise-based wrapper around Busboy. Process multipart/form-data content and returns it as a single object.", | ||
"version": "2.3.4", | ||
"version": "3.0.0", | ||
"author": "Nick K. <nick.kruchinin@gmail.com>", | ||
@@ -57,26 +57,26 @@ "license": "MIT", | ||
"lodash.merge": "4.6.1", | ||
"nanoid": "1.0.2", | ||
"nanoid": "1.2.1", | ||
"object-deep-from-entries": "0.3.0" | ||
}, | ||
"devDependencies": { | ||
"@babel/cli": "7.0.0-beta.42", | ||
"@babel/core": "7.0.0-beta.42", | ||
"@babel/plugin-proposal-class-properties": "7.0.0-beta.42", | ||
"@babel/plugin-proposal-export-default-from": "7.0.0-beta.42", | ||
"@babel/plugin-proposal-object-rest-spread": "7.0.0-beta.42", | ||
"@babel/plugin-transform-modules-commonjs": "7.0.0-beta.42", | ||
"@babel/cli": "7.0.0-rc.1", | ||
"@babel/core": "7.0.0-rc.1", | ||
"@babel/plugin-proposal-class-properties": "7.0.0-rc.1", | ||
"@babel/plugin-proposal-export-default-from": "7.0.0-rc.1", | ||
"@babel/plugin-proposal-object-rest-spread": "7.0.0-rc.1", | ||
"@babel/plugin-transform-modules-commonjs": "7.0.0-rc.1", | ||
"@octetstream/eslint-config": "2.1.0", | ||
"ava": "0.25.0", | ||
"codecov": "3.0.0", | ||
"eslint": "4.19.1", | ||
"eslint-plugin-ava": "4.5.1", | ||
"codecov": "3.0.4", | ||
"eslint": "5.4.0", | ||
"eslint-plugin-ava": "5.1.0", | ||
"husky": "0.14.3", | ||
"lint-staged": "7.0.2", | ||
"nyc": "11.6.0", | ||
"lint-staged": "7.2.2", | ||
"nyc": "12.0.2", | ||
"promise-fs": "1.3.0", | ||
"proxyquire": "2.0.1", | ||
"rimraf": "2.6.2", | ||
"sinon": "4.5.0", | ||
"supertest": "3.0.0" | ||
"sinon": "6.1.5", | ||
"supertest": "3.1.0" | ||
} | ||
} |
121
README.md
@@ -10,5 +10,2 @@ # then-busboy | ||
Note: The current documentation is for 2.x version of then-busboy. | ||
If you're looking for a previous version, check out the [1.x branch](https://github.com/octet-stream/then-busboy/tree/1.x). | ||
## Installation | ||
@@ -30,2 +27,4 @@ | ||
**Public** | ||
### `busboy(request[, options]) -> {Promise<object>}` | ||
@@ -38,2 +37,10 @@ | ||
### `isFile(value) -> {boolean}` | ||
Check if given value is a File instance. | ||
- **{any}** value – a value to verify | ||
**Private** | ||
### `constructor File(options)` | ||
@@ -96,21 +103,54 @@ | ||
### `isFile(value) -> {boolean}` | ||
## Fields format | ||
Check if given value is a File instance. | ||
then-busboy can restore an object structure from form-data field names | ||
if you will follow the naming formats with dots or square brackets: | ||
- **{any}** value – a value to verify | ||
### Dot notation | ||
## Fields format | ||
This notation looks similarly to JS object properties accessiong syntax: | ||
then-busboy can restore an object structure from form-data field names | ||
if you will follow the special naming format with bracket notation: | ||
``` | ||
# Flat objects looks the same in both notations | ||
# Note that the following notation examples is just a pseudo code | ||
name = "John Doe" | ||
age = 25 | ||
``` | ||
then-busboy will return the this object for an example from above: | ||
```json5 | ||
{ | ||
name: "John Doe", | ||
// By default, non-string values will be converted to their initial type. | ||
// So, "25" -> 25, "null" -> null, "false" -> false etc. | ||
age: 25 | ||
} | ||
``` | ||
# Note that the following example is just a pseudo code | ||
For deep objects or collections, use dot or brackets as a separator. | ||
**But don't mix them.** | ||
``` | ||
rootField.nestedField = "Some text here" | ||
``` | ||
```json5 | ||
{ | ||
rootField: { | ||
nestedField: "Some text here" | ||
} | ||
} | ||
``` | ||
### Bracket notation | ||
``` | ||
rootField[nestedField] = "I beat Twilight Sparkle and all I got was this lousy t-shirt" | ||
``` | ||
then-busboy will return the this object for an example from above: | ||
Becomes | ||
```js | ||
```json5 | ||
{ | ||
@@ -134,3 +174,3 @@ rootField: { | ||
```js | ||
```json5 | ||
{ | ||
@@ -142,4 +182,4 @@ message: { | ||
{ | ||
file: File, // this field will be represended as a File instance | ||
description: "Here is a description of the file" | ||
"file": File, // this field will be represended as a File instance | ||
"description": "Here is a description of the file" | ||
} | ||
@@ -151,4 +191,53 @@ ] | ||
**Note that there is no an implementation for array as *root field* for now!** | ||
Collections allowed too: | ||
``` | ||
[0][firstName] = "John" | ||
[0][lastName] = "Doe" | ||
[0][dob][day] = "1" | ||
[0][dob][month] = "Jan." | ||
[0][dob][year] = "1989" | ||
[0][skills][0] = "Node.js" | ||
[0][skills][1] = "CoffeeScript" | ||
[0][skills][2] = "JavaScript" | ||
[0][skills][3] = "Babel" | ||
[1][firstName] = "Max" | ||
[1][lastName] = "Doe" | ||
[1][dob][day] = "12" | ||
[1][dob][month] = "Mar." | ||
[1][dob][year] = "1992" | ||
[1][skills][0] = "Python" | ||
[1][skills][1] = "Flask" | ||
[1][skills][2] = "JavaScript" | ||
[1][skills][3] = "Babel" | ||
[1][skills][4] = "React" | ||
[1][skills][5] = "Redux" | ||
``` | ||
Then you will receive: | ||
```json5 | ||
[ | ||
{ | ||
firstName: "John", | ||
lastName: "Doe", | ||
dob: { | ||
day: 1, | ||
month: "Jan.", | ||
year: 1989 | ||
}, | ||
skills: ["Node.js", "CoffeeScript", "JavaScript", "Babel"] | ||
}, { | ||
firstName: "Max", | ||
lastName: "Doe", | ||
dob: { | ||
day: 12, | ||
month: "Mar.", | ||
year: 1992 | ||
}, | ||
skills: ["Python", "Flask", "JavaScript", "Babel", "React", "Redux"] | ||
} | ||
] | ||
``` | ||
## Usage | ||
@@ -155,0 +244,0 @@ |
27663
514
316
+ Addednanoid@1.2.1(transitive)
- Removednanoid@1.0.2(transitive)
Updatednanoid@1.2.1