UNPKG

tardis-machine

Version:

Locally runnable server with built-in data caching, providing both tick-level historical and consolidated real-time cryptocurrency market data via HTTP and WebSocket APIs

122 lines 4.72 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TardisMachine = void 0; const find_my_way_1 = __importDefault(require("find-my-way")); const http_1 = __importDefault(require("http")); const tardis_dev_1 = require("tardis-dev"); const uWebSockets_js_1 = require("uWebSockets.js"); const http_2 = require("./http"); const ws_1 = require("./ws"); const debug_1 = require("./debug"); const pkg = require('../package.json'); class TardisMachine { options; _httpServer; _wsServer; _eventLoopTimerId = undefined; constructor(options) { this.options = options; (0, tardis_dev_1.init)({ apiKey: options.apiKey, cacheDir: options.cacheDir, _userAgent: `tardis-machine/${pkg.version} (+https://github.com/tardis-dev/tardis-machine)` }); const router = (0, find_my_way_1.default)({ ignoreTrailingSlash: true }); this._httpServer = http_1.default.createServer((req, res) => { router.lookup(req, res); }); // set timeout to 0 meaning infinite http timout - streaming may take some time expecially for longer date ranges this._httpServer.timeout = 0; router.on('GET', '/replay', http_2.replayHttp); router.on('GET', '/replay-normalized', http_2.replayNormalizedHttp); router.on('GET', '/health-check', http_2.healthCheck); const wsRoutes = { '/ws-replay': ws_1.replayWS, '/ws-replay-normalized': ws_1.replayNormalizedWS, '/ws-stream-normalized': ws_1.streamNormalizedWS }; this._wsServer = (0, uWebSockets_js_1.App)().ws('/*', { compression: uWebSockets_js_1.DISABLED, maxPayloadLength: 512 * 1024, idleTimeout: 60, maxBackpressure: 5 * 1024 * 1024, closeOnBackpressureLimit: true, upgrade: (res, req, context) => { res.upgrade({ req }, req.getHeader('sec-websocket-key'), req.getHeader('sec-websocket-protocol'), req.getHeader('sec-websocket-extensions'), context); }, open: (ws) => { const path = ws.req.getUrl().toLocaleLowerCase(); ws.closed = false; const matchingRoute = wsRoutes[path]; if (matchingRoute !== undefined) { matchingRoute(ws, ws.req); } else { ws.end(1008); } }, message: (ws, message) => { if (ws.onmessage !== undefined) { ws.onmessage(message); } }, close: (ws) => { ws.closed = true; if (ws.onclose !== undefined) { ws.onclose(); } } }); } async start(port) { let start = process.hrtime(); const interval = 500; // based on https://github.com/tj/node-blocked/blob/master/index.js this._eventLoopTimerId = setInterval(() => { const delta = process.hrtime(start); const nanosec = delta[0] * 1e9 + delta[1]; const ms = nanosec / 1e6; const n = ms - interval; if (n > 2000) { (0, debug_1.debug)('Tardis-machine server event loop blocked for %d ms.', Math.round(n)); } start = process.hrtime(); }, interval); if (this.options.clearCache) { await (0, tardis_dev_1.clearCache)(); } await new Promise((resolve, reject) => { try { this._httpServer.on('error', reject); this._httpServer.listen(port, () => { this._wsServer.listen(port + 1, (listenSocket) => { if (listenSocket) { resolve(); } else { reject(new Error('ws server did not start')); } }); }); } catch (e) { reject(e); } }); } async stop() { await new Promise((resolve, reject) => { this._httpServer.close((err) => { err ? reject(err) : resolve(); }); }); if (this._eventLoopTimerId !== undefined) { clearInterval(this._eventLoopTimerId); } } } exports.TardisMachine = TardisMachine; //# sourceMappingURL=tardismachine.js.map