okotoki
Version:
Okotoki API client for node
241 lines • 8.16 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.coinIndex = exports.candle = exports.leveledTradeVolume = exports.tradeVolume = exports.price = exports.orderBook = exports.largeTrades = void 0;
const avsc_1 = require("avsc");
const debug_1 = __importDefault(require("debug"));
require("isomorphic-fetch");
const isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
const reconnecting_websocket_1 = __importDefault(require("./reconnecting-websocket"));
__exportStar(require("./types"), exports);
const largeTrades = (exchange, symbol, options) => ({
kind: 'largeTrades',
exchange,
symbol,
...options
});
exports.largeTrades = largeTrades;
const orderBook = (exchange, symbol, options) => ({
kind: 'orderBook',
exchange,
symbol,
...options
});
exports.orderBook = orderBook;
const price = (exchange, symbol) => ({
kind: 'price',
exchange,
symbol
});
exports.price = price;
const tradeVolume = (exchange, symbol) => ({
kind: 'tradeVolume',
exchange,
symbol
});
exports.tradeVolume = tradeVolume;
const leveledTradeVolume = (exchange, symbol, options) => ({
kind: 'leveledTradeVolume',
exchange,
symbol,
...options
});
exports.leveledTradeVolume = leveledTradeVolume;
const candle = (exchange, symbol, options) => ({
kind: 'candles',
exchange,
symbol,
...options
});
exports.candle = candle;
const coinIndex = (coin) => ({
kind: 'index',
coin
});
exports.coinIndex = coinIndex;
const defaultOptions = {
key: '',
secret: '',
debug: false,
useBinary: true
};
const defaultWsOptions = {
connectionTimeout: 4000,
WebSocket: isomorphic_ws_1.default,
debug: false,
maxReconnectionDelay: 10000,
minReconnectionDelay: 4000,
maxRetries: Infinity
};
class Api {
options;
wsOptions;
_rws;
_wsUrl = 'wss://api-eu.okotoki.com/ws';
_restUrl = 'https://api-eu.okotoki.com';
pingInterval = null;
binarySchema;
_initiallyConnected = false;
debug = (0, debug_1.default)('okotoki-api');
onMessage = () => { };
onConnectionStateChange = () => { };
constructor(options, wsOptions) {
this.options = options;
this.wsOptions = wsOptions;
this.options = { ...defaultOptions, ...options };
this._wsUrl = this.options.wsUrl || this._wsUrl;
this._restUrl = this.options.restUrl || this._restUrl;
this.debug.enabled = !!this.options.debug;
this.wsOptions = wsOptions
? { ...defaultWsOptions, ...wsOptions }
: defaultWsOptions;
this.connect();
}
getSupportedCoins = () => fetch(`${this._restUrl}/coins`).then((res) => res.json());
getMarketsForCoin = (coin) => fetch(`${this._restUrl}/markets/${coin.toUpperCase()}`).then((res) => res.json());
subscribe(subscriptions) {
this._sendSubscriptionMessage(subscriptions);
}
connect() {
this.debug('estabilishing connection to %s', this._wsUrl);
this._rws = new reconnecting_websocket_1.default(`${this._wsUrl}?useBinary=${this.options?.useBinary === undefined
? defaultOptions.useBinary
: this.options.useBinary}`, undefined, this.wsOptions);
this.onConnectionStateChange('connecting');
this._rws.onopen = this._onConnectionEstabilished;
this._rws.onclose = this._onConnectionClosed;
this._rws.onmessage = this._onMessage;
this._rws.onerror = this._onError;
}
disconnect() {
this.debug('disconnecting from to %s', this._wsUrl);
this.stopPingInterval();
this._rws.close();
}
reconnect() {
this.debug('reconnecting to %s', this._wsUrl);
this.stopPingInterval();
this._rws.reconnect();
}
_preConnectSubscriptionsQueue = [];
_sendSubscriptionMessage(subscriptions) {
if (this._initiallyConnected) {
this._send({
type: 'subscribe',
subscriptions: removeDuplicates(subscriptions)
});
}
else {
this._preConnectSubscriptionsQueue.push(subscriptions);
}
}
_rawBinaryToNormalized = (message) => {
// it is guaranteed that there is only one key
const res = Object.entries(message).map(([key, message]) => {
return {
...message,
type: key
};
});
return res[0];
};
_parseIncomingMessage = async (message) => {
if (message instanceof Blob) {
const buf = await new Response(message).arrayBuffer();
const fromBinary = this.binarySchema?.fromBuffer(Buffer.from(buf));
return this._rawBinaryToNormalized(fromBinary);
}
else if (message instanceof Buffer) {
const fromBinary = this.binarySchema?.fromBuffer(message);
return this._rawBinaryToNormalized(fromBinary);
}
else {
return JSON.parse(message);
}
};
_onMessage = async (event) => {
this._updatePingInterval();
const message = !this.options?.useBinary
? (() => {
const data = JSON.parse(event.data);
const type = Object.keys(data)[0];
return { ...data[type], type };
})()
: await this._parseIncomingMessage(event.data);
this.debug('received message %o', message);
if (message.type === 'binarySchema') {
this.binarySchema = avsc_1.Type.forSchema(message.schema);
return;
}
if (message.type === 'pong')
return;
if (message.type === 'subscribed')
return;
this.onMessage(message);
};
_send = (message) => {
this.debug('sending message %o', message);
this._rws?.send(JSON.stringify(message));
};
_updatePingInterval() {
this.stopPingInterval();
this.pingInterval = setInterval(this._ping, 60 * 1000);
}
stopPingInterval() {
if (!this.pingInterval)
return;
clearInterval(this.pingInterval);
this.pingInterval = null;
}
_ping = () => {
this.debug('sending ping');
this._send({
type: 'ping'
});
};
_auth = () => {
this.debug('sending authorization');
this._send({
type: 'auth',
key: this.options.key,
secret: this.options.secret
});
};
_onError = (error) => {
this.debug('connection error', error);
};
_onConnectionEstabilished = async () => {
this.debug('estabilished connection');
this._updatePingInterval();
this._initiallyConnected = true;
this._auth();
this._sendSubscriptionMessage(this._preConnectSubscriptionsQueue.flat());
this._preConnectSubscriptionsQueue = [];
this.onConnectionStateChange('connected');
};
_onConnectionClosed = () => {
this.debug('connection closed');
this.stopPingInterval();
this._initiallyConnected = false;
this.onConnectionStateChange('disconnected');
};
}
exports.default = Api;
const removeDuplicates = (arr) => arr.filter((item, index) => arr.indexOf(item) === index);
//# sourceMappingURL=index.js.map