@absmartly/javascript-sdk
Version:
A/B Smartly Javascript SDK
209 lines (208 loc) • 6.68 kB
JavaScript
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;
;