UNPKG

@getanthill/datastore

Version:

Event-Sourced Datastore

163 lines 5.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 = {}) { 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 = this._telemetry?.metrics?.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; } getToken() { return this._config.token; } 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) { const [domain] = config.url?.substring(5).split('/', 1); const tic = Date.now(); try { this._telemetry?.metric('datastore_core_request_total', { domain, state: 'request', method: config.method, }); const res = await this._axios.request(config); this._recordHttpRequestDuration?.(Date.now() - tic, { domain, state: 'success', method: config.method, }); this._telemetry?.metric('datastore_core_request_total', { domain, state: 'success', method: config.method, }); return res; } catch (err) { this._recordHttpRequestDuration?.(Date.now() - tic, { domain, state: 'error', method: config.method, status: err.response?.status, }); this._telemetry?.metric('datastore_request', { domain, state: 'error', method: config.method, status: err.response?.status, }); const _err = this.cloneAxiosError(err); this._telemetry?.logger?.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) { const originalRequest = error.config; if (originalRequest === undefined) { throw error; } // @ts-ignore const retry = originalRequest._retry ?? 0; const originalRequestMethod = originalRequest.method ?? 'get'; if (retry < this._config.maxRetry && this._config.retriableErrors.includes(error.message) && this._config.retriableMethods.includes(originalRequestMethod)) { if (typeof originalRequest.params?._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