@poppinss/multiparty 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/index.js +65 -22
- package/package.json +2 -3
- package/HISTORY.md +0 -255
package/README.md
CHANGED
|
@@ -84,6 +84,9 @@ Creates a new form. Options:
|
|
|
84
84
|
* `maxFilesSize` - Only relevant when `autoFiles` is `true`. Limits the
|
|
85
85
|
total bytes accepted for all files combined. If this value is exceeded,
|
|
86
86
|
an `error` event is emitted. The default is `Infinity`.
|
|
87
|
+
* `maxHeaderSize` - Limits the size of header names and values for each part.
|
|
88
|
+
If this value is exceeded, an `error` event is emitted. The default size is
|
|
89
|
+
16KB.
|
|
87
90
|
* `autoFields` - Enables `field` events and disables `part` events for fields.
|
|
88
91
|
This is automatically set to `true` if you add a `field` listener.
|
|
89
92
|
* `autoFiles` - Enables `file` events and disables `part` events for files.
|
package/index.js
CHANGED
|
@@ -39,11 +39,11 @@ var A = 97;
|
|
|
39
39
|
var Z = 122;
|
|
40
40
|
|
|
41
41
|
var CONTENT_TYPE_RE = /^multipart\/(?:form-data|related)(?:;|$)/i;
|
|
42
|
-
var
|
|
42
|
+
var CONTENT_TYPE_BOUNDARY_RE = /;[ \t]*boundary=(?:"([^"]*)"|([^;]+))/gi;
|
|
43
43
|
var FILE_EXT_RE = /(\.[_\-a-zA-Z0-9]{0,16})[\S\s]*/;
|
|
44
|
-
var FILENAME_PARAM_RE =
|
|
44
|
+
var FILENAME_PARAM_RE = /;[ \t]*filename=(?:"((?:[^"]|"(?![ \t]*(?:;|$)))*)"|([^;]+))/i
|
|
45
45
|
var LAST_BOUNDARY_SUFFIX_LEN = 4; // --\r\n
|
|
46
|
-
var NAME_PARAM_RE =
|
|
46
|
+
var NAME_PARAM_RE = /;[ \t]*name=(?:"([^"]*)"|([^;]+))/i
|
|
47
47
|
|
|
48
48
|
exports.Form = Form;
|
|
49
49
|
|
|
@@ -75,6 +75,7 @@ function Form(options) {
|
|
|
75
75
|
self.maxFields = opts.maxFields || 1000
|
|
76
76
|
self.maxFieldsSize = opts.maxFieldsSize || 2 * 1024 * 1024
|
|
77
77
|
self.maxFilesSize = opts.maxFilesSize || Infinity
|
|
78
|
+
self.maxHeaderSize = opts.maxHeaderSize || 16 * 1024
|
|
78
79
|
self.uploadDir = opts.uploadDir || os.tmpdir()
|
|
79
80
|
self.encoding = opts.encoding || 'utf8'
|
|
80
81
|
|
|
@@ -140,12 +141,24 @@ Form.prototype.parse = function(req, cb) {
|
|
|
140
141
|
});
|
|
141
142
|
});
|
|
142
143
|
self.on('field', function(name, value) {
|
|
143
|
-
var fieldsArray = fields
|
|
144
|
-
|
|
144
|
+
var fieldsArray = Object.prototype.hasOwnProperty.call(fields, name)
|
|
145
|
+
? fields[name]
|
|
146
|
+
: undefined;
|
|
147
|
+
if (Array.isArray(fieldsArray)) {
|
|
148
|
+
fieldsArray.push(value);
|
|
149
|
+
} else {
|
|
150
|
+
fields[name] = [value]
|
|
151
|
+
}
|
|
145
152
|
});
|
|
146
153
|
self.on('file', function(name, file) {
|
|
147
|
-
var filesArray = files
|
|
148
|
-
|
|
154
|
+
var filesArray = Object.prototype.hasOwnProperty.call(files, name)
|
|
155
|
+
? files[name]
|
|
156
|
+
: undefined;
|
|
157
|
+
if (Array.isArray(filesArray)) {
|
|
158
|
+
filesArray.push(file);
|
|
159
|
+
} else {
|
|
160
|
+
files[name] = [file]
|
|
161
|
+
}
|
|
149
162
|
});
|
|
150
163
|
self.on('close', function() {
|
|
151
164
|
end(function() {
|
|
@@ -185,10 +198,9 @@ Form.prototype.parse = function(req, cb) {
|
|
|
185
198
|
}
|
|
186
199
|
|
|
187
200
|
var boundary;
|
|
188
|
-
|
|
189
|
-
while ((m =
|
|
190
|
-
|
|
191
|
-
boundary = m[2] || m[3];
|
|
201
|
+
CONTENT_TYPE_BOUNDARY_RE.lastIndex = m.index + m[0].length - 1;
|
|
202
|
+
while ((m = CONTENT_TYPE_BOUNDARY_RE.exec(contentType))) {
|
|
203
|
+
boundary = m[1] || m[2];
|
|
192
204
|
break;
|
|
193
205
|
}
|
|
194
206
|
|
|
@@ -305,6 +317,7 @@ Form.prototype._write = function(buffer, encoding, cb) {
|
|
|
305
317
|
return;
|
|
306
318
|
}
|
|
307
319
|
self.onParseHeaderField(buffer.slice(self.headerFieldMark, i));
|
|
320
|
+
if (self.error) return;
|
|
308
321
|
self.headerFieldMark = null;
|
|
309
322
|
state = HEADER_VALUE_START;
|
|
310
323
|
break;
|
|
@@ -325,6 +338,7 @@ Form.prototype._write = function(buffer, encoding, cb) {
|
|
|
325
338
|
case HEADER_VALUE:
|
|
326
339
|
if (c === CR) {
|
|
327
340
|
self.onParseHeaderValue(buffer.slice(self.headerValueMark, i));
|
|
341
|
+
if (self.error) return;
|
|
328
342
|
self.headerValueMark = null;
|
|
329
343
|
self.onParseHeaderEnd();
|
|
330
344
|
state = HEADER_VALUE_ALMOST_DONE;
|
|
@@ -460,10 +474,20 @@ Form.prototype.onParsePartBegin = function() {
|
|
|
460
474
|
}
|
|
461
475
|
|
|
462
476
|
Form.prototype.onParseHeaderField = function(b) {
|
|
477
|
+
this.partHeadersSize += b.length;
|
|
478
|
+
if (this.partHeadersSize > this.maxHeaderSize) {
|
|
479
|
+
this.handleError(createError(413, 'maxHeaderSize ' + this.maxHeaderSize + ' exceeded'));
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
463
482
|
this.headerField += this.headerFieldDecoder.write(b);
|
|
464
483
|
}
|
|
465
484
|
|
|
466
485
|
Form.prototype.onParseHeaderValue = function(b) {
|
|
486
|
+
this.partHeadersSize += b.length;
|
|
487
|
+
if (this.partHeadersSize > this.maxHeaderSize) {
|
|
488
|
+
this.handleError(createError(413, 'maxHeaderSize ' + this.maxHeaderSize + ' exceeded'));
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
467
491
|
this.headerValue += this.headerValueDecoder.write(b);
|
|
468
492
|
}
|
|
469
493
|
|
|
@@ -489,7 +513,13 @@ Form.prototype.onParseHeaderEnd = function() {
|
|
|
489
513
|
|
|
490
514
|
Form.prototype.onParsePartData = function(b) {
|
|
491
515
|
if (this.partTransferEncoding === 'base64') {
|
|
492
|
-
|
|
516
|
+
// Count base64 characters, not whitespace, and retain incomplete groups.
|
|
517
|
+
var data = this.partRemainder + b.toString('binary').replace(/[\t\n\v\f\r ]/g, '');
|
|
518
|
+
var length = data.length - (data.length % 4);
|
|
519
|
+
this.partRemainder = data.slice(length);
|
|
520
|
+
if (length > 0) {
|
|
521
|
+
this.backpressure = ! this.destStream.write(data.slice(0, length), this.partTransferEncoding);
|
|
522
|
+
}
|
|
493
523
|
} else {
|
|
494
524
|
this.backpressure = ! this.destStream.write(b);
|
|
495
525
|
}
|
|
@@ -499,8 +529,10 @@ Form.prototype.onParsePartEnd = function() {
|
|
|
499
529
|
if (this.destStream) {
|
|
500
530
|
flushWriteCbs(this);
|
|
501
531
|
var s = this.destStream;
|
|
532
|
+
var remainder = this.partRemainder;
|
|
533
|
+
var encoding = this.partTransferEncoding;
|
|
502
534
|
process.nextTick(function() {
|
|
503
|
-
s.end();
|
|
535
|
+
s.end(remainder, encoding);
|
|
504
536
|
});
|
|
505
537
|
}
|
|
506
538
|
clearPartVars(this);
|
|
@@ -755,15 +787,16 @@ function handleField(self, fieldStream) {
|
|
|
755
787
|
self.handleError(err);
|
|
756
788
|
});
|
|
757
789
|
fieldStream.on('readable', function() {
|
|
758
|
-
var buffer
|
|
759
|
-
|
|
790
|
+
var buffer;
|
|
791
|
+
while ((buffer = fieldStream.read()) !== null) {
|
|
792
|
+
self.totalFieldSize += buffer.length;
|
|
793
|
+
if (self.totalFieldSize > self.maxFieldsSize) {
|
|
794
|
+
self.handleError(createError(413, 'maxFieldsSize ' + self.maxFieldsSize + ' exceeded'));
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
760
797
|
|
|
761
|
-
|
|
762
|
-
if (self.totalFieldSize > self.maxFieldsSize) {
|
|
763
|
-
self.handleError(createError(413, 'maxFieldsSize ' + self.maxFieldsSize + ' exceeded'));
|
|
764
|
-
return;
|
|
798
|
+
value += decoder.write(buffer);
|
|
765
799
|
}
|
|
766
|
-
value += decoder.write(buffer);
|
|
767
800
|
});
|
|
768
801
|
|
|
769
802
|
fieldStream.on('end', function() {
|
|
@@ -796,9 +829,11 @@ function hasListeners (emitter, type) {
|
|
|
796
829
|
|
|
797
830
|
function clearPartVars(self) {
|
|
798
831
|
self.partHeaders = {};
|
|
832
|
+
self.partHeadersSize = 0;
|
|
799
833
|
self.partName = null;
|
|
800
834
|
self.partFilename = null;
|
|
801
835
|
self.partTransferEncoding = 'binary';
|
|
836
|
+
self.partRemainder = '';
|
|
802
837
|
self.destStream = null;
|
|
803
838
|
|
|
804
839
|
self.headerFieldDecoder = new StringDecoder(self.encoding);
|
|
@@ -839,9 +874,9 @@ function uploadPath(baseDir, filename) {
|
|
|
839
874
|
function parseFilename(headerValue) {
|
|
840
875
|
var m = FILENAME_PARAM_RE.exec(headerValue)
|
|
841
876
|
if (!m) {
|
|
842
|
-
m = headerValue.match(
|
|
877
|
+
m = headerValue.match(/;[ \t]*filename\*=utf-8''([^;]*)/i)
|
|
843
878
|
if (m) {
|
|
844
|
-
m[1] =
|
|
879
|
+
m[1] = tryDecode(m[1]);
|
|
845
880
|
} else {
|
|
846
881
|
return;
|
|
847
882
|
}
|
|
@@ -858,3 +893,11 @@ function parseFilename(headerValue) {
|
|
|
858
893
|
function lower(c) {
|
|
859
894
|
return c | 0x20;
|
|
860
895
|
}
|
|
896
|
+
|
|
897
|
+
function tryDecode(value) {
|
|
898
|
+
try {
|
|
899
|
+
return decodeURIComponent(value);
|
|
900
|
+
} catch (e) {
|
|
901
|
+
return undefined;
|
|
902
|
+
}
|
|
903
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poppinss/multiparty",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "multipart/form-data parser which supports streaming",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"contributors": [
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"eslint": "^8.9.0",
|
|
23
23
|
"eslint-plugin-markdown": "2.2.1",
|
|
24
24
|
"mkdirp": "^1.0.4",
|
|
25
|
-
"mocha": "^
|
|
25
|
+
"mocha": "^10.8.2",
|
|
26
26
|
"nyc": "15.1.0",
|
|
27
27
|
"pend": "1.2.0",
|
|
28
28
|
"require-all": "3.0.0",
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"superagent": "^7.1.1"
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
|
-
"HISTORY.md",
|
|
34
33
|
"LICENSE",
|
|
35
34
|
"README.md",
|
|
36
35
|
"index.js"
|
package/HISTORY.md
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
unreleased
|
|
2
|
-
==========
|
|
3
|
-
|
|
4
|
-
* Fix form parsing when no `part` event listener added
|
|
5
|
-
|
|
6
|
-
4.2.3 / 2022-01-20
|
|
7
|
-
==================
|
|
8
|
-
|
|
9
|
-
* Fix handling of unquoted values in `Content-Disposition`
|
|
10
|
-
* deps: http-errors@~1.8.1
|
|
11
|
-
- deps: toidentifier@1.0.1
|
|
12
|
-
|
|
13
|
-
4.2.2 / 2020-07-27
|
|
14
|
-
==================
|
|
15
|
-
|
|
16
|
-
* Fix empty files on Node.js 14.x
|
|
17
|
-
* Fix form emitting aborted error after close
|
|
18
|
-
* Replace `fd-slicer` module with internal transform stream
|
|
19
|
-
* deps: http-errors@~1.8.0
|
|
20
|
-
- Fix error creating objects in some environments
|
|
21
|
-
- deps: inherits@2.0.4
|
|
22
|
-
- deps: setprototypeof@1.2.0
|
|
23
|
-
* deps: safe-buffer@5.2.1
|
|
24
|
-
|
|
25
|
-
4.2.1 / 2018-08-12
|
|
26
|
-
==================
|
|
27
|
-
|
|
28
|
-
* Use `uid-safe` module to for temp file names
|
|
29
|
-
* deps: fd-slicer@1.1.0
|
|
30
|
-
* deps: http-errors@~1.7.0
|
|
31
|
-
|
|
32
|
-
4.2.0 / 2018-07-30
|
|
33
|
-
==================
|
|
34
|
-
|
|
35
|
-
* Use `http-errors` for raised errors
|
|
36
|
-
* Use `random-bytes` module for polyfill
|
|
37
|
-
* perf: remove parameter reassignment
|
|
38
|
-
|
|
39
|
-
4.1.4 / 2018-05-11
|
|
40
|
-
==================
|
|
41
|
-
|
|
42
|
-
* Fix file extension filtering stopping on certain whitespace characters
|
|
43
|
-
* Use `safe-buffer` for improved API safety
|
|
44
|
-
* perf: enable strict mode
|
|
45
|
-
|
|
46
|
-
4.1.3 / 2017-01-22
|
|
47
|
-
==================
|
|
48
|
-
|
|
49
|
-
* Use `os.tmpdir()` instead of `os.tmpDir()`
|
|
50
|
-
* deps: fd-slicer@1.0.1
|
|
51
|
-
|
|
52
|
-
4.1.2 / 2015-05-09
|
|
53
|
-
==================
|
|
54
|
-
|
|
55
|
-
* Do not emit error on part prior to emitting part
|
|
56
|
-
* Fix filename with quotes truncating from certain clients
|
|
57
|
-
|
|
58
|
-
4.1.1 / 2015-01-18
|
|
59
|
-
==================
|
|
60
|
-
|
|
61
|
-
* Do not clobber existing temporary files
|
|
62
|
-
|
|
63
|
-
4.1.0 / 2014-12-04
|
|
64
|
-
==================
|
|
65
|
-
|
|
66
|
-
* Add `statusCode` field to HTTP-related errors
|
|
67
|
-
* deps: fd-slicer@1.0.0
|
|
68
|
-
|
|
69
|
-
4.0.0 / 2014-10-14
|
|
70
|
-
==================
|
|
71
|
-
|
|
72
|
-
* `part` events for fields no longer fire if `autoFields` is on
|
|
73
|
-
* `part` events for files no longer fire if `autoFiles` is on
|
|
74
|
-
* `field`, `file`, and `part` events are guaranteed to emit in the
|
|
75
|
-
correct order - the order that the user places the parts in the
|
|
76
|
-
request. Each `part` `end` event is guaranteed to emit before the
|
|
77
|
-
next `part` event is emitted.
|
|
78
|
-
* Drop Node.js 0.8.x support
|
|
79
|
-
* Improve random temp file names
|
|
80
|
-
- Now using 18 bytes of randomness instead of 8.
|
|
81
|
-
* More robust `maxFilesSize` implementation
|
|
82
|
-
- Before it was possible for race conditions to cause more than
|
|
83
|
-
`maxFilesSize` bytes to get written to disk. That is now fixed.
|
|
84
|
-
* Now `part` objects emit `error` events
|
|
85
|
-
- This makes streaming work better since the part stream will emit
|
|
86
|
-
an error when it is no longer streaming.
|
|
87
|
-
* Remove support for generating the hash digest of a part
|
|
88
|
-
- If you want this, do it in your own code.
|
|
89
|
-
* Remove undocumented `ws` property from `file` objects
|
|
90
|
-
* Require the close boundary
|
|
91
|
-
- This makes multiparty more RFC-compliant and makes some invalid
|
|
92
|
-
requests which used to work, now emit an error instead.
|
|
93
|
-
|
|
94
|
-
3.3.2 / 2014-08-07
|
|
95
|
-
==================
|
|
96
|
-
|
|
97
|
-
* Do not invoke callback after close
|
|
98
|
-
* Share callback ending logic between error and close
|
|
99
|
-
|
|
100
|
-
3.3.1 / 2014-07-22
|
|
101
|
-
==================
|
|
102
|
-
|
|
103
|
-
* Remove problematic test fixtures
|
|
104
|
-
|
|
105
|
-
3.3.0 / 2014-07-03
|
|
106
|
-
==================
|
|
107
|
-
|
|
108
|
-
* Always emit close after all parts ended
|
|
109
|
-
|
|
110
|
-
3.2.10 / 2014-07-03
|
|
111
|
-
===================
|
|
112
|
-
|
|
113
|
-
* Fix callback hang in node.js 0.8 on errors
|
|
114
|
-
* Remove execute bit from files
|
|
115
|
-
|
|
116
|
-
3.2.9 / 2014-06-16
|
|
117
|
-
==================
|
|
118
|
-
|
|
119
|
-
* Fix attaching error listeners directly after form.parse
|
|
120
|
-
* Fix to not synchronously invoke callback to form.parse on error
|
|
121
|
-
|
|
122
|
-
3.2.8 / 2014-06-01
|
|
123
|
-
==================
|
|
124
|
-
|
|
125
|
-
* Fix developer accidentally corrupting data
|
|
126
|
-
* Fix handling epilogue in a separate chunk
|
|
127
|
-
* Fix initial check errors to use supplied callback
|
|
128
|
-
|
|
129
|
-
3.2.7 / 2014-05-26
|
|
130
|
-
==================
|
|
131
|
-
|
|
132
|
-
* Fix errors hanging responses in callback-style
|
|
133
|
-
|
|
134
|
-
3.2.6 / 2014-05-13
|
|
135
|
-
==================
|
|
136
|
-
|
|
137
|
-
* Fix `maxFields` to error on field after max
|
|
138
|
-
|
|
139
|
-
3.2.5 / 2014-05-11
|
|
140
|
-
==================
|
|
141
|
-
|
|
142
|
-
* Support boundary containing equal sign
|
|
143
|
-
|
|
144
|
-
3.2.4 / 2014-03-26
|
|
145
|
-
==================
|
|
146
|
-
|
|
147
|
-
* Keep `part.byteCount` undefined in chunked encoding
|
|
148
|
-
* Fix temp files not always cleaned up
|
|
149
|
-
|
|
150
|
-
3.2.3 / 2014-02-20
|
|
151
|
-
==================
|
|
152
|
-
|
|
153
|
-
* Improve parsing boundary attribute from `Content-Type`
|
|
154
|
-
|
|
155
|
-
3.2.2 / 2014-01-29
|
|
156
|
-
==================
|
|
157
|
-
|
|
158
|
-
* Fix error on empty payloads
|
|
159
|
-
|
|
160
|
-
3.2.1 / 2014-01-27
|
|
161
|
-
==================
|
|
162
|
-
|
|
163
|
-
* Fix `maxFilesSize` overcalculation bug
|
|
164
|
-
|
|
165
|
-
3.2.0 / 2014-01-17
|
|
166
|
-
==================
|
|
167
|
-
|
|
168
|
-
* Add `maxFilesSize` for `autoFiles`
|
|
169
|
-
|
|
170
|
-
3.1.2 / 2014-01-13
|
|
171
|
-
==================
|
|
172
|
-
|
|
173
|
-
* Fix incorrectly using `autoFields` value for `autoFiles`
|
|
174
|
-
|
|
175
|
-
3.1.1 / 2013-12-13
|
|
176
|
-
==================
|
|
177
|
-
|
|
178
|
-
* Fix not emitting `close` after all part `end` events
|
|
179
|
-
|
|
180
|
-
3.1.0 / 2013-11-10
|
|
181
|
-
==================
|
|
182
|
-
|
|
183
|
-
* Support UTF-8 filename in `Content-Disposition`
|
|
184
|
-
|
|
185
|
-
3.0.0 / 2013-10-25
|
|
186
|
-
==================
|
|
187
|
-
|
|
188
|
-
* `form.parse` callback API changed in a compatibility-breaking manner
|
|
189
|
-
|
|
190
|
-
2.2.0 / 2013-10-15
|
|
191
|
-
==================
|
|
192
|
-
|
|
193
|
-
* Add callback API to support multiple files with same field name
|
|
194
|
-
* Fix assertion crash when max field count is exceeded
|
|
195
|
-
* Fix assertion crash when client aborts an invalid request
|
|
196
|
-
* Fix assertion crash when `EMFILE` occurrs
|
|
197
|
-
* Switch from assertions to only `error` events
|
|
198
|
-
* Unpipe the request when an error occurs to save resources
|
|
199
|
-
* Update readable-stream to ~1.1.9
|
|
200
|
-
|
|
201
|
-
2.1.9 / 2013-10-06
|
|
202
|
-
==================
|
|
203
|
-
|
|
204
|
-
* relax `Content-Type` detection regex
|
|
205
|
-
|
|
206
|
-
2.1.8 / 2013-08-26
|
|
207
|
-
==================
|
|
208
|
-
|
|
209
|
-
* Replace deprecated `Buffer.write()`
|
|
210
|
-
|
|
211
|
-
2.1.7 / 2013-05-23
|
|
212
|
-
==================
|
|
213
|
-
|
|
214
|
-
* Add repository field to package.json
|
|
215
|
-
|
|
216
|
-
2.1.6 / 2013-04-30
|
|
217
|
-
==================
|
|
218
|
-
|
|
219
|
-
* Expose `hash` as an option to `Form`
|
|
220
|
-
|
|
221
|
-
2.1.5 / 2013-04-10
|
|
222
|
-
==================
|
|
223
|
-
|
|
224
|
-
* Fix possible `close` event before all temp files are done
|
|
225
|
-
|
|
226
|
-
2.1.4 / 2013-04-09
|
|
227
|
-
==================
|
|
228
|
-
|
|
229
|
-
* Fix crash for invalid requests
|
|
230
|
-
|
|
231
|
-
2.1.3 / 2013-04-09
|
|
232
|
-
==================
|
|
233
|
-
|
|
234
|
-
* Add `file.size`
|
|
235
|
-
|
|
236
|
-
2.1.2 / 2013-04-08
|
|
237
|
-
==================
|
|
238
|
-
|
|
239
|
-
* Add proper backpressure support
|
|
240
|
-
|
|
241
|
-
2.1.1 / 2013-04-05
|
|
242
|
-
==================
|
|
243
|
-
|
|
244
|
-
* Add `part.byteCount` and `part.byteOffset`
|
|
245
|
-
* Fix uploads larger than 2KB
|
|
246
|
-
|
|
247
|
-
2.1.0 / 2013-04-04
|
|
248
|
-
==================
|
|
249
|
-
|
|
250
|
-
* Complete rewrite. See README for changes and new API.
|
|
251
|
-
|
|
252
|
-
2.0.0 / 2013-04-02
|
|
253
|
-
==================
|
|
254
|
-
|
|
255
|
-
* Fork and rewrite from `formidable`
|