kinto
Version:
An Offline-First JavaScript client for Kinto.
214 lines (191 loc) • 6.63 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
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; }; })();
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"); } }
var _errorsJs = require("./errors.js");
var _errorsJs2 = _interopRequireDefault(_errorsJs);
/**
* Enhanced HTTP client for the Kinto protocol.
*/
var HTTP = (function () {
_createClass(HTTP, null, [{
key: "DEFAULT_REQUEST_HEADERS",
/**
* Default HTTP request headers applied to each outgoing request.
*
* @type {Object}
*/
get: function get() {
return {
"Accept": "application/json",
"Content-Type": "application/json"
};
}
/**
* Default options.
*
* @type {Object}
*/
}, {
key: "defaultOptions",
get: function get() {
return { timeout: 5000, requestMode: "cors" };
}
/**
* Constructor.
*
* Options:
* - {Number} timeout The request timeout in ms (default: `5000`).
* - {String} requestMode The HTTP request mode (default: `"cors"`).
*
* @param {EventEmitter} events The event handler.
* @param {Object} options The options object.
*/
}]);
function HTTP(events) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
_classCallCheck(this, HTTP);
// public properties
/**
* The event emitter instance.
* @type {EventEmitter}
*/
if (!events) {
throw new Error("No events handler provided");
}
this.events = events;
options = Object.assign({}, HTTP.defaultOptions, options);
/**
* The request mode.
* @see https://fetch.spec.whatwg.org/#requestmode
* @type {String}
*/
this.requestMode = options.requestMode;
/**
* The request timeout.
* @type {Number}
*/
this.timeout = options.timeout;
}
/**
* Performs an HTTP request to the Kinto server.
*
* Options:
* - `{Object} headers` The request headers object (default: {})
*
* Resolves with an objet containing the following HTTP response properties:
* - `{Number} status` The HTTP status code.
* - `{Object} json` The JSON response body.
* - `{Headers} headers` The response headers object; see the ES6 fetch() spec.
*
* @param {String} url The URL.
* @param {Object} options The fetch() options object.
* @return {Promise}
*/
_createClass(HTTP, [{
key: "request",
value: function request(url) {
var _this = this;
var options = arguments.length <= 1 || arguments[1] === undefined ? { headers: {} } : arguments[1];
var response = undefined,
status = undefined,
statusText = undefined,
headers = undefined,
_timeoutId = undefined,
hasTimedout = undefined;
// Ensure default request headers are always set
options.headers = Object.assign({}, HTTP.DEFAULT_REQUEST_HEADERS, options.headers);
options.mode = this.requestMode;
return new Promise(function (resolve, reject) {
_timeoutId = setTimeout(function () {
hasTimedout = true;
reject(new Error("Request timeout."));
}, _this.timeout);
fetch(url, options).then(function (res) {
if (!hasTimedout) {
clearTimeout(_timeoutId);
resolve(res);
}
})["catch"](function (err) {
if (!hasTimedout) {
clearTimeout(_timeoutId);
reject(err);
}
});
}).then(function (res) {
response = res;
headers = res.headers;
status = res.status;
statusText = res.statusText;
_this._checkForDeprecationHeader(headers);
_this._checkForBackoffHeader(status, headers);
return res.text();
})
// Check if we have a body; if so parse it as JSON.
.then(function (text) {
if (text.length === 0) {
return null;
}
// Note: we can't consume the response body twice.
return JSON.parse(text);
})["catch"](function (err) {
var error = new Error("HTTP " + (status || 0) + "; " + err);
error.response = response;
error.stack = err.stack;
throw error;
}).then(function (json) {
if (json && status >= 400) {
var message = "HTTP " + status + "; ";
if (json.errno && json.errno in _errorsJs2["default"]) {
message += _errorsJs2["default"][json.errno];
if (json.message) {
message += ": " + json.message;
}
} else {
message += statusText || "";
}
var error = new Error(message.trim());
error.response = response;
error.data = json;
throw error;
}
return { status: status, json: json, headers: headers };
});
}
}, {
key: "_checkForDeprecationHeader",
value: function _checkForDeprecationHeader(headers) {
var alertHeader = headers.get("Alert");
if (!alertHeader) {
return;
}
var alert = undefined;
try {
alert = JSON.parse(alertHeader);
} catch (err) {
console.warn("Unable to parse Alert header message", alertHeader);
return;
}
console.warn(alert.message, alert.url);
this.events.emit("deprecated", alert);
}
}, {
key: "_checkForBackoffHeader",
value: function _checkForBackoffHeader(status, headers) {
var backoffMs = undefined;
var backoffSeconds = parseInt(headers.get("Backoff"), 10);
if (backoffSeconds > 0) {
backoffMs = new Date().getTime() + backoffSeconds * 1000;
} else {
backoffMs = 0;
}
this.events.emit("backoff", backoffMs);
}
}]);
return HTTP;
})();
exports["default"] = HTTP;
module.exports = exports["default"];