UNPKG

postgrest-client

Version:
1,789 lines (1,512 loc) 43.5 kB
/*! * PostgREST Client * (c) 2016 Caleb Meredith * Released under the MIT License. */ var PostgREST = /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { var PostgREST = __webpack_require__(1).default PostgREST.default = PostgREST PostgREST.Api = __webpack_require__(1).default PostgREST.ApiRequest = __webpack_require__(2).default module.exports = PostgREST /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var _ApiRequest = __webpack_require__(2); var _ApiRequest2 = _interopRequireDefault(_ApiRequest); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Creates a new PostgREST API object. Use this to start building PostgREST * requests. * * @class * @param {string} The base URL of the API. */ var Api = (function () { // TODO: Use an `OPTIONS *` request to get the server version to output // warnings for unsupported features. function Api(url) { _classCallCheck(this, Api); this.url = url; } /** * Convenience wrapper for starting a PostgREST request builder. Adds the * API URL to the provided path. * * @param {string} The HTTP method of the request. * @param {string} The path of the request. * @returns {ApiRequest} The API request object. */ _createClass(Api, [{ key: 'request', value: function request(method, path) { return new _ApiRequest2.default(method, this.url + path); } }]); return Api; })(); /** * Basic HTTP method functions for quick chaining. * * @param {string} The path of the request. * @returns {ApiRequest} The API request object. */ var methods = ['POST', 'GET', 'PATCH', 'DELETE', 'OPTIONS']; methods.forEach(function (method) { return Api.prototype[method.toLowerCase()] = function requestMethod(path) { return this.request(method, path); }; }); exports.default = Api; /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; Object.defineProperty(exports, "__esModule", { value: true }); var _superagent = __webpack_require__(3); function _typeof(obj) { return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var contentRangeStructure = /^(\d+)-(\d+)\/(\d+)$/; /** * A request building object which contains convenience methods for * communicating with a PostgREST server. * * @class * @param {string} The HTTP method of the request. * @param {string} The path to the request. */ var ApiRequest = (function (_Request) { _inherits(ApiRequest, _Request); function ApiRequest(method, path) { _classCallCheck(this, ApiRequest); var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ApiRequest).call(this, method, path)); _this.set('Accept', 'application/json'); // Fix for superagent disconnect on client & server. if (!_this.get) { _this.get = _this.getHeader; } return _this; } /** * Set auth using special formats. If only one string paramter is passed, it * is interpreted as a bearer token. If an object and nothing else is passed, * `user` and `pass` keys are extracted from it and used for basic auth. * * @param {string|object} The user, bearer token, or user/pass object. * @param {string|void} The pass or undefined. * @returns {ApiRequest} The API request object. */ _createClass(ApiRequest, [{ key: 'auth', value: function auth(user, pass) { if (typeof user === 'string' && pass == null) { this.set('Authorization', 'Bearer ' + user); return this; } if ((typeof user === 'undefined' ? 'undefined' : _typeof(user)) === 'object' && pass == null) { pass = user.pass; user = user.user; } return _get(Object.getPrototypeOf(ApiRequest.prototype), 'auth', this).call(this, user, pass); } /** * Takes a query object and translates it to a PostgREST filter query string. * All values are prefixed with `eq.`. * * @param {object} The object to match against. * @returns {ApiRequest} The API request object. */ }, { key: 'match', value: function match(query) { var newQuery = {}; Object.keys(query).forEach(function (key) { return newQuery[key] = 'eq.' + query[key]; }); return this.query(newQuery); } /** * Cleans up a select string by stripping all whitespace. Then the string is * set as a query string value. Also always forces a root @id column. * * @param {string} The unformatted select string. * @returns {ApiRequest} The API request object. */ }, { key: 'select', value: function select(_select) { if (_select) { this.query({ select: _select.replace(/\s/g, '') }); } return this; } /** * Tells PostgREST in what order the result should be returned. * * @param {string} The property name to order by. * @param {bool} True for descending results, false by default. * @param {bool} True for nulls first, false by default. * @returns {ApiRequest} The API request object. */ }, { key: 'order', value: function order(property) { var ascending = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; var nullsFirst = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; this.query('order=' + property + '.' + (ascending ? 'asc' : 'desc') + '.' + (nullsFirst ? 'nullsfirst' : 'nullslast')); return this; } /** * Specify a range of items for PostgREST to return. If the second value is * not defined, the rest of the collection will be sent back. * * @param {number} The first object to select. * @param {number|void} The last object to select. * @returns {ApiRequest} The API request object. */ }, { key: 'range', value: function range(from, to) { this.set('Range-Unit', 'items'); this.set('Range', (from || 0) + '-' + (to || '')); return this; } /** * Sets the header which signifies to PostgREST the response must be a single * object or 404. * * @returns {ApiRequest} The API request object. */ }, { key: 'single', value: function single() { return this.set('Prefer', 'plurality=singular'); } /** * Sends the request and returns a promise. The super class uses the errback * pattern, but this function overrides that preference to use a promise. * * @returns {Promise} Resolves when the request has completed. */ }, { key: 'end', value: function end() { var _this2 = this; return new Promise(function (resolve, reject) { return _get(Object.getPrototypeOf(ApiRequest.prototype), 'end', _this2).call(_this2, function (error, response) { if (error) { return reject(error); } var body = response.body; var headers = response.headers; var contentRange = headers['content-range']; if (Array.isArray(body) && contentRange && contentRangeStructure.test(contentRange)) { body.fullLength = parseInt(contentRangeStructure.exec(contentRange)[3], 10); } return resolve(body); }); }); } /** * Makes the ApiRequest object then-able. Allows for usage with * `Promise.resolve` and async/await contexts. Just a proxy for `.then()` on * the promise returned from `.end()`. * * @param {function} Called when the request resolves. * @param {function} Called when the request errors. * @returns {Promise} Resolves when the resolution resolves. */ }, { key: 'then', value: function then(resolve, reject) { return this.end().then(resolve, reject); } /** * Just a proxy for `.catch()` on the promise returned from `.end()`. * * @param {function} Called when the request errors. * @returns {Promise} Resolves when there is an error. */ }, { key: 'catch', value: function _catch(reject) { return this.end().catch(reject); } }]); return ApiRequest; })(_superagent.Request); /** * For all of the PostgREST filters add a shortcut method to use it. * * @param {string} The name of the column. * @param {any} The value of the column to be filtered. * @returns {ApiRequest} The API request object. */ var filters = ['eq', 'gt', 'lt', 'gte', 'lte', 'like', 'ilike', 'is', 'in', 'not']; filters.forEach(function (filter) { return ApiRequest.prototype[filter] = function filterValue(name, value) { return this.query(name + '=' + filter + '.' + (Array.isArray(value) ? value.join(',') : value)); }; }); exports.default = ApiRequest; /***/ }, /* 3 */ /***/ function(module, exports, __webpack_require__) { /** * Module dependencies. */ var Emitter = __webpack_require__(4); var reduce = __webpack_require__(5); /** * Root reference for iframes. */ var root; if (typeof window !== 'undefined') { // Browser window root = window; } else if (typeof self !== 'undefined') { // Web Worker root = self; } else { // Other environments root = this; } /** * Noop. */ function noop(){}; /** * Check if `obj` is a host object, * we don't want to serialize these :) * * TODO: future proof, move to compoent land * * @param {Object} obj * @return {Boolean} * @api private */ function isHost(obj) { var str = {}.toString.call(obj); switch (str) { case '[object File]': case '[object Blob]': case '[object FormData]': return true; default: return false; } } /** * Determine XHR. */ request.getXHR = function () { if (root.XMLHttpRequest && (!root.location || 'file:' != root.location.protocol || !root.ActiveXObject)) { return new XMLHttpRequest; } else { try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {} try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {} try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {} try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {} } return false; }; /** * Removes leading and trailing whitespace, added to support IE. * * @param {String} s * @return {String} * @api private */ var trim = ''.trim ? function(s) { return s.trim(); } : function(s) { return s.replace(/(^\s*|\s*$)/g, ''); }; /** * Check if `obj` is an object. * * @param {Object} obj * @return {Boolean} * @api private */ function isObject(obj) { return obj === Object(obj); } /** * Serialize the given `obj`. * * @param {Object} obj * @return {String} * @api private */ function serialize(obj) { if (!isObject(obj)) return obj; var pairs = []; for (var key in obj) { if (null != obj[key]) { pushEncodedKeyValuePair(pairs, key, obj[key]); } } return pairs.join('&'); } /** * Helps 'serialize' with serializing arrays. * Mutates the pairs array. * * @param {Array} pairs * @param {String} key * @param {Mixed} val */ function pushEncodedKeyValuePair(pairs, key, val) { if (Array.isArray(val)) { return val.forEach(function(v) { pushEncodedKeyValuePair(pairs, key, v); }); } pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(val)); } /** * Expose serialization method. */ request.serializeObject = serialize; /** * Parse the given x-www-form-urlencoded `str`. * * @param {String} str * @return {Object} * @api private */ function parseString(str) { var obj = {}; var pairs = str.split('&'); var parts; var pair; for (var i = 0, len = pairs.length; i < len; ++i) { pair = pairs[i]; parts = pair.split('='); obj[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]); } return obj; } /** * Expose parser. */ request.parseString = parseString; /** * Default MIME type map. * * superagent.types.xml = 'application/xml'; * */ request.types = { html: 'text/html', json: 'application/json', xml: 'application/xml', urlencoded: 'application/x-www-form-urlencoded', 'form': 'application/x-www-form-urlencoded', 'form-data': 'application/x-www-form-urlencoded' }; /** * Default serialization map. * * superagent.serialize['application/xml'] = function(obj){ * return 'generated xml here'; * }; * */ request.serialize = { 'application/x-www-form-urlencoded': serialize, 'application/json': JSON.stringify }; /** * Default parsers. * * superagent.parse['application/xml'] = function(str){ * return { object parsed from str }; * }; * */ request.parse = { 'application/x-www-form-urlencoded': parseString, 'application/json': JSON.parse }; /** * Parse the given header `str` into * an object containing the mapped fields. * * @param {String} str * @return {Object} * @api private */ function parseHeader(str) { var lines = str.split(/\r?\n/); var fields = {}; var index; var line; var field; var val; lines.pop(); // trailing CRLF for (var i = 0, len = lines.length; i < len; ++i) { line = lines[i]; index = line.indexOf(':'); field = line.slice(0, index).toLowerCase(); val = trim(line.slice(index + 1)); fields[field] = val; } return fields; } /** * Check if `mime` is json or has +json structured syntax suffix. * * @param {String} mime * @return {Boolean} * @api private */ function isJSON(mime) { return /[\/+]json\b/.test(mime); } /** * Return the mime type for the given `str`. * * @param {String} str * @return {String} * @api private */ function type(str){ return str.split(/ *; */).shift(); }; /** * Return header field parameters. * * @param {String} str * @return {Object} * @api private */ function params(str){ return reduce(str.split(/ *; */), function(obj, str){ var parts = str.split(/ *= */) , key = parts.shift() , val = parts.shift(); if (key && val) obj[key] = val; return obj; }, {}); }; /** * Initialize a new `Response` with the given `xhr`. * * - set flags (.ok, .error, etc) * - parse header * * Examples: * * Aliasing `superagent` as `request` is nice: * * request = superagent; * * We can use the promise-like API, or pass callbacks: * * request.get('/').end(function(res){}); * request.get('/', function(res){}); * * Sending data can be chained: * * request * .post('/user') * .send({ name: 'tj' }) * .end(function(res){}); * * Or passed to `.send()`: * * request * .post('/user') * .send({ name: 'tj' }, function(res){}); * * Or passed to `.post()`: * * request * .post('/user', { name: 'tj' }) * .end(function(res){}); * * Or further reduced to a single call for simple cases: * * request * .post('/user', { name: 'tj' }, function(res){}); * * @param {XMLHTTPRequest} xhr * @param {Object} options * @api private */ function Response(req, options) { options = options || {}; this.req = req; this.xhr = this.req.xhr; // responseText is accessible only if responseType is '' or 'text' and on older browsers this.text = ((this.req.method !='HEAD' && (this.xhr.responseType === '' || this.xhr.responseType === 'text')) || typeof this.xhr.responseType === 'undefined') ? this.xhr.responseText : null; this.statusText = this.req.xhr.statusText; this.setStatusProperties(this.xhr.status); this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders()); // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but // getResponseHeader still works. so we get content-type even if getting // other headers fails. this.header['content-type'] = this.xhr.getResponseHeader('content-type'); this.setHeaderProperties(this.header); this.body = this.req.method != 'HEAD' ? this.parseBody(this.text ? this.text : this.xhr.response) : null; } /** * Get case-insensitive `field` value. * * @param {String} field * @return {String} * @api public */ Response.prototype.get = function(field){ return this.header[field.toLowerCase()]; }; /** * Set header related properties: * * - `.type` the content type without params * * A response of "Content-Type: text/plain; charset=utf-8" * will provide you with a `.type` of "text/plain". * * @param {Object} header * @api private */ Response.prototype.setHeaderProperties = function(header){ // content-type var ct = this.header['content-type'] || ''; this.type = type(ct); // params var obj = params(ct); for (var key in obj) this[key] = obj[key]; }; /** * Parse the given body `str`. * * Used for auto-parsing of bodies. Parsers * are defined on the `superagent.parse` object. * * @param {String} str * @return {Mixed} * @api private */ Response.prototype.parseBody = function(str){ var parse = request.parse[this.type]; return parse && str && (str.length || str instanceof Object) ? parse(str) : null; }; /** * Set flags such as `.ok` based on `status`. * * For example a 2xx response will give you a `.ok` of __true__ * whereas 5xx will be __false__ and `.error` will be __true__. The * `.clientError` and `.serverError` are also available to be more * specific, and `.statusType` is the class of error ranging from 1..5 * sometimes useful for mapping respond colors etc. * * "sugar" properties are also defined for common cases. Currently providing: * * - .noContent * - .badRequest * - .unauthorized * - .notAcceptable * - .notFound * * @param {Number} status * @api private */ Response.prototype.setStatusProperties = function(status){ // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request if (status === 1223) { status = 204; } var type = status / 100 | 0; // status / class this.status = this.statusCode = status; this.statusType = type; // basics this.info = 1 == type; this.ok = 2 == type; this.clientError = 4 == type; this.serverError = 5 == type; this.error = (4 == type || 5 == type) ? this.toError() : false; // sugar this.accepted = 202 == status; this.noContent = 204 == status; this.badRequest = 400 == status; this.unauthorized = 401 == status; this.notAcceptable = 406 == status; this.notFound = 404 == status; this.forbidden = 403 == status; }; /** * Return an `Error` representative of this response. * * @return {Error} * @api public */ Response.prototype.toError = function(){ var req = this.req; var method = req.method; var url = req.url; var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')'; var err = new Error(msg); err.status = this.status; err.method = method; err.url = url; return err; }; /** * Expose `Response`. */ request.Response = Response; /** * Initialize a new `Request` with the given `method` and `url`. * * @param {String} method * @param {String} url * @api public */ function Request(method, url) { var self = this; Emitter.call(this); this._query = this._query || []; this.method = method; this.url = url; this.header = {}; this._header = {}; this.on('end', function(){ var err = null; var res = null; try { res = new Response(self); } catch(e) { err = new Error('Parser is unable to parse the response'); err.parse = true; err.original = e; // issue #675: return the raw response if the response parsing fails err.rawResponse = self.xhr && self.xhr.responseText ? self.xhr.responseText : null; return self.callback(err); } self.emit('response', res); if (err) { return self.callback(err, res); } if (res.status >= 200 && res.status < 300) { return self.callback(err, res); } var new_err = new Error(res.statusText || 'Unsuccessful HTTP response'); new_err.original = err; new_err.response = res; new_err.status = res.status; self.callback(new_err, res); }); } /** * Mixin `Emitter`. */ Emitter(Request.prototype); /** * Allow for extension */ Request.prototype.use = function(fn) { fn(this); return this; } /** * Set timeout to `ms`. * * @param {Number} ms * @return {Request} for chaining * @api public */ Request.prototype.timeout = function(ms){ this._timeout = ms; return this; }; /** * Clear previous timeout. * * @return {Request} for chaining * @api public */ Request.prototype.clearTimeout = function(){ this._timeout = 0; clearTimeout(this._timer); return this; }; /** * Abort the request, and clear potential timeout. * * @return {Request} * @api public */ Request.prototype.abort = function(){ if (this.aborted) return; this.aborted = true; this.xhr.abort(); this.clearTimeout(); this.emit('abort'); return this; }; /** * Set header `field` to `val`, or multiple fields with one object. * * Examples: * * req.get('/') * .set('Accept', 'application/json') * .set('X-API-Key', 'foobar') * .end(callback); * * req.get('/') * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' }) * .end(callback); * * @param {String|Object} field * @param {String} val * @return {Request} for chaining * @api public */ Request.prototype.set = function(field, val){ if (isObject(field)) { for (var key in field) { this.set(key, field[key]); } return this; } this._header[field.toLowerCase()] = val; this.header[field] = val; return this; }; /** * Remove header `field`. * * Example: * * req.get('/') * .unset('User-Agent') * .end(callback); * * @param {String} field * @return {Request} for chaining * @api public */ Request.prototype.unset = function(field){ delete this._header[field.toLowerCase()]; delete this.header[field]; return this; }; /** * Get case-insensitive header `field` value. * * @param {String} field * @return {String} * @api private */ Request.prototype.getHeader = function(field){ return this._header[field.toLowerCase()]; }; /** * Set Content-Type to `type`, mapping values from `request.types`. * * Examples: * * superagent.types.xml = 'application/xml'; * * request.post('/') * .type('xml') * .send(xmlstring) * .end(callback); * * request.post('/') * .type('application/xml') * .send(xmlstring) * .end(callback); * * @param {String} type * @return {Request} for chaining * @api public */ Request.prototype.type = function(type){ this.set('Content-Type', request.types[type] || type); return this; }; /** * Force given parser * * Sets the body parser no matter type. * * @param {Function} * @api public */ Request.prototype.parse = function(fn){ this._parser = fn; return this; }; /** * Set Accept to `type`, mapping values from `request.types`. * * Examples: * * superagent.types.json = 'application/json'; * * request.get('/agent') * .accept('json') * .end(callback); * * request.get('/agent') * .accept('application/json') * .end(callback); * * @param {String} accept * @return {Request} for chaining * @api public */ Request.prototype.accept = function(type){ this.set('Accept', request.types[type] || type); return this; }; /** * Set Authorization field value with `user` and `pass`. * * @param {String} user * @param {String} pass * @return {Request} for chaining * @api public */ Request.prototype.auth = function(user, pass){ var str = btoa(user + ':' + pass); this.set('Authorization', 'Basic ' + str); return this; }; /** * Add query-string `val`. * * Examples: * * request.get('/shoes') * .query('size=10') * .query({ color: 'blue' }) * * @param {Object|String} val * @return {Request} for chaining * @api public */ Request.prototype.query = function(val){ if ('string' != typeof val) val = serialize(val); if (val) this._query.push(val); return this; }; /** * Write the field `name` and `val` for "multipart/form-data" * request bodies. * * ``` js * request.post('/upload') * .field('foo', 'bar') * .end(callback); * ``` * * @param {String} name * @param {String|Blob|File} val * @return {Request} for chaining * @api public */ Request.prototype.field = function(name, val){ if (!this._formData) this._formData = new root.FormData(); this._formData.append(name, val); return this; }; /** * Queue the given `file` as an attachment to the specified `field`, * with optional `filename`. * * ``` js * request.post('/upload') * .attach(new Blob(['<a id="a"><b id="b">hey!</b></a>'], { type: "text/html"})) * .end(callback); * ``` * * @param {String} field * @param {Blob|File} file * @param {String} filename * @return {Request} for chaining * @api public */ Request.prototype.attach = function(field, file, filename){ if (!this._formData) this._formData = new root.FormData(); this._formData.append(field, file, filename); return this; }; /** * Send `data`, defaulting the `.type()` to "json" when * an object is given. * * Examples: * * // querystring * request.get('/search') * .end(callback) * * // multiple data "writes" * request.get('/search') * .send({ search: 'query' }) * .send({ range: '1..5' }) * .send({ order: 'desc' }) * .end(callback) * * // manual json * request.post('/user') * .type('json') * .send('{"name":"tj"}') * .end(callback) * * // auto json * request.post('/user') * .send({ name: 'tj' }) * .end(callback) * * // manual x-www-form-urlencoded * request.post('/user') * .type('form') * .send('name=tj') * .end(callback) * * // auto x-www-form-urlencoded * request.post('/user') * .type('form') * .send({ name: 'tj' }) * .end(callback) * * // defaults to x-www-form-urlencoded * request.post('/user') * .send('name=tobi') * .send('species=ferret') * .end(callback) * * @param {String|Object} data * @return {Request} for chaining * @api public */ Request.prototype.send = function(data){ var obj = isObject(data); var type = this.getHeader('Content-Type'); // merge if (obj && isObject(this._data)) { for (var key in data) { this._data[key] = data[key]; } } else if ('string' == typeof data) { if (!type) this.type('form'); type = this.getHeader('Content-Type'); if ('application/x-www-form-urlencoded' == type) { this._data = this._data ? this._data + '&' + data : data; } else { this._data = (this._data || '') + data; } } else { this._data = data; } if (!obj || isHost(data)) return this; if (!type) this.type('json'); return this; }; /** * Invoke the callback with `err` and `res` * and handle arity check. * * @param {Error} err * @param {Response} res * @api private */ Request.prototype.callback = function(err, res){ var fn = this._callback; this.clearTimeout(); fn(err, res); }; /** * Invoke callback with x-domain error. * * @api private */ Request.prototype.crossDomainError = function(){ var err = new Error('Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.'); err.crossDomain = true; err.status = this.status; err.method = this.method; err.url = this.url; this.callback(err); }; /** * Invoke callback with timeout error. * * @api private */ Request.prototype.timeoutError = function(){ var timeout = this._timeout; var err = new Error('timeout of ' + timeout + 'ms exceeded'); err.timeout = timeout; this.callback(err); }; /** * Enable transmission of cookies with x-domain requests. * * Note that for this to work the origin must not be * using "Access-Control-Allow-Origin" with a wildcard, * and also must set "Access-Control-Allow-Credentials" * to "true". * * @api public */ Request.prototype.withCredentials = function(){ this._withCredentials = true; return this; }; /** * Initiate request, invoking callback `fn(res)` * with an instanceof `Response`. * * @param {Function} fn * @return {Request} for chaining * @api public */ Request.prototype.end = function(fn){ var self = this; var xhr = this.xhr = request.getXHR(); var query = this._query.join('&'); var timeout = this._timeout; var data = this._formData || this._data; // store callback this._callback = fn || noop; // state change xhr.onreadystatechange = function(){ if (4 != xhr.readyState) return; // In IE9, reads to any property (e.g. status) off of an aborted XHR will // result in the error "Could not complete the operation due to error c00c023f" var status; try { status = xhr.status } catch(e) { status = 0; } if (0 == status) { if (self.timedout) return self.timeoutError(); if (self.aborted) return; return self.crossDomainError(); } self.emit('end'); }; // progress var handleProgress = function(e){ if (e.total > 0) { e.percent = e.loaded / e.total * 100; } self.emit('progress', e); }; if (this.hasListeners('progress')) { xhr.onprogress = handleProgress; } try { if (xhr.upload && this.hasListeners('progress')) { xhr.upload.onprogress = handleProgress; } } catch(e) { // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist. // Reported here: // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context } // timeout if (timeout && !this._timer) { this._timer = setTimeout(function(){ self.timedout = true; self.abort(); }, timeout); } // querystring if (query) { query = request.serializeObject(query); this.url += ~this.url.indexOf('?') ? '&' + query : '?' + query; } // initiate request xhr.open(this.method, this.url, true); // CORS if (this._withCredentials) xhr.withCredentials = true; // body if ('GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !isHost(data)) { // serialize stuff var contentType = this.getHeader('Content-Type'); var serialize = this._parser || request.serialize[contentType ? contentType.split(';')[0] : '']; if (!serialize && isJSON(contentType)) serialize = request.serialize['application/json']; if (serialize) data = serialize(data); } // set header fields for (var field in this.header) { if (null == this.header[field]) continue; xhr.setRequestHeader(field, this.header[field]); } // send stuff this.emit('request', this); // IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing) // We need null here if data is undefined xhr.send(typeof data !== 'undefined' ? data : null); return this; }; /** * Faux promise support * * @param {Function} fulfill * @param {Function} reject * @return {Request} */ Request.prototype.then = function (fulfill, reject) { return this.end(function(err, res) { err ? reject(err) : fulfill(res); }); } /** * Expose `Request`. */ request.Request = Request; /** * Issue a request: * * Examples: * * request('GET', '/users').end(callback) * request('/users').end(callback) * request('/users', callback) * * @param {String} method * @param {String|Function} url or callback * @return {Request} * @api public */ function request(method, url) { // callback if ('function' == typeof url) { return new Request('GET', method).end(url); } // url first if (1 == arguments.length) { return new Request('GET', method); } return new Request(method, url); } /** * GET `url` with optional callback `fn(res)`. * * @param {String} url * @param {Mixed|Function} data or fn * @param {Function} fn * @return {Request} * @api public */ request.get = function(url, data, fn){ var req = request('GET', url); if ('function' == typeof data) fn = data, data = null; if (data) req.query(data); if (fn) req.end(fn); return req; }; /** * HEAD `url` with optional callback `fn(res)`. * * @param {String} url * @param {Mixed|Function} data or fn * @param {Function} fn * @return {Request} * @api public */ request.head = function(url, data, fn){ var req = request('HEAD', url); if ('function' == typeof data) fn = data, data = null; if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * DELETE `url` with optional callback `fn(res)`. * * @param {String} url * @param {Function} fn * @return {Request} * @api public */ function del(url, fn){ var req = request('DELETE', url); if (fn) req.end(fn); return req; }; request.del = del; request.delete = del; /** * PATCH `url` with optional `data` and callback `fn(res)`. * * @param {String} url * @param {Mixed} data * @param {Function} fn * @return {Request} * @api public */ request.patch = function(url, data, fn){ var req = request('PATCH', url); if ('function' == typeof data) fn = data, data = null; if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * POST `url` with optional `data` and callback `fn(res)`. * * @param {String} url * @param {Mixed} data * @param {Function} fn * @return {Request} * @api public */ request.post = function(url, data, fn){ var req = request('POST', url); if ('function' == typeof data) fn = data, data = null; if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * PUT `url` with optional `data` and callback `fn(res)`. * * @param {String} url * @param {Mixed|Function} data or fn * @param {Function} fn * @return {Request} * @api public */ request.put = function(url, data, fn){ var req = request('PUT', url); if ('function' == typeof data) fn = data, data = null; if (data) req.send(data); if (fn) req.end(fn); return req; }; /** * Expose `request`. */ module.exports = request; /***/ }, /* 4 */ /***/ function(module, exports) { /** * Expose `Emitter`. */ module.exports = Emitter; /** * Initialize a new `Emitter`. * * @api public */ function Emitter(obj) { if (obj) return mixin(obj); }; /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin(obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key]; } return obj; } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; (this._callbacks[event] = this._callbacks[event] || []) .push(fn); return this; }; /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function(event, fn){ var self = this; this._callbacks = this._callbacks || {}; function on() { self.off(event, on); fn.apply(this, arguments); } on.fn = fn; this.on(event, on); return this; }; /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; // all if (0 == arguments.length) { this._callbacks = {}; return this; } // specific event var callbacks = this._callbacks[event]; if (!callbacks) return this; // remove all handlers if (1 == arguments.length) { delete this._callbacks[event]; return this; } // remove specific handler var cb; for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i]; if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1); break; } } return this; }; /** * Emit `event` with the given args. * * @param {String} event * @param {Mixed} ... * @return {Emitter} */ Emitter.prototype.emit = function(event){ this._callbacks = this._callbacks || {}; var args = [].slice.call(arguments, 1) , callbacks = this._callbacks[event]; if (callbacks) { callbacks = callbacks.slice(0); for (var i = 0, len = callbacks.length; i < len; ++i) { callbacks[i].apply(this, args); } } return this; }; /** * Return array of callbacks for `event`. * * @param {String} event * @return {Array} * @api public */ Emitter.prototype.listeners = function(event){ this._callbacks = this._callbacks || {}; return this._callbacks[event] || []; }; /** * Check if this emitter has `event` handlers. * * @param {String} event * @return {Boolean} * @api public */ Emitter.prototype.hasListeners = function(event){ return !! this.listeners(event).length; }; /***/ }, /* 5 */ /***/ function(module, exports) { /** * Reduce `arr` with `fn`. * * @param {Array} arr * @param {Function} fn * @param {Mixed} initial * * TODO: combatible error handling? */ module.exports = function(arr, fn, initial){ var idx = 0; var len = arr.length; var curr = arguments.length == 3 ? initial : arr[idx++]; while (idx < len) { curr = fn.call(null, curr, arr[idx], ++idx, arr); } return curr; }; /***/ } /******/ ]);