superagent 2.2.0 → 2.3.0

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,12 @@
1
+ # 2.3.0
2
+
3
+ * Enabled `.field()` to handle objects (Affan Shahid)
4
+ * Added authentication with client certificates (terusus)
5
+ * Added `.catch()` for more Promise-like interface (Maxim Samoilov, Kornel Lesiński)
6
+ * Silenced errors from incomplete gzip streams for compatibility with web browsers (Kornel Lesiński)
7
+ * Fixed `event.direction` in uploads (Kornel Lesiński)
8
+ * Fixed returned value of overwritten response object's `on()` method (Juan Dopazo)
9
+
1
10
  # 2.2.0
2
11
 
3
12
  * Added `timedout` property to node Request instance (Alexander Pope)
package/Readme.md CHANGED
@@ -18,7 +18,7 @@ component:
18
18
  $ component install visionmedia/superagent
19
19
  ```
20
20
 
21
- Works with [browserify](https://github.com/substack/node-browserify) and should work with [webpack](https://github.com/visionmedia/superagent/wiki/Superagent-for-Webpack)
21
+ Works with [browserify](https://github.com/substack/node-browserify) and should work with [webpack](https://github.com/visionmedia/superagent/wiki/SuperAgent-for-Webpack)
22
22
 
23
23
  ```js
24
24
  request
@@ -46,7 +46,7 @@ Even though IE9 is supported, a polyfill for `window.FormData` is required for `
46
46
 
47
47
  # Plugins
48
48
 
49
- Superagent is easily extended via plugins.
49
+ SuperAgent is easily extended via plugins.
50
50
 
51
51
  ```js
52
52
  var nocache = require('superagent-no-cache');
@@ -68,18 +68,17 @@ Existing plugins:
68
68
  * [superagent-suffix](https://github.com/timneutkens1/superagent-suffix) - suffix URLs with a given path
69
69
  * [superagent-mock](https://github.com/M6Web/superagent-mock) - simulate HTTP calls by returning data fixtures based on the requested URL
70
70
  * [superagent-mocker](https://github.com/shuvalov-anton/superagent-mocker) — simulate REST API
71
- * [superagent-cache](https://github.com/jpodwys/superagent-cache) - superagent with built-in, flexible caching
71
+ * [superagent-cache](https://github.com/jpodwys/superagent-cache) - SuperAgent with built-in, flexible caching (compatible with SuperAgent `1.x`)
72
72
  * [superagent-jsonapify](https://github.com/alex94puchades/superagent-jsonapify) - A lightweight [json-api](http://jsonapi.org/format/) client addon for superagent
73
73
  * [superagent-serializer](https://github.com/zzarcon/superagent-serializer) - Converts server payload into different cases
74
- * [superagent-promise-plugin](https://github.com/jomaxx/superagent-promise-plugin) - Shims req.end to return a promise when executed with no callback.
75
74
  * [superagent-use](https://github.com/koenpunt/superagent-use) - A client addon to apply plugins to all requests.
76
75
  * [superagent-httpbackend](https://www.npmjs.com/package/superagent-httpbackend) - stub out requests using AngularJS' $httpBackend syntax
77
76
  * [superagent-throttle](https://github.com/leviwheatcroft/superagent-throttle) - queues and intelligently throttles requests
78
- * [superagent-charset](https://github.com/magicdawn/superagent-charset) - add charset support for node's superagent
77
+ * [superagent-charset](https://github.com/magicdawn/superagent-charset) - add charset support for node's SuperAgent
79
78
 
80
79
  Please prefix your plugin with `superagent-*` so that it can easily be found by others.
81
80
 
82
- For superagent extensions such as couchdb and oauth visit the [wiki](https://github.com/visionmedia/superagent/wiki).
81
+ For SuperAgent extensions such as couchdb and oauth visit the [wiki](https://github.com/visionmedia/superagent/wiki).
83
82
 
84
83
  ## Running node tests
85
84
 
@@ -119,7 +118,7 @@ Edit tests and refresh your browser. You do not have to restart the test runner.
119
118
 
120
119
  **npm (for browser standalone)** When we publish versions to npm, we run `make superagent.js` which generates the standalone `superagent.js` file via `browserify`, and this file is included in the package published to npm (but this file is never checked into the git repository). If users want to install via npm but serve a single `.js` file directly to the browser, the `node_modules/superagent/superagent.js` is a standalone browserified file ready to go for that purpose. It is not minified.
121
120
 
122
- **npm (for browserify)** is handled via the `package.json` `browser` field which allows users to install superagent via npm, reference it from their browser code with `require('superagent')`, and then build their own application bundle via `browserify`, which will use `lib/client.js` as the superagent entrypoint.
121
+ **npm (for browserify)** is handled via the `package.json` `browser` field which allows users to install SuperAgent via npm, reference it from their browser code with `require('superagent')`, and then build their own application bundle via `browserify`, which will use `lib/client.js` as the SuperAgent entrypoint.
123
122
 
124
123
  **bower** is configured via the `bower.json` file. Bower installs files directly from git/github without any transformation.
125
124
 
package/docs/index.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  # SuperAgent
3
3
 
4
- Super Agent is light-weight progressive ajax API crafted for flexibility, readability, and a low learning curve after being frustrated with many of the existing request APIs. It also works with Node.js!
4
+ SuperAgent is light-weight progressive ajax API crafted for flexibility, readability, and a low learning curve after being frustrated with many of the existing request APIs. It also works with Node.js!
5
5
 
6
6
  request
7
7
  .post('/api/pet')
@@ -46,7 +46,7 @@ ES6 promises are supported. Instead of `.end()` you can call `.then()`:
46
46
 
47
47
  });
48
48
 
49
- __DELETE__, __HEAD__, __POST__, __PUT__ and other __HTTP__ verbs may also be used, simply change the method name:
49
+ __DELETE__, __HEAD__, __PATCH__, __POST__, and __PUT__ requests can also be used, simply change the method name:
50
50
 
51
51
  request
52
52
  .head('/favicon.ico')
@@ -199,7 +199,7 @@ You can also use the `.query()` method for HEAD requests. The following will pro
199
199
 
200
200
  ## Serializing request body
201
201
 
202
- 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.
202
+ 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.
203
203
 
204
204
  ## Setting Accept
205
205
 
@@ -227,7 +227,7 @@ In a similar fashion to the `.type()` method it is also possible to set the Acce
227
227
 
228
228
  ## Parsing response bodies
229
229
 
230
- Super Agent will parse known response-body data for you, currently supporting `application/x-www-form-urlencoded`, `application/json`, and `multipart/form-data`.
230
+ SuperAgent will parse known response-body data for you, currently supporting `application/x-www-form-urlencoded`, `application/json`, and `multipart/form-data`.
231
231
 
232
232
  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.
233
233
 
@@ -365,7 +365,7 @@ In a similar fashion to the `.type()` method it is also possible to set the Acce
365
365
 
366
366
  ## Multipart requests
367
367
 
368
- Super Agent is also great for _building_ multipart requests for which it provides methods `.attach()` and `.field()`.
368
+ SuperAgent is also great for _building_ multipart requests for which it provides methods `.attach()` and `.field()`.
369
369
 
370
370
  ### Attaching files
371
371
 
@@ -454,23 +454,23 @@ Your callback function will always be passed two arguments: error and response.
454
454
 
455
455
  ## Promise and Generator support
456
456
 
457
- Superagent's request is a "thenable" object that's compatible with JavaScript promises and `async`/`await` syntax.
457
+ SuperAgent's request is a "thenable" object that's compatible with JavaScript promises and `async`/`await` syntax.
458
458
 
459
- 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:
459
+ 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:
460
460
 
461
461
  var res = yield request
462
462
  .get('http://local')
463
463
  .auth('tobi', 'learnboost')
464
464
 
465
- Note that superagent expects the global `Promise` object to be present. You'll need a polyfill to use promises in Internet Explorer or Node.js 0.10.
465
+ Note that SuperAgent expects the global `Promise` object to be present. You'll need a polyfill to use promises in Internet Explorer or Node.js 0.10.
466
466
 
467
467
 
468
468
  ## Browser and node versions
469
469
 
470
- Superagent has two implementations: one for web browsers (using XHR) and one for Node.JS (using core http module). By default Browserify and WebPack will pick the browser version.
470
+ SuperAgent has two implementations: one for web browsers (using XHR) and one for Node.JS (using core http module). By default Browserify and WebPack will pick the browser version.
471
471
 
472
472
  If want to use WebPack to compile code for Node.JS, you *must* specify [node target](webpack.github.io/docs/configuration.html#target) in its configuration.
473
473
 
474
474
  ### Using browser version in electron
475
475
 
476
- [Electron](http://electron.atom.io/) developers report if you would prefer to use the browser version of superagent instead of the Node version, you can `require('superagent/superagent')`. Your requests will now show up in the Chrome developer tools Network tab. Note this environment is not covered by automated test suite and not officially supported.
476
+ [Electron](http://electron.atom.io/) developers report if you would prefer to use the browser version of SuperAgent instead of the Node version, you can `require('superagent/superagent')`. Your requests will now show up in the Chrome developer tools Network tab. Note this environment is not covered by automated test suite and not officially supported.
package/lib/client.js CHANGED
@@ -773,24 +773,24 @@ Request.prototype.end = function(fn){
773
773
  };
774
774
 
775
775
  // progress
776
- var handleProgress = function(e){
776
+ var handleProgress = function(direction, e) {
777
777
  if (e.total > 0) {
778
778
  e.percent = e.loaded / e.total * 100;
779
779
  }
780
- e.direction = 'download';
780
+ e.direction = direction;
781
781
  self.emit('progress', e);
782
- };
783
- if (this.hasListeners('progress')) {
784
- xhr.onprogress = handleProgress;
785
782
  }
786
- try {
787
- if (xhr.upload && this.hasListeners('progress')) {
788
- xhr.upload.onprogress = handleProgress;
783
+ if (this.hasListeners('progress')) {
784
+ try {
785
+ xhr.onprogress = handleProgress.bind(null, 'download');
786
+ if (xhr.upload) {
787
+ xhr.upload.onprogress = handleProgress.bind(null, 'upload');
788
+ }
789
+ } catch(e) {
790
+ // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist.
791
+ // Reported here:
792
+ // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context
789
793
  }
790
- } catch(e) {
791
- // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist.
792
- // Reported here:
793
- // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context
794
794
  }
795
795
 
796
796
  // timeout
package/lib/node/agent.js CHANGED
@@ -6,7 +6,7 @@
6
6
  var CookieJar = require('cookiejar').CookieJar;
7
7
  var CookieAccess = require('cookiejar').CookieAccessInfo;
8
8
  var parse = require('url').parse;
9
- var request = require('./index');
9
+ var request = require('../..');
10
10
  var methods = require('methods');
11
11
 
12
12
  /**
@@ -23,7 +23,11 @@ module.exports = Agent;
23
23
 
24
24
  function Agent(options) {
25
25
  if (!(this instanceof Agent)) return new Agent(options);
26
- if (options) this._ca = options.ca;
26
+ if (options) {
27
+ this._ca = options.ca;
28
+ this._key = options.key;
29
+ this._cert = options.cert;
30
+ }
27
31
  this.jar = new CookieJar;
28
32
  }
29
33
 
@@ -70,6 +74,8 @@ methods.forEach(function(method){
70
74
  Agent.prototype[name] = function(url, fn){
71
75
  var req = request(method, url);
72
76
  req.ca(this._ca);
77
+ req.key(this._key);
78
+ req.cert(this._cert);
73
79
 
74
80
  req.on('response', this._saveCookies.bind(this));
75
81
  req.on('redirect', this._saveCookies.bind(this));
package/lib/node/index.js CHANGED
@@ -489,6 +489,32 @@ Request.prototype.ca = function(cert){
489
489
  return this;
490
490
  };
491
491
 
492
+ /**
493
+ * Set the client certificate key option for https request.
494
+ *
495
+ * @param {Buffer | String} cert
496
+ * @return {Request} for chaining
497
+ * @api public
498
+ */
499
+
500
+ Request.prototype.key = function(cert){
501
+ this._key = cert;
502
+ return this;
503
+ };
504
+
505
+ /**
506
+ * Set the client certificate option for https request.
507
+ *
508
+ * @param {Buffer | String} cert
509
+ * @return {Request} for chaining
510
+ * @api public
511
+ */
512
+
513
+ Request.prototype.cert = function(cert){
514
+ this._cert = cert;
515
+ return this;
516
+ };
517
+
492
518
  /**
493
519
  * Return an http[s] request.
494
520
  *
@@ -514,6 +540,8 @@ Request.prototype.request = function(){
514
540
  options.path = url.pathname;
515
541
  options.host = url.hostname;
516
542
  options.ca = this._ca;
543
+ options.key = this._key;
544
+ options.cert = this._cert;
517
545
  options.agent = this._agent;
518
546
 
519
547
  // initiate request
package/lib/node/utils.js CHANGED
@@ -83,6 +83,10 @@ exports.unzip = function(req, res){
83
83
  stream.req = req;
84
84
 
85
85
  unzip.on('error', function(err){
86
+ if (err && err.code === 'Z_BUF_ERROR') { // unexpected end of file is ignored by browsers and curl
87
+ stream.emit('end');
88
+ return;
89
+ }
86
90
  stream.emit('error', err);
87
91
  });
88
92
 
@@ -119,6 +123,7 @@ exports.unzip = function(req, res){
119
123
  } else {
120
124
  _on.call(res, type, fn);
121
125
  }
126
+ return this;
122
127
  };
123
128
  };
124
129
 
@@ -77,6 +77,10 @@ exports.then = function then(resolve, reject) {
77
77
  return this._fullfilledPromise.then(resolve, reject);
78
78
  }
79
79
 
80
+ exports.catch = function(cb) {
81
+ return this.then(undefined, cb);
82
+ };
83
+
80
84
  /**
81
85
  * Allow for extension
82
86
  */
@@ -166,21 +170,42 @@ exports.unset = function(field){
166
170
  };
167
171
 
168
172
  /**
169
- * Write the field `name` and `val` for "multipart/form-data"
170
- * request bodies.
173
+ * Write the field `name` and `val`, or multiple fields with one object
174
+ * for "multipart/form-data" request bodies.
171
175
  *
172
176
  * ``` js
173
177
  * request.post('/upload')
174
178
  * .field('foo', 'bar')
175
179
  * .end(callback);
180
+ *
181
+ * request.post('/upload')
182
+ * .field({ foo: 'bar', baz: 'qux' })
183
+ * .end(callback);
176
184
  * ```
177
185
  *
178
- * @param {String} name
186
+ * @param {String|Object} name
179
187
  * @param {String|Blob|File|Buffer|fs.ReadStream} val
180
188
  * @return {Request} for chaining
181
189
  * @api public
182
190
  */
183
191
  exports.field = function(name, val) {
192
+
193
+ // name should be either a string or an object.
194
+ if (null === name || undefined === name) {
195
+ throw new Error('.field(name, val) name can not be empty');
196
+ }
197
+
198
+ if (isObject(name)) {
199
+ for (var key in name) {
200
+ this.field(key, name[key]);
201
+ }
202
+ return this;
203
+ }
204
+
205
+ // val should be defined now
206
+ if (null === val || undefined === val) {
207
+ throw new Error('.field(name, val) val can not be empty');
208
+ }
184
209
  this._getFormData().append(name, val);
185
210
  return this;
186
211
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superagent",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "elegant & feature rich browser / node HTTP with a fluent API",
5
5
  "scripts": {
6
6
  "prepublish": "make all",
package/superagent.js CHANGED
@@ -93,6 +93,10 @@ exports.then = function then(resolve, reject) {
93
93
  return this._fullfilledPromise.then(resolve, reject);
94
94
  }
95
95
 
96
+ exports.catch = function(cb) {
97
+ return this.then(undefined, cb);
98
+ };
99
+
96
100
  /**
97
101
  * Allow for extension
98
102
  */
@@ -182,21 +186,42 @@ exports.unset = function(field){
182
186
  };
183
187
 
184
188
  /**
185
- * Write the field `name` and `val` for "multipart/form-data"
186
- * request bodies.
189
+ * Write the field `name` and `val`, or multiple fields with one object
190
+ * for "multipart/form-data" request bodies.
187
191
  *
188
192
  * ``` js
189
193
  * request.post('/upload')
190
194
  * .field('foo', 'bar')
191
195
  * .end(callback);
196
+ *
197
+ * request.post('/upload')
198
+ * .field({ foo: 'bar', baz: 'qux' })
199
+ * .end(callback);
192
200
  * ```
193
201
  *
194
- * @param {String} name
202
+ * @param {String|Object} name
195
203
  * @param {String|Blob|File|Buffer|fs.ReadStream} val
196
204
  * @return {Request} for chaining
197
205
  * @api public
198
206
  */
199
207
  exports.field = function(name, val) {
208
+
209
+ // name should be either a string or an object.
210
+ if (null === name || undefined === name) {
211
+ throw new Error('.field(name, val) name can not be empty');
212
+ }
213
+
214
+ if (isObject(name)) {
215
+ for (var key in name) {
216
+ this.field(key, name[key]);
217
+ }
218
+ return this;
219
+ }
220
+
221
+ // val should be defined now
222
+ if (null === val || undefined === val) {
223
+ throw new Error('.field(name, val) val can not be empty');
224
+ }
200
225
  this._getFormData().append(name, val);
201
226
  return this;
202
227
  };
@@ -1337,24 +1362,24 @@ Request.prototype.end = function(fn){
1337
1362
  };
1338
1363
 
1339
1364
  // progress
1340
- var handleProgress = function(e){
1365
+ var handleProgress = function(direction, e) {
1341
1366
  if (e.total > 0) {
1342
1367
  e.percent = e.loaded / e.total * 100;
1343
1368
  }
1344
- e.direction = 'download';
1369
+ e.direction = direction;
1345
1370
  self.emit('progress', e);
1346
- };
1347
- if (this.hasListeners('progress')) {
1348
- xhr.onprogress = handleProgress;
1349
1371
  }
1350
- try {
1351
- if (xhr.upload && this.hasListeners('progress')) {
1352
- xhr.upload.onprogress = handleProgress;
1372
+ if (this.hasListeners('progress')) {
1373
+ try {
1374
+ xhr.onprogress = handleProgress.bind(null, 'download');
1375
+ if (xhr.upload) {
1376
+ xhr.upload.onprogress = handleProgress.bind(null, 'upload');
1377
+ }
1378
+ } catch(e) {
1379
+ // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist.
1380
+ // Reported here:
1381
+ // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context
1353
1382
  }
1354
- } catch(e) {
1355
- // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist.
1356
- // Reported here:
1357
- // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context
1358
1383
  }
1359
1384
 
1360
1385
  // timeout