UNPKG

@absmartly/javascript-sdk

Version:

A/B Smartly Javascript SDK

209 lines (208 loc) 6.68 kB
"use strict"; require("core-js/modules/es.array.map.js"); require("core-js/modules/es.promise.js"); var __importDefault = void 0 && (void 0).__importDefault || function (mod) { return mod && mod.__esModule ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const fetch_1 = __importDefault(require("./fetch")); const abort_1 = require("./abort"); const errors_1 = require("./errors"); const utils_1 = require("./utils"); class Client { constructor(opts) { this._opts = Object.assign({ agent: "javascript-client", apiKey: undefined, application: undefined, endpoint: undefined, environment: undefined, retries: 5, timeout: 3000, keepalive: true }, opts); for (var _i = 0, _arr = ["agent", "application", "apiKey", "endpoint", "environment"]; _i < _arr.length; _i++) { const key = _arr[_i]; if (key in this._opts && this._opts[key] !== undefined) { const value = this._opts[key]; if (typeof value !== "string" || value.length === 0) { if (key === "application") { if (value !== null && typeof value === "object" && "name" in value) { continue; } } throw new Error(`Invalid '${key}' in options argument`); } } else { throw new Error(`Missing '${key}' in options argument`); } } if (typeof this._opts.application === "string") { this._opts.application = { name: this._opts.application, version: 0 }; } this._delay = 50; } getContext(options) { return this.getUnauthed(Object.assign(Object.assign({}, options), { path: "/context", query: { application: (0, utils_1.getApplicationName)(this._opts.application), environment: this._opts.environment } })); } createContext(params, options) { const body = { units: params.units }; return this.post(Object.assign(Object.assign({}, options), { path: "/context", body })); } publish(params, options) { const body = { units: params.units, hashed: params.hashed, publishedAt: params.publishedAt || Date.now() }; if (Array.isArray(params.goals) && params.goals.length > 0) { body.goals = params.goals; } if (Array.isArray(params.exposures) && params.exposures.length > 0) { body.exposures = params.exposures; } if (Array.isArray(params.attributes) && params.attributes.length > 0) { body.attributes = params.attributes; } return this.put(Object.assign(Object.assign({}, options), { path: "/context", body })); } request(options) { var _a, _b; let url = `${this._opts.endpoint}${options.path}`; if (options.query) { const keys = Object.keys(options.query); if (keys.length > 0) { const encoded = keys.map(k => options.query ? `${k}=${encodeURIComponent(options.query[k])}` : null).join("&"); url = `${url}?${encoded}`; } } const controller = new abort_1.AbortController(); const tryOnce = () => { const opts = { method: options.method, body: options.body !== undefined ? JSON.stringify(options.body, null, 0) : undefined, signal: controller.signal, keepalive: this._opts.keepalive }; if (options.auth) { opts.headers = { "Content-Type": "application/json", "X-API-Key": this._opts.apiKey, "X-Agent": this._opts.agent, "X-Environment": this._opts.environment, "X-Application": (0, utils_1.getApplicationName)(this._opts.application), "X-Application-Version": (0, utils_1.getApplicationVersion)(this._opts.application) }; } return (0, fetch_1.default)(url, opts).then(response => { if (!response.ok) { const bail = response.status >= 400 && response.status < 500; return response.text().then(text => { const error = new Error(text !== null && text.length > 0 ? text : response.statusText); error._bail = bail; return Promise.reject(error); }); } return response.json(); }); }; const wait = ms => new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { delete wait.reject; resolve(); }, ms); wait.reject = reason => { clearTimeout(timeoutId); reject(reason); }; }); const tryWith = (retries, timeout, tries = 0, waited = 0) => { delete tryWith.timedout; return tryOnce().catch(reason => { console.warn(reason); if (reason._bail || retries <= 0) { throw new Error(reason.message); } else if (tries >= retries) { throw new errors_1.RetryError(tries, reason, url); } else if (waited >= timeout || reason.name === "AbortError") { if (tryWith.timedout) { throw new errors_1.TimeoutError(timeout); } throw reason; } let delay = (1 << tries) * this._delay + 0.5 * Math.random() * this._delay; if (waited + delay > timeout) { delay = timeout - waited; } return wait(delay).then(() => tryWith(retries, timeout, tries + 1, waited + delay)); }); }; const abort = () => { if (wait.reject) { wait.reject(new errors_1.AbortError()); } else { controller.abort(); } }; if (options.signal) { options.signal.addEventListener("abort", abort); } const timeout = options.timeout || this._opts.timeout || 0; const timeoutId = timeout > 0 ? setTimeout(() => { tryWith.timedout = true; abort(); }, timeout) : 0; const finalCleanUp = () => { clearTimeout(timeoutId); if (options.signal) { options.signal.removeEventListener("abort", abort); } }; return tryWith((_a = this._opts.retries) !== null && _a !== void 0 ? _a : 5, (_b = this._opts.timeout) !== null && _b !== void 0 ? _b : 3000).then(value => { finalCleanUp(); return value; }).catch(error => { finalCleanUp(); throw error; }); } post(options) { return this.request(Object.assign(Object.assign({}, options), { auth: true, method: "POST" })); } put(options) { return this.request(Object.assign(Object.assign({}, options), { auth: true, method: "PUT" })); } getUnauthed(options) { return this.request(Object.assign(Object.assign({}, options), { method: "GET" })); } } exports.default = Client;