diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..8974620
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Change Log
+
+All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+# 0.1.0 (2017-12-27)
+
+
+### Features
+
+* **request:** CHECKOUT-2738 Add RequestSender ([8c953bd](https://github.com/bigcommerce/request-sender-js/commit/8c953bd))
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..b284d00
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,23 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var cookie = require("js-cookie");
+var request_factory_1 = require("./request-factory");
+var request_sender_1 = require("./request-sender");
+var payload_transformer_1 = require("./payload-transformer");
+var timeout_1 = require("./timeout");
+/**
+ * @return {RequestSender}
+ */
+function createRequestSender() {
+ return new request_sender_1.default(new request_factory_1.default(), new payload_transformer_1.default(), cookie);
+}
+exports.createRequestSender = createRequestSender;
+/**
+ * @param {number} [delay]
+ * @return {Timeout}
+ */
+function createTimeout(delay) {
+ return new timeout_1.default(delay);
+}
+exports.createTimeout = createTimeout;
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/lib/index.js.map b/lib/index.js.map
new file mode 100644
index 0000000..2ef5435
--- /dev/null
+++ b/lib/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":";;AAAA,kCAAoC;AACpC,qDAA+C;AAC/C,mDAA6C;AAC7C,6DAAuD;AACvD,qCAAgC;AAEhC;;GAEG;AACH;IACI,MAAM,CAAC,IAAI,wBAAa,CACpB,IAAI,yBAAc,EAAE,EACpB,IAAI,6BAAkB,EAAE,EACxB,MAAM,CACT,CAAC;AACN,CAAC;AAND,kDAMC;AAED;;;GAGG;AACH,uBAA8B,KAAK;IAC/B,MAAM,CAAC,IAAI,iBAAO,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAFD,sCAEC"}
\ No newline at end of file
diff --git a/lib/payload-transformer.js b/lib/payload-transformer.js
new file mode 100644
index 0000000..bdfc787
--- /dev/null
+++ b/lib/payload-transformer.js
@@ -0,0 +1,78 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var tslib_1 = require("tslib");
+var JSON_CONTENT_TYPE_REGEXP = /application\/(\w+\+)?json/;
+var PayloadTransformer = /** @class */ (function () {
+ function PayloadTransformer() {
+ }
+ /**
+ * @param {RequestOptions} options
+ * @returns {any}
+ */
+ PayloadTransformer.prototype.toRequestBody = function (options) {
+ var contentType = this._getHeader(options.headers, 'Content-Type');
+ if (options.body && JSON_CONTENT_TYPE_REGEXP.test(contentType)) {
+ return JSON.stringify(options.body);
+ }
+ return options.body;
+ };
+ /**
+ * @param {XMLHttpRequest} xhr
+ * @returns {Response}
+ */
+ PayloadTransformer.prototype.toResponse = function (xhr) {
+ var headers = this._parseResponseHeaders(xhr.getAllResponseHeaders());
+ var body = this._parseResponseBody('response' in xhr ? xhr.response : xhr.responseText, headers);
+ return {
+ body: body,
+ headers: headers,
+ status: xhr.status,
+ statusText: xhr.statusText,
+ };
+ };
+ /**
+ * @private
+ * @param {string} body
+ * @param {Object} headers
+ * @return {any}
+ */
+ PayloadTransformer.prototype._parseResponseBody = function (body, headers) {
+ var contentType = this._getHeader(headers, 'Content-Type');
+ if (body && JSON_CONTENT_TYPE_REGEXP.test(contentType)) {
+ return JSON.parse(body);
+ }
+ return body;
+ };
+ /**
+ * @private
+ * @param {string} rawHeaders
+ * @return {Object}
+ */
+ PayloadTransformer.prototype._parseResponseHeaders = function (rawHeaders) {
+ var lines = rawHeaders ? rawHeaders.replace(/\r?\n[\t ]+/g, ' ').split(/\r?\n/) : [];
+ return lines.reduce(function (headers, line) {
+ var parts = line.split(':');
+ var key = parts.shift().trim();
+ if (!key) {
+ return headers;
+ }
+ return tslib_1.__assign({}, headers, (_a = {}, _a[key.toLowerCase()] = parts.join(':').trim(), _a));
+ var _a;
+ }, {});
+ };
+ /**
+ * @private
+ * @param {Object} headers
+ * @param {string} key
+ * @return {string}
+ */
+ PayloadTransformer.prototype._getHeader = function (headers, key) {
+ if (!headers || !key) {
+ return '';
+ }
+ return headers[key] || headers[key.toLowerCase()] || '';
+ };
+ return PayloadTransformer;
+}());
+exports.default = PayloadTransformer;
+//# sourceMappingURL=payload-transformer.js.map
\ No newline at end of file
diff --git a/lib/payload-transformer.js.map b/lib/payload-transformer.js.map
new file mode 100644
index 0000000..319e6ec
--- /dev/null
+++ b/lib/payload-transformer.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"payload-transformer.js","sourceRoot":"","sources":["../src/payload-transformer.js"],"names":[],"mappings":";;;AAAA,IAAM,wBAAwB,GAAG,2BAA2B,CAAC;AAE7D;IAAA;IAmFA,CAAC;IAlFG;;;OAGG;IACH,0CAAa,GAAb,UAAc,OAAO;QACjB,IAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QAErE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,wBAAwB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;IACxB,CAAC;IAED;;;OAGG;IACH,uCAAU,GAAV,UAAW,GAAG;QACV,IAAM,OAAO,GAAG,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC,CAAC;QACxE,IAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAEnG,MAAM,CAAC;YACH,IAAI,MAAA;YACJ,OAAO,SAAA;YACP,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,UAAU,EAAE,GAAG,CAAC,UAAU;SAC7B,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACH,+CAAkB,GAAlB,UAAmB,IAAI,EAAE,OAAO;QAC5B,IAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QAE7D,EAAE,CAAC,CAAC,IAAI,IAAI,wBAAwB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAED,MAAM,CAAC,IAAI,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,kDAAqB,GAArB,UAAsB,UAAU;QAC5B,IAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAEvF,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,UAAC,OAAO,EAAE,IAAI;YAC9B,IAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAM,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;YAEjC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACP,MAAM,CAAC,OAAO,CAAC;YACnB,CAAC;YAED,MAAM,sBACC,OAAO,eACT,GAAG,CAAC,WAAW,EAAE,IAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,OAC7C;;QACN,CAAC,EAAE,EAAE,CAAC,CAAC;IACX,CAAC;IAED;;;;;OAKG;IACH,uCAAU,GAAV,UAAW,OAAO,EAAE,GAAG;QACnB,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YACnB,MAAM,CAAC,EAAE,CAAC;QACd,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAC5D,CAAC;IACL,yBAAC;AAAD,CAAC,AAnFD,IAmFC"}
\ No newline at end of file
diff --git a/lib/request-factory.js b/lib/request-factory.js
new file mode 100644
index 0000000..084cdca
--- /dev/null
+++ b/lib/request-factory.js
@@ -0,0 +1,64 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var queryString = require("query-string");
+var RequestFactory = /** @class */ (function () {
+ function RequestFactory() {
+ }
+ /**
+ * @param {string} url
+ * @param {Object} [options={}]
+ * @returns {XMLHttpRequest}
+ */
+ RequestFactory.prototype.createRequest = function (url, options) {
+ if (options === void 0) { options = {}; }
+ var xhr = new XMLHttpRequest();
+ this._configureRequest(xhr, url, options);
+ return xhr;
+ };
+ /**
+ * @private
+ * @param {XMLHttpRequest} xhr
+ * @param {string} url
+ * @param {RequestOptions} options
+ * @returns {void}
+ */
+ RequestFactory.prototype._configureRequest = function (xhr, url, options) {
+ xhr.open(options.method, this._formatUrl(url, options.params), true);
+ this._configureRequestHeaders(xhr, options.headers);
+ if (typeof options.credentials === 'boolean') {
+ xhr.withCredentials = options.credentials;
+ }
+ if (typeof options.timeout === 'number') {
+ xhr.timeout = options.timeout;
+ }
+ };
+ /**
+ * @private
+ * @param {XMLHttpRequest} xhr
+ * @param {Object} headers
+ * @return {void}
+ */
+ RequestFactory.prototype._configureRequestHeaders = function (xhr, headers) {
+ if (!headers) {
+ return;
+ }
+ Object.keys(headers).forEach(function (key) {
+ xhr.setRequestHeader(key, headers[key]);
+ });
+ };
+ /**
+ * @private
+ * @param {string} url
+ * @param {Object} params
+ * @return {string}
+ */
+ RequestFactory.prototype._formatUrl = function (url, params) {
+ if (!params || Object.keys(params).length === 0) {
+ return url;
+ }
+ return url + "?" + queryString.stringify(params);
+ };
+ return RequestFactory;
+}());
+exports.default = RequestFactory;
+//# sourceMappingURL=request-factory.js.map
\ No newline at end of file
diff --git a/lib/request-factory.js.map b/lib/request-factory.js.map
new file mode 100644
index 0000000..05ce287
--- /dev/null
+++ b/lib/request-factory.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"request-factory.js","sourceRoot":"","sources":["../src/request-factory.js"],"names":[],"mappings":";;AAAA,0CAA4C;AAE5C;IAAA;IAgEA,CAAC;IA/DG;;;;OAIG;IACH,sCAAa,GAAb,UAAc,GAAG,EAAE,OAAY;QAAZ,wBAAA,EAAA,YAAY;QAC3B,IAAM,GAAG,GAAG,IAAI,cAAc,EAAE,CAAC;QAEjC,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAE1C,MAAM,CAAC,GAAG,CAAC;IACf,CAAC;IAED;;;;;;OAMG;IACH,0CAAiB,GAAjB,UAAkB,GAAG,EAAE,GAAG,EAAE,OAAO;QAC/B,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;QAErE,IAAI,CAAC,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAEpD,EAAE,CAAC,CAAC,OAAO,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC;YAC3C,GAAG,CAAC,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC;QAC9C,CAAC;QAED,EAAE,CAAC,CAAC,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC;YACtC,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAClC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,iDAAwB,GAAxB,UAAyB,GAAG,EAAE,OAAO;QACjC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACX,MAAM,CAAC;QACX,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,UAAC,GAAG;YAC7B,GAAG,CAAC,gBAAgB,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;;;OAKG;IACH,mCAAU,GAAV,UAAW,GAAG,EAAE,MAAM;QAClB,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;YAC9C,MAAM,CAAC,GAAG,CAAC;QACf,CAAC;QAED,MAAM,CAAI,GAAG,SAAI,WAAW,CAAC,SAAS,CAAC,MAAM,CAAG,CAAC;IACrD,CAAC;IACL,qBAAC;AAAD,CAAC,AAhED,IAgEC"}
\ No newline at end of file
diff --git a/lib/request-options.typedef.js b/lib/request-options.typedef.js
new file mode 100644
index 0000000..97ba2ec
--- /dev/null
+++ b/lib/request-options.typedef.js
@@ -0,0 +1,11 @@
+"use strict";
+/**
+ * @typedef {Object} RequestOptions
+ * @property {?any} body
+ * @property {?Object} headers
+ * @property {?Object} params
+ * @property {?string} method
+ * @property {?boolean} credentials
+ * @property {?Timeout} timeout
+ */
+//# sourceMappingURL=request-options.typedef.js.map
\ No newline at end of file
diff --git a/lib/request-options.typedef.js.map b/lib/request-options.typedef.js.map
new file mode 100644
index 0000000..0dbbb2f
--- /dev/null
+++ b/lib/request-options.typedef.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"request-options.typedef.js","sourceRoot":"","sources":["../src/request-options.typedef.js"],"names":[],"mappings":";AAAA;;;;;;;;GAQG"}
\ No newline at end of file
diff --git a/lib/request-sender.js b/lib/request-sender.js
new file mode 100644
index 0000000..f1210fc
--- /dev/null
+++ b/lib/request-sender.js
@@ -0,0 +1,121 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var tslib_1 = require("tslib");
+var isPromise = require("is-promise");
+var lodash_1 = require("lodash");
+var timeout_1 = require("./timeout");
+var RequestSender = /** @class */ (function () {
+ /**
+ * @constructor
+ * @param {RequestFactory} requestFactory
+ * @param {PayloadTransformer} payloadTransformer
+ * @param {Cookie} cookie
+ */
+ function RequestSender(requestFactory, payloadTransformer, cookie) {
+ this._requestFactory = requestFactory;
+ this._payloadTransformer = payloadTransformer;
+ this._cookie = cookie;
+ }
+ /**
+ * @param {string} url
+ * @param {RequestOptions} [options={}]
+ * @return {Promise}
+ */
+ RequestSender.prototype.sendRequest = function (url, options) {
+ var _this = this;
+ if (options === void 0) { options = {}; }
+ var requestOptions = this._mergeDefaultOptions(options);
+ var request = this._requestFactory.createRequest(url, requestOptions);
+ return new Promise(function (resolve, reject) {
+ var requestHandler = function () {
+ var response = _this._payloadTransformer.toResponse(request);
+ if (response.status >= 200 && response.status < 300) {
+ resolve(response);
+ }
+ else {
+ reject(response);
+ }
+ };
+ request.onload = requestHandler;
+ request.onerror = requestHandler;
+ request.onabort = requestHandler;
+ request.ontimeout = requestHandler;
+ if (requestOptions.timeout instanceof timeout_1.default) {
+ requestOptions.timeout.onComplete(function () { return request.abort(); });
+ requestOptions.timeout.start();
+ }
+ if (isPromise(requestOptions.timeout)) {
+ requestOptions.timeout.then(function () { return request.abort(); });
+ }
+ request.send(_this._payloadTransformer.toRequestBody(requestOptions));
+ });
+ };
+ /**
+ * @param {string} url
+ * @param {RequestOptions} [options={}]
+ * @return {Promise}
+ */
+ RequestSender.prototype.get = function (url, options) {
+ if (options === void 0) { options = {}; }
+ return this.sendRequest(url, tslib_1.__assign({}, options, { method: 'GET' }));
+ };
+ /**
+ * @param {string} url
+ * @param {RequestOptions} [options={}]
+ * @return {Promise}
+ */
+ RequestSender.prototype.post = function (url, options) {
+ if (options === void 0) { options = {}; }
+ return this.sendRequest(url, tslib_1.__assign({}, options, { method: 'POST' }));
+ };
+ /**
+ * @param {string} url
+ * @param {RequestOptions} [options={}]
+ * @return {Promise}
+ */
+ RequestSender.prototype.put = function (url, options) {
+ if (options === void 0) { options = {}; }
+ return this.sendRequest(url, tslib_1.__assign({}, options, { method: 'PUT' }));
+ };
+ /**
+ * @param {string} url
+ * @param {RequestOptions} [options={}]
+ * @return {Promise}
+ */
+ RequestSender.prototype.patch = function (url, options) {
+ if (options === void 0) { options = {}; }
+ return this.sendRequest(url, tslib_1.__assign({}, options, { method: 'PATCH' }));
+ };
+ /**
+ * @param {string} url
+ * @param {RequestOptions} [options={}]
+ * @return {Promise}
+ */
+ RequestSender.prototype.delete = function (url, options) {
+ if (options === void 0) { options = {}; }
+ return this.sendRequest(url, tslib_1.__assign({}, options, { method: 'DELETE' }));
+ };
+ /**
+ * @private
+ * @param {RequestOptions} options
+ * @return {RequestOptions}
+ */
+ RequestSender.prototype._mergeDefaultOptions = function (options) {
+ var defaultOptions = {
+ credentials: true,
+ method: 'GET',
+ headers: {
+ 'Accept': 'application/json, text/plain, */*',
+ 'Content-Type': 'application/json',
+ },
+ };
+ var csrfToken = this._cookie.get('XSRF-TOKEN');
+ if (csrfToken) {
+ defaultOptions.headers['X-XSRF-TOKEN'] = csrfToken;
+ }
+ return lodash_1.merge({}, defaultOptions, options);
+ };
+ return RequestSender;
+}());
+exports.default = RequestSender;
+//# sourceMappingURL=request-sender.js.map
\ No newline at end of file
diff --git a/lib/request-sender.js.map b/lib/request-sender.js.map
new file mode 100644
index 0000000..e3f9599
--- /dev/null
+++ b/lib/request-sender.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"request-sender.js","sourceRoot":"","sources":["../src/request-sender.js"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/lib/response.typedef.js b/lib/response.typedef.js
new file mode 100644
index 0000000..db25bfc
--- /dev/null
+++ b/lib/response.typedef.js
@@ -0,0 +1,9 @@
+"use strict";
+/**
+ * @typedef {Object} Response
+ * @property {any} body
+ * @property {Object} headers
+ * @property {number} status
+ * @property {string} statusText
+ */
+//# sourceMappingURL=response.typedef.js.map
\ No newline at end of file
diff --git a/lib/response.typedef.js.map b/lib/response.typedef.js.map
new file mode 100644
index 0000000..8c142be
--- /dev/null
+++ b/lib/response.typedef.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"response.typedef.js","sourceRoot":"","sources":["../src/response.typedef.js"],"names":[],"mappings":";AAAA;;;;;;GAMG"}
\ No newline at end of file
diff --git a/lib/responses.mock.js b/lib/responses.mock.js
new file mode 100644
index 0000000..58ace6e
--- /dev/null
+++ b/lib/responses.mock.js
@@ -0,0 +1,37 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var tslib_1 = require("tslib");
+function getResponse(body, headers, status, statusText) {
+ if (headers === void 0) { headers = {}; }
+ if (status === void 0) { status = 200; }
+ if (statusText === void 0) { statusText = 'OK'; }
+ return {
+ body: body,
+ status: status,
+ statusText: statusText,
+ headers: tslib_1.__assign({ 'content-type': 'application/json' }, headers),
+ };
+}
+exports.getResponse = getResponse;
+function getErrorResponse(body, headers, status, statusText) {
+ if (headers === void 0) { headers = {}; }
+ if (status === void 0) { status = 400; }
+ if (statusText === void 0) { statusText = 'Bad Request'; }
+ return {
+ body: body,
+ status: status,
+ statusText: statusText,
+ headers: tslib_1.__assign({ 'content-type': 'application/json' }, headers),
+ };
+}
+exports.getErrorResponse = getErrorResponse;
+function getTimeoutResponse() {
+ return {
+ body: '',
+ headers: {},
+ status: 0,
+ statusText: undefined,
+ };
+}
+exports.getTimeoutResponse = getTimeoutResponse;
+//# sourceMappingURL=responses.mock.js.map
\ No newline at end of file
diff --git a/lib/responses.mock.js.map b/lib/responses.mock.js.map
new file mode 100644
index 0000000..3292aa2
--- /dev/null
+++ b/lib/responses.mock.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"responses.mock.js","sourceRoot":"","sources":["../src/responses.mock.js"],"names":[],"mappings":";;;AAAA,qBAA4B,IAAI,EAAE,OAAY,EAAE,MAAY,EAAE,UAAiB;IAA7C,wBAAA,EAAA,YAAY;IAAE,uBAAA,EAAA,YAAY;IAAE,2BAAA,EAAA,iBAAiB;IAC3E,MAAM,CAAC;QACH,IAAI,MAAA;QACJ,MAAM,QAAA;QACN,UAAU,YAAA;QACV,OAAO,qBACH,cAAc,EAAE,kBAAkB,IAC/B,OAAO,CACb;KACJ,CAAC;AACN,CAAC;AAVD,kCAUC;AAED,0BAAiC,IAAI,EAAE,OAAY,EAAE,MAAY,EAAE,UAA0B;IAAtD,wBAAA,EAAA,YAAY;IAAE,uBAAA,EAAA,YAAY;IAAE,2BAAA,EAAA,0BAA0B;IACzF,MAAM,CAAC;QACH,IAAI,MAAA;QACJ,MAAM,QAAA;QACN,UAAU,YAAA;QACV,OAAO,qBACH,cAAc,EAAE,kBAAkB,IAC/B,OAAO,CACb;KACJ,CAAC;AACN,CAAC;AAVD,4CAUC;AAED;IACI,MAAM,CAAC;QACH,IAAI,EAAE,EAAE;QACR,OAAO,EAAE,EAAE;QACX,MAAM,EAAE,CAAC;QACT,UAAU,EAAE,SAAS;KACxB,CAAC;AACN,CAAC;AAPD,gDAOC"}
\ No newline at end of file
diff --git a/lib/timeout.js b/lib/timeout.js
new file mode 100644
index 0000000..daddb26
--- /dev/null
+++ b/lib/timeout.js
@@ -0,0 +1,44 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+var Timeout = /** @class */ (function () {
+ /**
+ * @param {number} [delay]
+ * @constructor
+ */
+ function Timeout(delay) {
+ var _this = this;
+ this._delay = delay;
+ this._timeoutToken = null;
+ this._promise = new Promise(function (resolve) {
+ _this._resolve = resolve;
+ });
+ }
+ /**
+ * @param {Function} callback
+ * @return {void}
+ */
+ Timeout.prototype.onComplete = function (callback) {
+ this._promise.then(callback);
+ };
+ /**
+ * @return {void}
+ */
+ Timeout.prototype.complete = function () {
+ this._resolve();
+ if (this._timeoutToken) {
+ clearTimeout(this._timeoutToken);
+ }
+ };
+ /**
+ * @return {void}
+ */
+ Timeout.prototype.start = function () {
+ var _this = this;
+ if (this._delay) {
+ this._timeoutToken = setTimeout(function () { return _this.complete(); }, this._delay);
+ }
+ };
+ return Timeout;
+}());
+exports.default = Timeout;
+//# sourceMappingURL=timeout.js.map
\ No newline at end of file
diff --git a/lib/timeout.js.map b/lib/timeout.js.map
new file mode 100644
index 0000000..318dab1
--- /dev/null
+++ b/lib/timeout.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"timeout.js","sourceRoot":"","sources":["../src/timeout.js"],"names":[],"mappings":";;AAAA;IACI;;;OAGG;IACH,iBAAY,KAAK;QAAjB,iBAOC;QANG,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAE1B,IAAI,CAAC,QAAQ,GAAG,IAAI,OAAO,CAAC,UAAC,OAAO;YAChC,KAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QAC5B,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;OAGG;IACH,4BAAU,GAAV,UAAW,QAAQ;QACf,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,0BAAQ,GAAR;QACI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAEhB,EAAE,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;YACrB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,uBAAK,GAAL;QAAA,iBAIC;QAHG,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACd,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,cAAM,OAAA,KAAI,CAAC,QAAQ,EAAE,EAAf,CAAe,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACxE,CAAC;IACL,CAAC;IACL,cAAC;AAAD,CAAC,AAzCD,IAyCC"}
\ No newline at end of file
diff --git a/package.json b/package.json
index 4750a91..c05760c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@bigcommerce/request-sender",
- "version": "0.0.0",
+ "version": "0.1.0",
"description": "HTTP request client for browsers",
"main": "lib/index.js",
"files": [