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
JavaScript
;
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