portals
Version:
Client-side HTTP requests with middleware support.
282 lines (266 loc) • 10.1 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var __assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
/**
* Read all response headers from the given XHR instance into a standard
* object literal.
*/
function getAllHeaders(xhr) {
// Read all headers into a string
var headers = xhr.getAllResponseHeaders();
// Split the header components apart into an array
var arr = headers.trim().split(/[\r\n]+/);
// Map over all of the headers to create an object
var output = {};
for (var _i = 0, arr_1 = arr; _i < arr_1.length; _i++) {
var line = arr_1[_i];
var parts = line.split(": ");
var header = parts.shift();
var value = parts.join(": ");
if (!!header) {
output[header] = value;
}
}
// Return the object
return output;
}
/**
* Sends the request to the server. This is used by createPortal() after
* all middleware has been applied.
*/
function send(request) {
return new Promise(function (accept, reject) {
// Create the XHR object for the request
var xhr = new XMLHttpRequest();
// Open the request with the given configuration
xhr.open(request.method, request.url, true);
xhr.withCredentials = !!request.withCredentials;
// Add each header to the XHR request
if (typeof request.headers === "object") {
for (var k in request.headers) {
var value = request.headers[k];
xhr.setRequestHeader(k, (Array.isArray(value) ?
value.join(", ") :
value));
}
}
// Reject on error
xhr.onerror = function (ev) {
reject(ev.error || new Error("XHR request failed without reason."));
};
// Generate a formatted object for the response
xhr.onload = function () {
var res = {
xhr: xhr,
statusCode: xhr.status,
contentType: xhr.getResponseHeader("Content-Type"),
headers: getAllHeaders(xhr),
body: xhr.responseText,
};
accept(res);
};
// Send the request
xhr.send(request.body);
});
}
/**
* Creates and returns a new "portal" instance for creating HTTP
* requests. Each request and response is passed through the
* given middleware.
*/
function createPortal() {
var middleware = [];
for (var _i = 0; _i < arguments.length; _i++) {
middleware[_i] = arguments[_i];
}
// Add our send method as "middleware"
middleware = middleware.concat(send);
return function portal(request) {
var oc_content_type = (request.headers ? request.headers["Content-Type"] : null);
// Create a new copy of our request object so middleware doesn't mutate
// a given object
request = __assign({ method: "GET", withCredentials: false, headers: {
"Content-Type": "text/plain",
} }, request);
// If the request body is a FormData object, then we automatically set
// the Content-Type regardless of middleware
if (!oc_content_type && request.body instanceof FormData) {
request.headers["Content-Type"] = "multipart/form-data";
}
// Track the last invoked middleware index
var lastIndex = -1;
// Next function invokes the next middleware in the stack (only once)
function next(i) {
// Warn and bail if a middleware is invoking its next() method multiple times
if (lastIndex > i) {
console.warn("Middleware fired its next() method more than once.");
return Promise.reject(new Error("Middleware fired its next() method more than once."));
}
// Track the last index that will fire
lastIndex = i;
// Invoke the next middleware in the stack or report an error
try {
var res = middleware[i](request, function () { return next(i + 1); });
return Promise.resolve(res);
}
catch (err) {
return Promise.reject(err);
}
}
return next(0);
};
}
/**
* Encode and parse JSON requests and responses.
*/
function supportsJson() {
return function (req, next) {
if (req.json === true ||
(req.body !== null &&
(typeof req.body === "object" &&
!(req.body instanceof FormData) ||
Array.isArray(req.body)))) {
if (typeof req.headers !== "object") {
req.headers = {};
}
req.headers["Content-Type"] = "application/json";
req.body = JSON.stringify(req.body);
}
return next().then(function (res) {
if (res.contentType && res.contentType.indexOf("json") > -1) {
res.body = JSON.parse(res.body);
}
return res;
});
};
}
/**
* Applies the hostname prefix to the URL unless the given
* URL already has a hostname.
*/
function applyHostnamePrefix(url, prefix) {
return (url.indexOf("http") === 0 ? url : prefix + url);
}
/**
* Applies a URI prefix.
*/
function applyResourcePrefix(url, prefix) {
if (url.indexOf("http") === 0) {
var matches = url.match(/^(https?\:\/\/[^\/?#]+)(?:[\/?#]|$)/i);
var domain = matches && matches[1];
return (domain + prefix + url.replace(domain || "", ""));
}
else {
return prefix + url;
}
}
/**
* Adds the given prefix string to the request's URL. Application rules
* differ if the given prefix includes a hostname or not.
*/
function withPrefix(prefix) {
var applyPrefix = (prefix.indexOf("http") === 0 ? applyHostnamePrefix : applyResourcePrefix);
return function (request, next) {
request.url = applyPrefix(request.url, prefix);
return next();
};
}
/**
* Add a header to the request that can either override, or be overridden
* by, the headers in the request.
*/
function withHeader(name, value, override) {
if (override === void 0) { override = false; }
// If value isn't a function, make it one
var getValue = (typeof value !== "function" ? function () { return value; } : value);
return function (request, next) {
var _a;
if (typeof request.headers !== "object") {
request.headers = {};
}
if (override) {
request.headers[name] = getValue(request);
}
else {
request.headers = __assign((_a = {}, _a[name] = getValue(request), _a), request.headers);
}
return next();
};
}
/**
* Adds the Authorization header with the returned string as the header value.
* Won't set the Authorization token if the result from getToken is falsey.
*/
function withAuthorization(getToken, prefix) {
if (prefix === void 0) { prefix = ""; }
return function (request, next) {
var token = getToken(request);
if (!!token) {
if (typeof request.headers !== "object") {
request.headers = {};
}
request.headers["Authorization"] = prefix + token;
}
return next();
};
}
/**
* Adds the Authorization header with the returned string as the bearer token.
* Won't set the Authorization token if the result from getToken is falsey.
*/
function withBearer(getToken) {
return withAuthorization(getToken, "Bearer ");
}
/**
* Passes along the XSRF header from the XRSF-TOKEN cookie.
*/
function withXSRFToken(cookieName, headerName) {
if (cookieName === void 0) { cookieName = "XSRF-TOKEN"; }
if (headerName === void 0) { headerName = "X-XSRF-TOKEN"; }
return function (req, next) {
var match = document.cookie.match(new RegExp("(^|;\\s*)(" + cookieName + ")=([^;]*)"));
var cookie = (match ? decodeURIComponent(match[3]) : "");
if (cookie) {
req.headers = __assign({}, req.headers);
req.headers[headerName] = cookie;
}
return next();
};
}
(function (Method) {
Method["GET"] = "GET";
Method["POST"] = "POST";
Method["PUT"] = "PUT";
Method["PATCH"] = "PATCH";
Method["DELETE"] = "DELETE";
Method["HEAD"] = "HEAD";
Method["UPDATE"] = "UPDATE";
})(exports.Method || (exports.Method = {}));
exports.getAllHeaders = getAllHeaders;
exports.send = send;
exports.createPortal = createPortal;
exports.supportsJson = supportsJson;
exports.withPrefix = withPrefix;
exports.withHeader = withHeader;
exports.withAuthorization = withAuthorization;
exports.withBearer = withBearer;
exports.withXSRFToken = withXSRFToken;