UNPKG

@getanthill/datastore

Version:

Event-Sourced Datastore

167 lines 6.83 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const axios_1 = __importDefault(require("axios")); const qs_1 = __importDefault(require("qs")); const merge_1 = __importDefault(require("lodash/merge")); class Core extends events_1.EventEmitter { constructor(config = {}) { var _a, _b; super(); this._config = { baseUrl: 'http://localhost:3001', timeout: 10000, token: 'token', debug: false, retriableMethods: Core.DEFAULT_RETRIABLE_METHODS, retriableErrors: Core.DEFAULT_RETRIABLE_ERRORS, maxRetry: Core.DEFAULT_MAX_RETRY, }; this._telemetry = null; this._config = (0, merge_1.default)({}, this._config, config); this._axios = axios_1.default.create({ baseURL: this._config.baseUrl, timeout: this._config.timeout, withCredentials: true, headers: { ...Core.DEFAULT_HEADERS, Authorization: this._config.token, }, paramsSerializer: { serialize: Core.paramsSerializer, }, }); // @ts-ignore this._axios.interceptors.response.use((response) => response, this.responseInterceptor.bind(this)); if (this._config.telemetry) { this._telemetry = this._config.telemetry; this._recordHttpRequestDuration = (_b = (_a = this._telemetry) === null || _a === void 0 ? void 0 : _a.metrics) === null || _b === void 0 ? void 0 : _b.createHistogram('datastore_core_request_duration_ms', // name 'Duration of the Datastore Core SDK HTTP requests in ms', // description [1, 2, 3, 5, 10, 25, 50, 100, 250, 1000], // buckets 'ms'); } } static paramsSerializer(params) { const p = params; if ('_q' in p) { p._q = JSON.stringify(p._q); } return ('q=' + encodeURIComponent(JSON.stringify(params)) + '&' + qs_1.default.stringify(p, { arrayFormat: 'brackets' })); } cloneAxiosError(err) { const _err = new Error(err.message); _err.name = err.name; // @ts-ignore _err.code = err.code; // @ts-ignore _err.config = err.config; // @ts-ignore _err.response = err.response; // @ts-ignore _err.response && (_err.response.request = undefined); return _err; } setTimeout(timeout) { this._axios.defaults.timeout = timeout; } getPath(...fragments) { return '/' + ['api', ...fragments].join('/'); } inspect(obj) { if (this._config.debug === true) { console.log(require('util').inspect(obj, false, null, true)); } return obj; } async request(config) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; const [domain] = (_a = config.url) === null || _a === void 0 ? void 0 : _a.substring(5).split('/', 1); const tic = Date.now(); try { (_b = this._telemetry) === null || _b === void 0 ? void 0 : _b.metric('datastore_core_request_total', { domain, state: 'request', method: config.method, }); config.headers = { ...config.headers, 'force-primary': (_d = (_c = config.headers) === null || _c === void 0 ? void 0 : _c['force-primary']) !== null && _d !== void 0 ? _d : this._config.forcePrimary, }; const res = await this._axios.request(config); (_e = this._recordHttpRequestDuration) === null || _e === void 0 ? void 0 : _e.call(this, Date.now() - tic, { domain, state: 'success', method: config.method, }); (_f = this._telemetry) === null || _f === void 0 ? void 0 : _f.metric('datastore_core_request_total', { domain, state: 'success', method: config.method, }); return res; } catch (err) { (_g = this._recordHttpRequestDuration) === null || _g === void 0 ? void 0 : _g.call(this, Date.now() - tic, { domain, state: 'error', method: config.method, status: (_h = err.response) === null || _h === void 0 ? void 0 : _h.status, }); (_j = this._telemetry) === null || _j === void 0 ? void 0 : _j.metric('datastore_request', { domain, state: 'error', method: config.method, status: (_k = err.response) === null || _k === void 0 ? void 0 : _k.status, }); const _err = this.cloneAxiosError(err); (_m = (_l = this._telemetry) === null || _l === void 0 ? void 0 : _l.logger) === null || _m === void 0 ? void 0 : _m.warn('[sdk.core#request] Error', { err: _err }); this.inspect(_err); throw _err; } } /** * @warn this interceptor is here to handle race conditions * happening in Node.js 20 with `keepAlive` enabled with parallel * requests. * * @see https://github.com/node-fetch/node-fetch/issues/1735 */ responseInterceptor(error) { var _a, _b, _c; const originalRequest = error.config; if (originalRequest === undefined) { throw error; } // @ts-ignore const retry = (_a = originalRequest._retry) !== null && _a !== void 0 ? _a : 0; const originalRequestMethod = (_b = originalRequest.method) !== null && _b !== void 0 ? _b : 'get'; if (retry < this._config.maxRetry && this._config.retriableErrors.includes(error.message) && this._config.retriableMethods.includes(originalRequestMethod)) { if (typeof ((_c = originalRequest.params) === null || _c === void 0 ? void 0 : _c._q) === 'string') { originalRequest.params._q = JSON.parse(originalRequest.params._q); } // @ts-ignore originalRequest._retry = retry + 1; return this._axios(originalRequest); } throw error; } } Core.ERRORS = {}; Core.DEFAULT_RETRIABLE_METHODS = ['get', 'head', 'options']; Core.DEFAULT_RETRIABLE_ERRORS = ['socket hang up']; Core.DEFAULT_MAX_RETRY = 3; Core.DEFAULT_HEADERS = Object.freeze({ 'Content-Type': 'application/json', Accept: 'application/json', }); exports.default = Core; //# sourceMappingURL=Core.js.map