superagent 3.8.2 → 3.8.3

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/History.md CHANGED
@@ -1,3 +1,16 @@
1
+
2
+ # 3.8.3 (2018-04-29)
3
+
4
+ * Add flags for 201 & 422 responses (Nikhil Fadnis)
5
+ * Emit progress event while uploading Node `Buffer` via send method (Sergey Akhalkov)
6
+ * Fixed setting correct cookies for redirects (Damien Clark)
7
+ * Replace .catch with ['catch'] for IE9 Support (Miguel Stevens)
8
+
9
+ # 3.8.2 (2017-12-09)
10
+
11
+ * Fixed handling of exceptions thrown from callbacks
12
+ * Stricter matching of `+json` MIME types.
13
+
1
14
  # 3.8.1 (2017-11-08)
2
15
 
3
16
  * Clear authorization header on cross-domain redirect
package/Readme.md CHANGED
@@ -70,6 +70,7 @@ Existing plugins:
70
70
  * [superagent-httpbackend](https://www.npmjs.com/package/superagent-httpbackend) - stub out requests using AngularJS' $httpBackend syntax
71
71
  * [superagent-throttle](https://github.com/leviwheatcroft/superagent-throttle) - queues and intelligently throttles requests
72
72
  * [superagent-charset](https://github.com/magicdawn/superagent-charset) - add charset support for node's SuperAgent
73
+ * [superagent-verbose-errors](https://github.com/jcoreio/superagent-verbose-errors) - include response body in error messages for failed requests
73
74
 
74
75
  Please prefix your plugin with `superagent-*` so that it can easily be found by others.
75
76
 
package/docs/index.md CHANGED
@@ -8,12 +8,8 @@ SuperAgent is light-weight progressive ajax API crafted for flexibility, readabi
8
8
  .send({ name: 'Manny', species: 'cat' })
9
9
  .set('X-API-Key', 'foobar')
10
10
  .set('Accept', 'application/json')
11
- .end(function(err, res){
12
- if (err || !res.ok) {
13
- alert('Oh no! error');
14
- } else {
15
- alert('yay got ' + JSON.stringify(res.body));
16
- }
11
+ .then(function(res) {
12
+ alert('yay got ' + JSON.stringify(res.body));
17
13
  });
18
14
 
19
15
  ## Test documentation
@@ -22,27 +18,32 @@ The following [test documentation](docs/test.html) was generated with [Mocha's](
22
18
 
23
19
  ## Request basics
24
20
 
25
- A request can be initiated by invoking the appropriate method on the `request` object, then calling `.end()` to send the request. For example a simple __GET__ request:
21
+ A request can be initiated by invoking the appropriate method on the `request` object, then calling `.then()` (or `.end()` [or `await`](#promise-and-generator-support)) to send the request. For example a simple __GET__ request:
26
22
 
27
23
  request
28
24
  .get('/search')
29
- .end(function(err, res){
30
-
25
+ .then(function(res) {
26
+ // res.body, res.headers, res.status
27
+ })
28
+ .catch(function(err) {
29
+ // err.message, err.response
31
30
  });
32
31
 
33
- A method string may also be passed:
32
+ HTTP method may also be passed as a string:
34
33
 
35
- request('GET', '/search').end(callback);
34
+ request('GET', '/search').then(success, failure);
36
35
 
37
- ES6 promises are supported. *Instead* of `.end()` you can call `.then()`:
36
+ Old-style callbacks are also supported. *Instead of* `.then()` you can call `.end()`:
38
37
 
39
- request('GET', '/search').then(success, failure);
38
+ request('GET', '/search').end(function(err, res){
39
+ if (res.ok) {}
40
+ });
40
41
 
41
- The __Node__ client may also provide absolute URLs. In browsers absolute URLs won't work unless the server implements [CORS](#cors).
42
+ Absolute URLs can be used. In web browsers absolute URLs work only if the server implements [CORS](#cors).
42
43
 
43
44
  request
44
45
  .get('http://example.com/search')
45
- .end(function(err, res){
46
+ .then(function(res) {
46
47
 
47
48
  });
48
49
 
@@ -52,7 +53,7 @@ The __Node__ client supports making requests to [Unix Domain Sockets](http://en.
52
53
  // Use `%2F` as `/` in SOCKET_PATH
53
54
  request
54
55
  .get('http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search')
55
- .end(function(err, res){
56
+ .then(res => {
56
57
 
57
58
  });
58
59
 
@@ -60,13 +61,13 @@ __DELETE__, __HEAD__, __PATCH__, __POST__, and __PUT__ requests can also be used
60
61
 
61
62
  request
62
63
  .head('/favicon.ico')
63
- .end(function(err, res){
64
+ .then(function(res) {
64
65
 
65
66
  });
66
67
 
67
68
  __DELETE__ can be also called as `.del()` for compatibility with old IE where `delete` is a reserved word.
68
69
 
69
- The HTTP method defaults to __GET__, so if you wish, the following is valid:
70
+ The HTTP method defaults to __GET__, so if you wish, the following is valid:
70
71
 
71
72
  request('/search', function(err, res){
72
73
 
@@ -80,14 +81,14 @@ Setting header fields is simple, invoke `.set()` with a field name and value:
80
81
  .get('/search')
81
82
  .set('API-Key', 'foobar')
82
83
  .set('Accept', 'application/json')
83
- .end(callback);
84
+ .then(callback);
84
85
 
85
86
  You may also pass an object to set several fields in a single call:
86
87
 
87
88
  request
88
89
  .get('/search')
89
90
  .set({ 'API-Key': 'foobar', Accept: 'application/json' })
90
- .end(callback);
91
+ .then(callback);
91
92
 
92
93
  ## `GET` requests
93
94
 
@@ -98,7 +99,7 @@ The `.query()` method accepts objects, which when used with the __GET__ method w
98
99
  .query({ query: 'Manny' })
99
100
  .query({ range: '1..5' })
100
101
  .query({ order: 'desc' })
101
- .end(function(err, res){
102
+ .then(function(res) {
102
103
 
103
104
  });
104
105
 
@@ -107,7 +108,7 @@ Or as a single object:
107
108
  request
108
109
  .get('/search')
109
110
  .query({ query: 'Manny', range: '1..5', order: 'desc' })
110
- .end(function(err, res){
111
+ .then(function(res) {
111
112
 
112
113
  });
113
114
 
@@ -116,7 +117,7 @@ The `.query()` method accepts strings as well:
116
117
  request
117
118
  .get('/querystring')
118
119
  .query('search=Manny&range=1..5')
119
- .end(function(err, res){
120
+ .then(function(res) {
120
121
 
121
122
  });
122
123
 
@@ -126,7 +127,7 @@ Or joined:
126
127
  .get('/querystring')
127
128
  .query('search=Manny')
128
129
  .query('range=1..5')
129
- .end(function(err, res){
130
+ .then(function(res) {
130
131
 
131
132
  });
132
133
 
@@ -137,7 +138,7 @@ You can also use the `.query()` method for HEAD requests. The following will pro
137
138
  request
138
139
  .head('/users')
139
140
  .query({ email: 'joe@smith.com' })
140
- .end(function(err, res){
141
+ .then(function(res) {
141
142
 
142
143
  });
143
144
 
@@ -148,20 +149,20 @@ A typical JSON __POST__ request might look a little like the following, where we
148
149
  request.post('/user')
149
150
  .set('Content-Type', 'application/json')
150
151
  .send('{"name":"tj","pet":"tobi"}')
151
- .end(callback)
152
+ .then(callback)
152
153
 
153
154
  Since JSON is undoubtedly the most common, it's the _default_! The following example is equivalent to the previous.
154
155
 
155
156
  request.post('/user')
156
157
  .send({ name: 'tj', pet: 'tobi' })
157
- .end(callback)
158
+ .then(callback)
158
159
 
159
160
  Or using multiple `.send()` calls:
160
161
 
161
162
  request.post('/user')
162
163
  .send({ name: 'tj' })
163
164
  .send({ pet: 'tobi' })
164
- .end(callback)
165
+ .then(callback)
165
166
 
166
167
  By default sending strings will set the `Content-Type` to `application/x-www-form-urlencoded`,
167
168
  multiple calls will be concatenated with `&`, here resulting in `name=tj&pet=tobi`:
@@ -169,7 +170,7 @@ By default sending strings will set the `Content-Type` to `application/x-www-for
169
170
  request.post('/user')
170
171
  .send('name=tj')
171
172
  .send('pet=tobi')
172
- .end(callback);
173
+ .then(callback);
173
174
 
174
175
  SuperAgent formats are extensible, however by default "json" and "form" are supported. To send the data as `application/x-www-form-urlencoded` simply invoke `.type()` with "form", where the default is "json". This request will __POST__ the body "name=tj&pet=tobi".
175
176
 
@@ -177,13 +178,13 @@ SuperAgent formats are extensible, however by default "json" and "form" are supp
177
178
  .type('form')
178
179
  .send({ name: 'tj' })
179
180
  .send({ pet: 'tobi' })
180
- .end(callback)
181
+ .then(callback)
181
182
 
182
183
  Sending a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData) object is also supported. The following example will __POST__ the content of the HTML form identified by id="myForm":
183
184
 
184
185
  request.post('/user')
185
186
  .send(new FormData(document.getElementById('myForm')))
186
- .end(callback)
187
+ .then(callback)
187
188
 
188
189
  ## Setting the `Content-Type`
189
190
 
@@ -207,8 +208,28 @@ simply the extension name such as "xml", "json", "png", etc:
207
208
 
208
209
  ## Serializing request body
209
210
 
210
- SuperAgent will automatically serialize JSON and forms. If you want to send the payload in a custom format, you can replace the built-in serialization with `.serialize()` method.
211
+ SuperAgent will automatically serialize JSON and forms.
212
+ You can setup automatic serialization for other types as well:
213
+
214
+ ```js
215
+ request.serialize['application/xml'] = function (obj) {
216
+ return 'string generated from obj';
217
+ };
218
+
219
+ //going forward, all requests with a Content-type of
220
+ //'application/xml' will be automatically serialized
221
+ ```
222
+ If you want to send the payload in a custom format, you can replace
223
+ the built-in serialization with the `.serialize()` method on a per-request basis:
211
224
 
225
+ ```js
226
+ request
227
+ .post('/user')
228
+ .send({foo: 'bar'})
229
+ .serialize(function serializer(obj) {
230
+ return 'string generated from obj';
231
+ });
232
+ ```
212
233
  ## Retrying requests
213
234
 
214
235
  When given the `.retry()` method, SuperAgent will automatically retry requests, if they fail in a way that is transient or could be due to a flaky Internet connection.
@@ -217,8 +238,9 @@ This method has two optional arguments: number of retries (default 3) and a call
217
238
 
218
239
  request
219
240
  .get('http://example.com/search')
220
- .retry(2)
221
- .end(callback);
241
+ .retry(2) // or:
242
+ .retry(2, callback)
243
+ .then(finished);
222
244
 
223
245
  Use `.retry()` only with requests that are *idempotent* (i.e. multiple requests reaching the server won't cause undesirable side effects like duplicate purchases).
224
246
 
@@ -248,7 +270,7 @@ If you are calling Facebook's API, be sure to send an `Accept: application/json`
248
270
  .query({ format: 'json' })
249
271
  .query({ dest: '/login' })
250
272
  .send({ post: 'data', here: 'wahoo' })
251
- .end(callback);
273
+ .then(callback);
252
274
 
253
275
  By default the query string is not assembled in any particular order. An asciibetically-sorted query string can be enabled with `req.sortQuery()`. You may also provide a custom sorting comparison function with `req.sortQuery(myComparisonFn)`. The comparison function should take 2 arguments and return a negative/zero/positive integer.
254
276
 
@@ -258,7 +280,7 @@ By default the query string is not assembled in any particular order. An asciibe
258
280
  .query('name=Nick')
259
281
  .query('search=Manny')
260
282
  .sortQuery()
261
- .end(callback)
283
+ .then(callback)
262
284
 
263
285
  // customized sort function
264
286
  request.get('/user')
@@ -267,7 +289,7 @@ By default the query string is not assembled in any particular order. An asciibe
267
289
  .sortQuery(function(a, b){
268
290
  return a.length - b.length;
269
291
  })
270
- .end(callback)
292
+ .then(callback)
271
293
  ```
272
294
 
273
295
  ## TLS options
@@ -289,7 +311,7 @@ request
289
311
  .post('/client-auth')
290
312
  .key(key)
291
313
  .cert(cert)
292
- .end(callback);
314
+ .then(callback);
293
315
  ```
294
316
 
295
317
  ```js
@@ -298,12 +320,32 @@ var ca = fs.readFileSync('ca.cert.pem');
298
320
  request
299
321
  .post('https://localhost/private-ca-server')
300
322
  .ca(ca)
301
- .end(callback);
323
+ .then(res => {});
302
324
  ```
303
325
 
304
326
  ## Parsing response bodies
305
327
 
306
- SuperAgent will parse known response-body data for you, currently supporting `application/x-www-form-urlencoded`, `application/json`, and `multipart/form-data`.
328
+ SuperAgent will parse known response-body data for you,
329
+ currently supporting `application/x-www-form-urlencoded`,
330
+ `application/json`, and `multipart/form-data`. You can setup
331
+ automatic parsing for other response-body data as well:
332
+
333
+ ```js
334
+ //browser
335
+ request.parse['application/xml'] = function (str) {
336
+ return {'object': 'parsed from str'};
337
+ };
338
+
339
+ //node
340
+ request.parse['application/xml'] = function (res, cb) {
341
+ //parse response text and set res.body here
342
+
343
+ cb(null, res);
344
+ };
345
+
346
+ //going forward, responses of type 'application/xml'
347
+ //will be parsed automatically
348
+ ```
307
349
 
308
350
  You can set a custom parser (that takes precedence over built-in parsers) with the `.buffer(true).parse(fn)` method. If response buffering is not enabled (`.buffer(false)`) then the `response` event will be emitted without waiting for the body parser to finish, so `response.body` won't be available.
309
351
 
@@ -414,8 +456,10 @@ You should use both `deadline` and `response` timeouts. This way you can use a s
414
456
  response: 5000, // Wait 5 seconds for the server to start sending,
415
457
  deadline: 60000, // but allow 1 minute for the file to finish loading.
416
458
  })
417
- .end(function(err, res){
418
- if (err.timeout) { /* timed out! */ }
459
+ .then(res => {
460
+ /* responded in time */
461
+ }, err => {
462
+ if (err.timeout) { /* timed out! */ } else { /* other error */ }
419
463
  });
420
464
 
421
465
  Timeout errors have a `.timeout` property.
@@ -427,12 +471,12 @@ In both Node and browsers auth available via the `.auth()` method:
427
471
  request
428
472
  .get('http://local')
429
473
  .auth('tobi', 'learnboost')
430
- .end(callback);
474
+ .then(callback);
431
475
 
432
476
 
433
477
  In the _Node_ client Basic auth can be in the URL as "user:pass":
434
478
 
435
- request.get('http://tobi:learnboost@local').end(callback);
479
+ request.get('http://tobi:learnboost@local').then(callback);
436
480
 
437
481
  By default only `Basic` auth is used. In browser you can add `{type:'auto'}` to enable all methods built-in in the browser (Digest, NTLM, etc.):
438
482
 
@@ -445,7 +489,7 @@ By default up to 5 redirects will be followed, however you may specify this with
445
489
  request
446
490
  .get('/some.png')
447
491
  .redirects(2)
448
- .end(callback);
492
+ .then(callback);
449
493
 
450
494
  ## Agents for global state
451
495
 
@@ -464,7 +508,7 @@ In browsers cookies are managed automatically by the browser, so the `.agent()`
464
508
 
465
509
  ### Default options for multiple requests
466
510
 
467
- Regular request methods (`.use()`, `.set()`, `.auth()`) called on the agent will be used as defaults for all requests made by that agent.
511
+ Regular request methods called on the agent will be used as defaults for all requests made by that agent.
468
512
 
469
513
  const agent = request.agent()
470
514
  .use(plugin)
@@ -473,9 +517,13 @@ Regular request methods (`.use()`, `.set()`, `.auth()`) called on the agent will
473
517
  await agent.get('/with-plugin-and-auth');
474
518
  await agent.get('/also-with-plugin-and-auth');
475
519
 
520
+ The complete list of methods that the agent can use to set defaults is: `use`, `on`, `once`, `set`, `query`, `type`, `accept`, `auth`, `withCredentials`, `sortQuery`, `retry`, `ok`, `redirects`, `timeout`, `buffer`, `serialize`, `parse`, `ca`, `key`, `pfx`, `cert`.
521
+
476
522
  ## Piping data
477
523
 
478
- The Node client allows you to pipe data to and from the request. For example piping a file's contents as the request:
524
+ The Node client allows you to pipe data to and from the request. Please note that `.pipe()` is used **instead of** `.end()`/`.then()` methods.
525
+
526
+ For example piping a file's contents as the request:
479
527
 
480
528
  const request = require('superagent');
481
529
  const fs = require('fs');
@@ -493,19 +541,19 @@ Or piping the response to a file:
493
541
  const req = request.get('/some.json');
494
542
  req.pipe(stream);
495
543
 
496
- Note that you should **NOT** attempt to pipe the result of `.end()` or the `Response` object:
544
+ It's not possible to mix pipes and callbacks or promises. Note that you should **NOT** attempt to pipe the result of `.end()` or the `Response` object:
497
545
 
498
546
  // Don't do either of these:
499
547
  const stream = getAWritableStream();
500
548
  const req = request
501
549
  .get('/some.json')
502
- // this pipes garbage to the stream and fails in unexpected ways
503
- .end((err, response) => response.pipe(stream))
550
+ // BAD: this pipes garbage to the stream and fails in unexpected ways
551
+ .end((err, this_does_not_work) => this_does_not_work.pipe(stream))
504
552
  const req = request
505
553
  .get('/some.json')
506
554
  .end()
507
- // this is also unsupported, .pipe calls .end for you.
508
- .pipe(stream);
555
+ // BAD: this is also unsupported, .pipe calls .end for you.
556
+ .pipe(nope_its_too_late);
509
557
 
510
558
  In a [future version](https://github.com/visionmedia/superagent/issues/1188) of superagent, improper calls to `pipe()` will fail.
511
559
 
@@ -519,7 +567,7 @@ When you use `.field()` or `.attach()` you can't use `.send()` and you *must not
519
567
 
520
568
  To send a file use `.attach(name, [file], [options])`. You can attach multiple files by calling `.attach` multiple times. The arguments are:
521
569
 
522
- * `name` — filed name in the form.
570
+ * `name` — field name in the form.
523
571
  * `file` — either string with file path or `Blob`/`Buffer` object.
524
572
  * `options` — (optional) either string with custom file name or `{filename: string}` object. In Node also `{contentType: 'mime/type'}` is supported. In browser create a `Blob` with an appropriate type instead.
525
573
 
@@ -530,7 +578,7 @@ To send a file use `.attach(name, [file], [options])`. You can attach multiple f
530
578
  .attach('image1', 'path/to/felix.jpeg')
531
579
  .attach('image2', imageBuffer, 'luna.jpeg')
532
580
  .field('caption', 'My cats')
533
- .end(callback);
581
+ .then(callback);
534
582
 
535
583
  ### Field values
536
584
 
@@ -542,7 +590,7 @@ Much like form fields in HTML, you can set field values with `.field(name, value
542
590
  .field('user[email]', 'tobi@learnboost.com')
543
591
  .field('friends[]', ['loki', 'jane'])
544
592
  .attach('image', 'path/to/tobi.png')
545
- .end(callback);
593
+ .then(callback);
546
594
 
547
595
  ## Compression
548
596
 
@@ -563,7 +611,7 @@ The `.withCredentials()` method enables the ability to send cookies from the ori
563
611
  request
564
612
  .get('http://api.example.com:4001/')
565
613
  .withCredentials()
566
- .then(function(res){
614
+ .then(function(res) {
567
615
  assert.equal(200, res.status);
568
616
  assert.equal('tobi', res.text);
569
617
  })
@@ -575,7 +623,7 @@ Your callback function will always be passed two arguments: error and response.
575
623
  request
576
624
  .post('/upload')
577
625
  .attach('image', 'path/to/tobi.png')
578
- .end(function(err, res){
626
+ .then(function(res) {
579
627
 
580
628
  });
581
629
 
@@ -585,7 +633,7 @@ An "error" event is also emitted, with you can listen for:
585
633
  .post('/upload')
586
634
  .attach('image', 'path/to/tobi.png')
587
635
  .on('error', handle)
588
- .end(function(err, res){
636
+ .then(function(res) {
589
637
 
590
638
  });
591
639
 
@@ -629,7 +677,9 @@ SuperAgent fires `progress` events on upload and download of large files.
629
677
 
630
678
  ## Promise and Generator support
631
679
 
632
- SuperAgent's request is a "thenable" object that's compatible with JavaScript promises and `async`/`await` syntax. Do not call `.end()` if you're using promises.
680
+ SuperAgent's request is a "thenable" object that's compatible with JavaScript promises and `async`/`await` syntax.
681
+
682
+ If you're using promises, **do not** call `.end()` or `.pipe()`. Any use of `.then()` or `await` disables all other ways of using the request.
633
683
 
634
684
  Libraries like [co](https://github.com/tj/co) or a web framework like [koa](https://github.com/koajs/koa) can `yield` on any SuperAgent method:
635
685
 
package/lib/client.js CHANGED
@@ -186,7 +186,7 @@ request.types = {
186
186
 
187
187
  request.serialize = {
188
188
  'application/x-www-form-urlencoded': serialize,
189
- 'application/json': JSON.stringify,
189
+ 'application/json': JSON.stringify
190
190
  };
191
191
 
192
192
  /**
@@ -200,7 +200,7 @@ request.serialize = {
200
200
 
201
201
  request.parse = {
202
202
  'application/x-www-form-urlencoded': parseString,
203
- 'application/json': JSON.parse,
203
+ 'application/json': JSON.parse
204
204
  };
205
205
 
206
206
  /**
package/lib/node/index.js CHANGED
@@ -25,6 +25,7 @@ const zlib = require('zlib');
25
25
  const util = require('util');
26
26
  const pkg = require('../../package.json');
27
27
  const RequestBase = require('../request-base');
28
+ const CookieJar = require('cookiejar');
28
29
 
29
30
  function request(method, url) {
30
31
  // callback
@@ -654,15 +655,24 @@ Request.prototype.request = function(){
654
655
  if (this.username && this.password) {
655
656
  this.auth(this.username, this.password);
656
657
  }
657
-
658
- // add cookies
659
- if (this.cookies) req.setHeader('Cookie', this.cookies);
660
-
661
658
  for (const key in this.header) {
662
659
  if (this.header.hasOwnProperty(key))
663
660
  req.setHeader(key, this.header[key]);
664
661
  }
665
662
 
663
+ // add cookies
664
+ if (this.cookies) {
665
+ if(this.header.hasOwnProperty('cookie')) {
666
+ // merge
667
+ const tmpJar = new CookieJar.CookieJar();
668
+ tmpJar.setCookies(this.header.cookie.split(';'));
669
+ tmpJar.setCookies(this.cookies.split(';'));
670
+ req.setHeader('Cookie',tmpJar.getCookies(CookieJar.CookieAccessInfo.All).toValueString());
671
+ } else {
672
+ req.setHeader('Cookie', this.cookies);
673
+ }
674
+ }
675
+
666
676
  return req;
667
677
  };
668
678
 
@@ -942,6 +952,47 @@ Request.prototype._end = function() {
942
952
 
943
953
  this.emit('request', this);
944
954
 
955
+ const getProgressMonitor = () => {
956
+ const lengthComputable = true;
957
+ const total = req.getHeader('Content-Length');
958
+ let loaded = 0;
959
+
960
+ const progress = new Stream.Transform();
961
+ progress._transform = (chunk, encoding, cb) => {
962
+ loaded += chunk.length;
963
+ this.emit('progress', {
964
+ direction: 'upload',
965
+ lengthComputable,
966
+ loaded,
967
+ total,
968
+ });
969
+ cb(null, chunk);
970
+ };
971
+ return progress;
972
+ };
973
+
974
+ const bufferToChunks = (buffer) => {
975
+ const chunkSize = 16 * 1024; // default highWaterMark value
976
+ const chunking = new Stream.Readable();
977
+ const totalLength = buffer.length;
978
+ const remainder = totalLength % chunkSize;
979
+ const cutoff = totalLength - remainder;
980
+
981
+ for (let i = 0; i < cutoff; i += chunkSize) {
982
+ const chunk = buffer.slice(i, i + chunkSize);
983
+ chunking.push(chunk);
984
+ }
985
+
986
+ if (remainder > 0) {
987
+ const remainderBuffer = buffer.slice(-remainder);
988
+ chunking.push(remainderBuffer);
989
+ }
990
+
991
+ chunking.push(null); // no more data
992
+
993
+ return chunking;
994
+ }
995
+
945
996
  // if a FormData instance got created, then we send that as the request body
946
997
  const formData = this._formData;
947
998
  if (formData) {
@@ -961,27 +1012,11 @@ Request.prototype._end = function() {
961
1012
  if ('number' == typeof length) {
962
1013
  req.setHeader('Content-Length', length);
963
1014
  }
964
-
965
- const getProgressMonitor = () => {
966
- const lengthComputable = true;
967
- const total = req.getHeader('Content-Length');
968
- let loaded = 0;
969
-
970
- const progress = new Stream.Transform();
971
- progress._transform = (chunk, encoding, cb) => {
972
- loaded += chunk.length;
973
- this.emit('progress', {
974
- direction: 'upload',
975
- lengthComputable,
976
- loaded,
977
- total,
978
- });
979
- cb(null, chunk);
980
- };
981
- return progress;
982
- };
1015
+
983
1016
  formData.pipe(getProgressMonitor()).pipe(req);
984
1017
  });
1018
+ } else if (Buffer.isBuffer(data)) {
1019
+ bufferToChunks(data).pipe(getProgressMonitor()).pipe(req);
985
1020
  } else {
986
1021
  req.end(data);
987
1022
  }
@@ -244,7 +244,7 @@ RequestBase.prototype.then = function then(resolve, reject) {
244
244
  return this._fullfilledPromise.then(resolve, reject);
245
245
  };
246
246
 
247
- RequestBase.prototype.catch = function(cb) {
247
+ RequestBase.prototype['catch'] = function(cb) {
248
248
  return this.then(undefined, cb);
249
249
  };
250
250
 
@@ -124,6 +124,7 @@ ResponseBase.prototype._setStatusProperties = function(status){
124
124
  : false;
125
125
 
126
126
  // sugar
127
+ this.created = 201 == status;
127
128
  this.accepted = 202 == status;
128
129
  this.noContent = 204 == status;
129
130
  this.badRequest = 400 == status;
@@ -131,4 +132,5 @@ ResponseBase.prototype._setStatusProperties = function(status){
131
132
  this.notAcceptable = 406 == status;
132
133
  this.forbidden = 403 == status;
133
134
  this.notFound = 404 == status;
135
+ this.unprocessableEntity = 422 == status;
134
136
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superagent",
3
- "version": "3.8.2",
3
+ "version": "3.8.3",
4
4
  "description": "elegant & feature rich browser / node HTTP with a fluent API",
5
5
  "scripts": {
6
6
  "prepare": "make all",
@@ -29,11 +29,11 @@
29
29
  "debug": "^3.1.0",
30
30
  "extend": "^3.0.0",
31
31
  "form-data": "^2.3.1",
32
- "formidable": "^1.1.1",
32
+ "formidable": "^1.2.0",
33
33
  "methods": "^1.1.1",
34
34
  "mime": "^1.4.1",
35
35
  "qs": "^6.5.1",
36
- "readable-stream": "^2.0.5"
36
+ "readable-stream": "^2.3.5"
37
37
  },
38
38
  "devDependencies": {
39
39
  "Base64": "^1.0.1",
@@ -41,9 +41,9 @@
41
41
  "body-parser": "^1.18.2",
42
42
  "browserify": "^14.1.0",
43
43
  "cookie-parser": "^1.4.3",
44
- "express": "^4.16.0",
44
+ "express": "^4.16.3",
45
45
  "express-session": "^1.15.6",
46
- "marked": "^0.3.6",
46
+ "marked": "0.3.12",
47
47
  "mocha": "^3.5.3",
48
48
  "multer": "^1.3.0",
49
49
  "should": "^11.2.0",