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 +9 -0
- package/Readme.md +6 -7
- package/docs/index.md +10 -10
- package/lib/client.js +12 -12
- package/lib/node/agent.js +8 -2
- package/lib/node/index.js +28 -0
- package/lib/node/utils.js +5 -0
- package/lib/request-base.js +28 -3
- package/package.json +1 -1
- package/superagent.js +40 -15
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/
|
|
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
|
-
|
|
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) -
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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__,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
780
|
+
e.direction = direction;
|
|
781
781
|
self.emit('progress', e);
|
|
782
|
-
};
|
|
783
|
-
if (this.hasListeners('progress')) {
|
|
784
|
-
xhr.onprogress = handleProgress;
|
|
785
782
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
xhr.
|
|
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('
|
|
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)
|
|
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
|
|
package/lib/request-base.js
CHANGED
|
@@ -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
|
|
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
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
|
|
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 =
|
|
1369
|
+
e.direction = direction;
|
|
1345
1370
|
self.emit('progress', e);
|
|
1346
|
-
};
|
|
1347
|
-
if (this.hasListeners('progress')) {
|
|
1348
|
-
xhr.onprogress = handleProgress;
|
|
1349
1371
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
xhr.
|
|
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
|