@grvt/client 1.5.0-alpha.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/ws/interfaces.d.ts +1 -1
- package/ws/ws.d.ts +12 -2
- package/ws/ws.js +161 -29
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grvt/client",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Node.js & JavaScript client for GRVT REST APIs & WebSockets",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"test": "nodemon dist/index.js"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"axios": "^1.
|
|
36
|
+
"axios": "^1.11.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
package/ws/interfaces.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ECandlestickInterval, ECandlestickType, IWSCandlestickFeedDataV1, IWSCandlestickFeedSelectorV1, IWSDepositFeedDataV1, IWSDepositFeedSelectorV1, IWSFillFeedDataV1, IWSFillFeedSelectorV1, IWSMiniTickerFeedDataV1, IWSMiniTickerFeedSelectorV1, IWSOrderFeedDataV1, IWSOrderFeedSelectorV1, IWSOrderGroupFeedDataV1, IWSOrderStateFeedDataV1, IWSOrderStateFeedSelectorV1, IWSOrderbookLevelsFeedDataV1, IWSOrderbookLevelsFeedSelectorV1, IWSPositionsFeedDataV1, IWSPositionsFeedSelectorV1, IWSTickerFeedDataV1, IWSTickerFeedSelectorV1, IWSTradeFeedDataV1, IWSTradeFeedSelectorV1, IWSTransferFeedDataV1, IWSTransferFeedSelectorV1, IWSWithdrawalFeedDataV1, IWSWithdrawalFeedSelectorV1 } from '../interfaces';
|
|
2
2
|
export declare enum EStream {
|
|
3
3
|
CANDLE = "candle",
|
|
4
4
|
MINI_DELTA = "mini.d",
|
package/ws/ws.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { TWSRequest } from './interfaces';
|
|
2
|
+
export declare class WSError extends Error {
|
|
3
|
+
code?: number;
|
|
4
|
+
status?: number;
|
|
5
|
+
constructor(response: {
|
|
6
|
+
status?: number;
|
|
7
|
+
code?: number;
|
|
8
|
+
message?: string;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
2
11
|
interface IOptions {
|
|
3
12
|
url: string | URL;
|
|
4
13
|
protocols?: string | string[];
|
|
@@ -46,14 +55,15 @@ export declare class WS {
|
|
|
46
55
|
private readonly _onCloses;
|
|
47
56
|
onClose(callback: (e: CloseEvent) => void): () => void;
|
|
48
57
|
private readonly _onErrors;
|
|
49
|
-
onError(callback: (e: Event) => void): () => void;
|
|
58
|
+
onError(callback: (e: Event | WSError) => void): () => void;
|
|
50
59
|
private _subscribeCurrentPairs;
|
|
51
60
|
private _subscribe;
|
|
52
61
|
/**
|
|
53
62
|
* Only supports one feed
|
|
54
63
|
*/
|
|
55
64
|
subscribe(_options: TWSRequest): string;
|
|
56
|
-
|
|
65
|
+
subscribes(...subscriptions: TWSRequest[]): string[];
|
|
66
|
+
unsubscribe(...pairedConsumerKeys: string[]): this;
|
|
57
67
|
connect(): this;
|
|
58
68
|
disconnect(): this;
|
|
59
69
|
ready(delay?: number): Promise<unknown>;
|
package/ws/ws.js
CHANGED
|
@@ -20,7 +20,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
20
20
|
return t;
|
|
21
21
|
};
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.WS = void 0;
|
|
23
|
+
exports.WS = exports.WSError = void 0;
|
|
24
24
|
const ws_candlestick_feed_data_v_1_1 = require("../interfaces/codegen/schema-maps/ws_candlestick_feed_data_v_1");
|
|
25
25
|
const ws_deposit_feed_data_v_1_1 = require("../interfaces/codegen/schema-maps/ws_deposit_feed_data_v_1");
|
|
26
26
|
const ws_fill_feed_data_v_1_1 = require("../interfaces/codegen/schema-maps/ws_fill_feed_data_v_1");
|
|
@@ -36,6 +36,15 @@ const ws_transfer_feed_data_v_1_1 = require("../interfaces/codegen/schema-maps/w
|
|
|
36
36
|
const ws_withdrawal_feed_data_v_1_1 = require("../interfaces/codegen/schema-maps/ws_withdrawal_feed_data_v_1");
|
|
37
37
|
const utils_1 = require("../utils");
|
|
38
38
|
const interfaces_1 = require("./interfaces");
|
|
39
|
+
class WSError extends Error {
|
|
40
|
+
constructor(response) {
|
|
41
|
+
super(response.message);
|
|
42
|
+
this.name = 'WSError';
|
|
43
|
+
this.code = response.code;
|
|
44
|
+
this.status = response.status;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.WSError = WSError;
|
|
39
48
|
const omitZeroStr = (str) => str === '0' ? '' : str;
|
|
40
49
|
class WS {
|
|
41
50
|
get _ws() {
|
|
@@ -140,7 +149,7 @@ class WS {
|
|
|
140
149
|
reconnect();
|
|
141
150
|
});
|
|
142
151
|
currentWs.addEventListener('message', (e) => {
|
|
143
|
-
var _a, _b, _c, _d, _e, _f;
|
|
152
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
144
153
|
// only keep the latest ws
|
|
145
154
|
if (currentWs !== this.__ws) {
|
|
146
155
|
this._close(currentWs);
|
|
@@ -152,17 +161,33 @@ class WS {
|
|
|
152
161
|
}
|
|
153
162
|
const message = utils_1.JsonUtils.parse(e.data);
|
|
154
163
|
/**
|
|
155
|
-
*
|
|
164
|
+
* Handle error response
|
|
156
165
|
*/
|
|
157
|
-
if (message.status
|
|
166
|
+
if ((_b = (_a = message.status) !== null && _a !== void 0 ? _a : message.c) !== null && _b !== void 0 ? _b : message.code) {
|
|
167
|
+
/**
|
|
168
|
+
* Ignore 1003 response "Request could not be processed due to malformed syntax"
|
|
169
|
+
* This happens when client send
|
|
170
|
+
*/
|
|
171
|
+
const code = (_c = message.c) !== null && _c !== void 0 ? _c : message.code;
|
|
172
|
+
if (code !== 1003) {
|
|
173
|
+
const error = new WSError(Object.assign(Object.assign({}, message), { code, message: (_d = message.m) !== null && _d !== void 0 ? _d : message.message }));
|
|
174
|
+
if (this._onErrors.length) {
|
|
175
|
+
for (const onError of this._onErrors) {
|
|
176
|
+
onError(error);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
console.error(error);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
158
183
|
return;
|
|
159
184
|
}
|
|
160
|
-
const stream = message.s = (
|
|
185
|
+
const stream = message.s = (_f = (_e = message.s) === null || _e === void 0 ? void 0 : _e.replace) === null || _f === void 0 ? void 0 : _f.call(_e, `${this._version}.`, '');
|
|
161
186
|
const result = this._messageLiteToFull(message);
|
|
162
187
|
// no entity found
|
|
163
188
|
if (!result) {
|
|
164
189
|
// if no entity found and not a subscription message
|
|
165
|
-
if (!((
|
|
190
|
+
if (!((_g = message.s1) === null || _g === void 0 ? void 0 : _g.length)) {
|
|
166
191
|
console.warn('Error: something went wrong with message', message);
|
|
167
192
|
}
|
|
168
193
|
return;
|
|
@@ -171,7 +196,7 @@ class WS {
|
|
|
171
196
|
console.warn('Error: cannot parse stream or feed from message', message);
|
|
172
197
|
return;
|
|
173
198
|
}
|
|
174
|
-
const instrument = (
|
|
199
|
+
const instrument = (_k = (_j = (_h = result === null || result === void 0 ? void 0 : result.legs) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.instrument) !== null && _k !== void 0 ? _k : result === null || result === void 0 ? void 0 : result.instrument;
|
|
175
200
|
/**
|
|
176
201
|
* Handle subscriptions with instrument
|
|
177
202
|
*/
|
|
@@ -572,30 +597,45 @@ class WS {
|
|
|
572
597
|
this._pairs[pair][consumerKey] = onMessage;
|
|
573
598
|
return `${pair}__${consumerKey}`;
|
|
574
599
|
}
|
|
575
|
-
_removeConsumer(
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
600
|
+
_removeConsumer(...pairedConsumerKeys) {
|
|
601
|
+
const toUnsubscribe = {};
|
|
602
|
+
for (const pairedConsumerKey of pairedConsumerKeys) {
|
|
603
|
+
const [stream, feed, consumerKey] = pairedConsumerKey.split('__');
|
|
604
|
+
const pairKey = this._getPair({ stream, feed });
|
|
605
|
+
if (!this._pairs[pairKey]) {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
let needUnsubscribe = true;
|
|
609
|
+
const pairPrimary = pairKey.split('@')[0];
|
|
610
|
+
for (const key of Object.keys(this._pairs)) {
|
|
611
|
+
if (key.split('@')[0] !== pairPrimary) {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
const primaryGroup = this._pairs[key];
|
|
615
|
+
const _a = primaryGroup, _b = consumerKey, _ = _a[_b], keep = __rest(_a, [typeof _b === "symbol" ? _b : _b + ""]);
|
|
616
|
+
this._pairs[key] = keep;
|
|
617
|
+
if (Object.keys(keep).length) {
|
|
618
|
+
needUnsubscribe = false;
|
|
619
|
+
}
|
|
586
620
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
621
|
+
if (needUnsubscribe) {
|
|
622
|
+
if (!toUnsubscribe[stream]) {
|
|
623
|
+
toUnsubscribe[stream] = [];
|
|
624
|
+
}
|
|
625
|
+
const primaryFeed = feed.split('@')[0];
|
|
626
|
+
if (!toUnsubscribe[stream].some((f) => f.startsWith(`${primaryFeed}@`))) {
|
|
627
|
+
toUnsubscribe[stream].push(feed);
|
|
628
|
+
}
|
|
592
629
|
}
|
|
593
630
|
}
|
|
594
|
-
|
|
631
|
+
/**
|
|
632
|
+
* Send unsubscribes
|
|
633
|
+
*/
|
|
634
|
+
for (const stream of Object.keys(toUnsubscribe)) {
|
|
595
635
|
this._sendMessage({
|
|
596
636
|
method: 'unsubscribe',
|
|
597
637
|
stream,
|
|
598
|
-
feed: [
|
|
638
|
+
feed: toUnsubscribe[stream]
|
|
599
639
|
});
|
|
600
640
|
}
|
|
601
641
|
}
|
|
@@ -634,12 +674,24 @@ class WS {
|
|
|
634
674
|
}
|
|
635
675
|
_subscribeCurrentPairs() {
|
|
636
676
|
const pairs = Object.keys(this._pairs);
|
|
637
|
-
|
|
677
|
+
const groupStreams = {};
|
|
678
|
+
// keep last primary stream
|
|
679
|
+
for (const pair of pairs.reverse()) {
|
|
638
680
|
const { stream, feed } = this._parsePair(pair);
|
|
681
|
+
if (!groupStreams[stream]) {
|
|
682
|
+
groupStreams[stream] = [];
|
|
683
|
+
}
|
|
684
|
+
const primaryFeed = feed.split('@')[0];
|
|
685
|
+
if (!groupStreams[stream].some((f) => f.startsWith(`${primaryFeed}@`))) {
|
|
686
|
+
groupStreams[stream].push(feed);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
const streams = Object.keys(groupStreams);
|
|
690
|
+
for (const stream of streams) {
|
|
639
691
|
this._sendMessage({
|
|
640
692
|
method: 'subscribe',
|
|
641
693
|
stream,
|
|
642
|
-
feed: [
|
|
694
|
+
feed: groupStreams[stream]
|
|
643
695
|
});
|
|
644
696
|
}
|
|
645
697
|
}
|
|
@@ -696,8 +748,88 @@ class WS {
|
|
|
696
748
|
void this._subscribe(pair, subscribePayload).catch(onError);
|
|
697
749
|
return this._addConsumer(pair, onMessage);
|
|
698
750
|
}
|
|
699
|
-
|
|
700
|
-
|
|
751
|
+
subscribes(...subscriptions) {
|
|
752
|
+
var _a;
|
|
753
|
+
if (!subscriptions.length) {
|
|
754
|
+
return [];
|
|
755
|
+
}
|
|
756
|
+
const pairs = [];
|
|
757
|
+
const payloads = [];
|
|
758
|
+
const groupStreams = {};
|
|
759
|
+
for (const subscription of subscriptions) {
|
|
760
|
+
const { onData: onMessage, onError } = subscription, options = __rest(subscription, ["onData", "onError"]);
|
|
761
|
+
const subscribePayload = this._parseStream(options);
|
|
762
|
+
const stream = subscribePayload === null || subscribePayload === void 0 ? void 0 : subscribePayload.stream;
|
|
763
|
+
const feed = (_a = subscribePayload === null || subscribePayload === void 0 ? void 0 : subscribePayload.feed) === null || _a === void 0 ? void 0 : _a[0];
|
|
764
|
+
if (!stream || !feed) {
|
|
765
|
+
throw new Error('Unknown stream or feed');
|
|
766
|
+
}
|
|
767
|
+
if (!groupStreams[stream]) {
|
|
768
|
+
groupStreams[stream] = [];
|
|
769
|
+
}
|
|
770
|
+
const primaryFeed = feed.split('@')[0];
|
|
771
|
+
if (groupStreams[stream].some((f) => f.startsWith(`${primaryFeed}@`))) {
|
|
772
|
+
throw new Error(`Attempted to subscribe to same primary selector twice, in same request: ${primaryFeed}`);
|
|
773
|
+
}
|
|
774
|
+
groupStreams[stream].push(feed);
|
|
775
|
+
payloads.push(subscribePayload);
|
|
776
|
+
pairs.push({
|
|
777
|
+
pair: this._getPair({ stream, feed }),
|
|
778
|
+
onData: onMessage,
|
|
779
|
+
onError
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Subscribe in parallel and listen for responses
|
|
784
|
+
*/
|
|
785
|
+
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
786
|
+
var _b;
|
|
787
|
+
yield this.ready();
|
|
788
|
+
for (const { pair, onError } of pairs) {
|
|
789
|
+
let _resolve;
|
|
790
|
+
const onPaired = (e) => {
|
|
791
|
+
var _a, _b, _c;
|
|
792
|
+
const message = utils_1.JsonUtils.parse(e.data);
|
|
793
|
+
if (!(message === null || message === void 0 ? void 0 : message.s) || !((_a = message === null || message === void 0 ? void 0 : message.s1) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
const responseStream = (_c = (_b = message.s) === null || _b === void 0 ? void 0 : _b.replace) === null || _c === void 0 ? void 0 : _c.call(_b, `${this._version}.`, '');
|
|
797
|
+
const { stream, feed } = this._parsePair(pair);
|
|
798
|
+
const asset = feed.split('@')[0];
|
|
799
|
+
const subs = message.s1;
|
|
800
|
+
const isResolved = stream === responseStream && (subs.includes(asset) ||
|
|
801
|
+
subs.includes(asset.toLowerCase()) ||
|
|
802
|
+
subs.includes(feed) ||
|
|
803
|
+
subs.includes(feed.toLowerCase()));
|
|
804
|
+
if (isResolved) {
|
|
805
|
+
_resolve();
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
const promise = new Promise((resolve) => {
|
|
809
|
+
_resolve = resolve;
|
|
810
|
+
this._ws.addEventListener('message', onPaired);
|
|
811
|
+
});
|
|
812
|
+
utils_1.Utils.timeout(promise, (_b = this._options.timeout) !== null && _b !== void 0 ? _b : 30000, new Error('Subscribe Timeout: ' + pair))
|
|
813
|
+
.catch((error) => onError === null || onError === void 0 ? void 0 : onError(error))
|
|
814
|
+
.finally(() => {
|
|
815
|
+
this._ws.removeEventListener('message', onPaired);
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
const streams = Object.keys(groupStreams);
|
|
819
|
+
for (const stream of streams) {
|
|
820
|
+
this._sendMessage({
|
|
821
|
+
method: 'subscribe',
|
|
822
|
+
stream,
|
|
823
|
+
feed: groupStreams[stream]
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
}));
|
|
827
|
+
return pairs.map(({ pair, onData }) => {
|
|
828
|
+
return this._addConsumer(pair, onData);
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
unsubscribe(...pairedConsumerKeys) {
|
|
832
|
+
this._removeConsumer(...pairedConsumerKeys);
|
|
701
833
|
return this;
|
|
702
834
|
}
|
|
703
835
|
connect() {
|