shoonya-sdk 1.1.2 → 1.2.0
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/README.md +1 -0
- package/dist/index.cjs +108 -67
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +108 -67
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,4 +68,5 @@ wsClient.connect();
|
|
|
68
68
|
- Auto Refresh Access Token When it is expired
|
|
69
69
|
- Sync Credentials and Tokens between Rest and WS Clients
|
|
70
70
|
- Reconnect with Shoonya WS at fixed time interval, which is configurable
|
|
71
|
+
- Configurable heartbeat timer to keep connection alive
|
|
71
72
|
- and more...
|
package/dist/index.cjs
CHANGED
|
@@ -77,27 +77,6 @@ var import_totp_generator2 = __toESM(require("totp-generator"), 1);
|
|
|
77
77
|
|
|
78
78
|
// src/token-manager.ts
|
|
79
79
|
var import_totp_generator = __toESM(require("totp-generator"), 1);
|
|
80
|
-
|
|
81
|
-
// src/utils/request.ts
|
|
82
|
-
async function request(path, body) {
|
|
83
|
-
const httpBaseUrl = "https://api.shoonya.com/NorenWClientTP/";
|
|
84
|
-
const jData = `jData=${JSON.stringify(body.data)}${body.key ? "&jKey=" + body.key : ""}`;
|
|
85
|
-
try {
|
|
86
|
-
const req = await fetch(httpBaseUrl + paths[path], {
|
|
87
|
-
method: "POST",
|
|
88
|
-
body: jData,
|
|
89
|
-
keepalive: false
|
|
90
|
-
});
|
|
91
|
-
let data = await req.json();
|
|
92
|
-
return data;
|
|
93
|
-
} catch (err) {
|
|
94
|
-
const errMessage = `Error: ${err?.message} in ${path}`;
|
|
95
|
-
throw new Error(errMessage);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
__name(request, "request");
|
|
99
|
-
|
|
100
|
-
// src/token-manager.ts
|
|
101
80
|
var Tokens = class {
|
|
102
81
|
static {
|
|
103
82
|
__name(this, "Tokens");
|
|
@@ -142,6 +121,25 @@ var Tokens = class {
|
|
|
142
121
|
};
|
|
143
122
|
var tokens = new Tokens();
|
|
144
123
|
async function refreshAccessToken(retryCount = 3) {
|
|
124
|
+
let errorMsg = "";
|
|
125
|
+
for (let i = 0; i < retryCount; i++) {
|
|
126
|
+
try {
|
|
127
|
+
logWithDate("trying to fetch the access token");
|
|
128
|
+
const token = await getAccessToken();
|
|
129
|
+
logWithDate("fetched token successfully");
|
|
130
|
+
tokens.updateAccessToken(token);
|
|
131
|
+
logWithDate("updated token successfully");
|
|
132
|
+
return;
|
|
133
|
+
} catch (errMsg) {
|
|
134
|
+
errorMsg += `${errMsg}, `;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Attempted to refresh access token ${retryCount} times. but failed. Errors: ${errorMsg}`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
__name(refreshAccessToken, "refreshAccessToken");
|
|
142
|
+
function getAccessToken() {
|
|
145
143
|
const userCred = tokens.getCred();
|
|
146
144
|
const cred = {
|
|
147
145
|
apkversion: "1.0.0",
|
|
@@ -153,25 +151,33 @@ async function refreshAccessToken(retryCount = 3) {
|
|
|
153
151
|
imei: userCred?.imei || "api",
|
|
154
152
|
source: "API"
|
|
155
153
|
};
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
errMsg += `Error: ${err.message}`;
|
|
154
|
+
return new Promise((resolve2, reject) => {
|
|
155
|
+
const jData = `jData=${JSON.stringify(cred)}`;
|
|
156
|
+
fetch("https://api.shoonya.com/NorenWClientTP/QuickAuth", {
|
|
157
|
+
method: "POST",
|
|
158
|
+
body: jData
|
|
159
|
+
}).then((res) => res.json()).then((data) => {
|
|
160
|
+
if (data.stat.toLowerCase() === "ok") {
|
|
161
|
+
logWithDate("refreshed token successfully");
|
|
162
|
+
return resolve2(data.susertoken);
|
|
166
163
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
164
|
+
logWithDate("failed to refresh token due to", data.emsg);
|
|
165
|
+
reject(data.emsg);
|
|
166
|
+
}).catch((err) => {
|
|
167
|
+
errorLogWithDate("Failed to refresh token. Error", err);
|
|
168
|
+
reject(err.message);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
173
171
|
}
|
|
174
|
-
__name(
|
|
172
|
+
__name(getAccessToken, "getAccessToken");
|
|
173
|
+
function logWithDate(...data) {
|
|
174
|
+
console.log("[", (/* @__PURE__ */ new Date()).toISOString(), "]:", ...data);
|
|
175
|
+
}
|
|
176
|
+
__name(logWithDate, "logWithDate");
|
|
177
|
+
function errorLogWithDate(...data) {
|
|
178
|
+
console.error("[", (/* @__PURE__ */ new Date()).toISOString(), "]:", ...data);
|
|
179
|
+
}
|
|
180
|
+
__name(errorLogWithDate, "errorLogWithDate");
|
|
175
181
|
|
|
176
182
|
// src/utils/logger.ts
|
|
177
183
|
var import_node_path = require("path");
|
|
@@ -767,6 +773,8 @@ var WebsocketClient = class extends import_events.EventEmitter {
|
|
|
767
773
|
logger;
|
|
768
774
|
retryCount = 0;
|
|
769
775
|
maxRetryAttempt;
|
|
776
|
+
isHeartbeatStarted = false;
|
|
777
|
+
heartbeatInterval;
|
|
770
778
|
/**
|
|
771
779
|
*
|
|
772
780
|
* @param params
|
|
@@ -779,9 +787,11 @@ var WebsocketClient = class extends import_events.EventEmitter {
|
|
|
779
787
|
cred,
|
|
780
788
|
logging = false,
|
|
781
789
|
logFileType = "SEPARATE",
|
|
782
|
-
maxRetryAttempt = 3
|
|
790
|
+
maxRetryAttempt = 3,
|
|
791
|
+
heartbeatInterval = 15e3
|
|
783
792
|
} = params || {};
|
|
784
793
|
this.maxRetryAttempt = maxRetryAttempt;
|
|
794
|
+
this.heartbeatInterval = heartbeatInterval;
|
|
785
795
|
this.logger = new logger_default(
|
|
786
796
|
"shoonya_ws-client",
|
|
787
797
|
1024 * 1024 * 10,
|
|
@@ -834,6 +844,7 @@ var WebsocketClient = class extends import_events.EventEmitter {
|
|
|
834
844
|
if (result.t === "ck" && result.s.toLowerCase() === "ok") {
|
|
835
845
|
setTimeout(() => {
|
|
836
846
|
this.logger.log("|WSClient| Connected with Shoonya WS.");
|
|
847
|
+
this.sendHeartbeat(this.heartbeatInterval);
|
|
837
848
|
this.emit("connected");
|
|
838
849
|
this.resubscribe();
|
|
839
850
|
}, 3e3);
|
|
@@ -863,7 +874,7 @@ var WebsocketClient = class extends import_events.EventEmitter {
|
|
|
863
874
|
this.emit("error", error);
|
|
864
875
|
this.ws.close(0);
|
|
865
876
|
};
|
|
866
|
-
wsCloseEvent =
|
|
877
|
+
wsCloseEvent = (code, reason) => {
|
|
867
878
|
this.logger.log(
|
|
868
879
|
`WS Close Event: code: ${code}, reason: ${reason.toString()}`
|
|
869
880
|
);
|
|
@@ -872,19 +883,7 @@ var WebsocketClient = class extends import_events.EventEmitter {
|
|
|
872
883
|
return;
|
|
873
884
|
}
|
|
874
885
|
if (code === 1) {
|
|
875
|
-
|
|
876
|
-
this.logger.log("Trying to refresh the Access Token");
|
|
877
|
-
await refreshAccessToken();
|
|
878
|
-
this.logger.log("Access Token Refreshed");
|
|
879
|
-
} catch (err) {
|
|
880
|
-
err instanceof Error && this.logger.log(
|
|
881
|
-
`Failed to update access token. Err: ${err.message}`,
|
|
882
|
-
"ERROR"
|
|
883
|
-
);
|
|
884
|
-
console.error("Failed to refresh access token", err);
|
|
885
|
-
}
|
|
886
|
-
this.emit("tokenRefresh");
|
|
887
|
-
this.connect();
|
|
886
|
+
this.refreshAccessToken();
|
|
888
887
|
return;
|
|
889
888
|
}
|
|
890
889
|
if (this.retryCount === this.maxRetryAttempt) {
|
|
@@ -933,6 +932,9 @@ var WebsocketClient = class extends import_events.EventEmitter {
|
|
|
933
932
|
close() {
|
|
934
933
|
this.ws.close(0);
|
|
935
934
|
}
|
|
935
|
+
isWsOpen() {
|
|
936
|
+
return this.ws.OPEN === this.ws.readyState;
|
|
937
|
+
}
|
|
936
938
|
resubscribe() {
|
|
937
939
|
if (this.subscribedTokens.length) {
|
|
938
940
|
const tokensToSub = Array.from(new Set(this.subscribedTokens));
|
|
@@ -953,21 +955,24 @@ var WebsocketClient = class extends import_events.EventEmitter {
|
|
|
953
955
|
}
|
|
954
956
|
refreshEveryMorning = (dt) => {
|
|
955
957
|
const { hour, min } = dt;
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
if (lastMin !== min && hour === currentHour && min === currentMin) {
|
|
963
|
-
this.logger.log(
|
|
964
|
-
`refreshing access token at ${currentHour}:${currentMin} - ${lastMin}`
|
|
965
|
-
);
|
|
966
|
-
lastMin = min;
|
|
967
|
-
return this.ws.close(1, "Reconnection");
|
|
958
|
+
const task = /* @__PURE__ */ __name(() => {
|
|
959
|
+
this.logger.log(`time to refresh access token`);
|
|
960
|
+
if (this.isWsOpen()) {
|
|
961
|
+
this.ws.close(1, "Reconnection");
|
|
962
|
+
} else {
|
|
963
|
+
this.refreshAccessToken();
|
|
968
964
|
}
|
|
969
|
-
|
|
970
|
-
|
|
965
|
+
const delay2 = this.calculateNextRefreshDelay(hour, min);
|
|
966
|
+
this.logger.log(
|
|
967
|
+
`Token will refresh at ${new Date(Date.now() + delay2).toISOString()}`
|
|
968
|
+
);
|
|
969
|
+
setTimeout(task, delay2);
|
|
970
|
+
}, "task");
|
|
971
|
+
const delay = this.calculateNextRefreshDelay(hour, min);
|
|
972
|
+
this.logger.log(
|
|
973
|
+
`Token will refresh at ${new Date(Date.now() + delay).toISOString()}`
|
|
974
|
+
);
|
|
975
|
+
setTimeout(task, delay);
|
|
971
976
|
};
|
|
972
977
|
parseDailyRefreshTime(time) {
|
|
973
978
|
const [offsetType] = time[5];
|
|
@@ -1012,6 +1017,42 @@ var WebsocketClient = class extends import_events.EventEmitter {
|
|
|
1012
1017
|
}
|
|
1013
1018
|
return null;
|
|
1014
1019
|
}
|
|
1020
|
+
refreshAccessToken() {
|
|
1021
|
+
this.logger.log("Trying to refresh the Access Token");
|
|
1022
|
+
refreshAccessToken().then(() => {
|
|
1023
|
+
this.logger.log("Access Token Refreshed");
|
|
1024
|
+
this.emit("tokenRefresh");
|
|
1025
|
+
this.connect();
|
|
1026
|
+
}).catch((err) => {
|
|
1027
|
+
this.logger.log(
|
|
1028
|
+
`Failed to update access token. Err: ${err.message}`,
|
|
1029
|
+
"ERROR"
|
|
1030
|
+
);
|
|
1031
|
+
console.error("Failed to refresh access token", err);
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
calculateNextRefreshDelay(hour, minute) {
|
|
1035
|
+
const dateNow = /* @__PURE__ */ new Date();
|
|
1036
|
+
const targetDate = new Date(dateNow);
|
|
1037
|
+
targetDate.setUTCHours(hour, minute, 0, 0);
|
|
1038
|
+
if (targetDate <= dateNow) {
|
|
1039
|
+
targetDate.setUTCDate(targetDate.getUTCDate() + 1);
|
|
1040
|
+
}
|
|
1041
|
+
while ([0, 6].includes(targetDate.getUTCDay())) {
|
|
1042
|
+
targetDate.setUTCDate(targetDate.getUTCDate() + 1);
|
|
1043
|
+
}
|
|
1044
|
+
return targetDate.getTime() - dateNow.getTime();
|
|
1045
|
+
}
|
|
1046
|
+
sendHeartbeat(interval) {
|
|
1047
|
+
if (this.isHeartbeatStarted)
|
|
1048
|
+
return;
|
|
1049
|
+
this.isHeartbeatStarted = true;
|
|
1050
|
+
setInterval(() => {
|
|
1051
|
+
if (this.isWsOpen()) {
|
|
1052
|
+
this.ws.ping("ping");
|
|
1053
|
+
}
|
|
1054
|
+
}, interval);
|
|
1055
|
+
}
|
|
1015
1056
|
};
|
|
1016
1057
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1017
1058
|
0 && (module.exports = {
|
package/dist/index.d.cts
CHANGED
|
@@ -813,7 +813,13 @@ declare class RestClient {
|
|
|
813
813
|
getHistoricData(option: {
|
|
814
814
|
exch: string;
|
|
815
815
|
token: string;
|
|
816
|
+
/**
|
|
817
|
+
* time in seconds since 1st Jan 1970
|
|
818
|
+
*/
|
|
816
819
|
st?: string;
|
|
820
|
+
/**
|
|
821
|
+
* time in seconds since 1st Jan 1970
|
|
822
|
+
*/
|
|
817
823
|
et?: string;
|
|
818
824
|
}): Promise<HistoricData>;
|
|
819
825
|
getOptionChain(option: {
|
|
@@ -908,6 +914,8 @@ declare class WebsocketClient extends EventEmitter {
|
|
|
908
914
|
private logger;
|
|
909
915
|
private retryCount;
|
|
910
916
|
private maxRetryAttempt;
|
|
917
|
+
private isHeartbeatStarted;
|
|
918
|
+
private heartbeatInterval;
|
|
911
919
|
/**
|
|
912
920
|
*
|
|
913
921
|
* @param params
|
|
@@ -920,6 +928,7 @@ declare class WebsocketClient extends EventEmitter {
|
|
|
920
928
|
cred?: UserCred;
|
|
921
929
|
logFileType?: LoggerType;
|
|
922
930
|
maxRetryAttempt?: number;
|
|
931
|
+
heartbeatInterval?: number;
|
|
923
932
|
});
|
|
924
933
|
connect(): void;
|
|
925
934
|
private wsOpenEvent;
|
|
@@ -929,12 +938,16 @@ declare class WebsocketClient extends EventEmitter {
|
|
|
929
938
|
subscribe(scrips: string | string[], feedType?: FeedType): void;
|
|
930
939
|
unsubscribe(scrips: string | string[]): void;
|
|
931
940
|
close(): void;
|
|
941
|
+
isWsOpen(): boolean;
|
|
932
942
|
private resubscribe;
|
|
933
943
|
private send;
|
|
934
944
|
private refreshEveryMorning;
|
|
935
945
|
private parseDailyRefreshTime;
|
|
936
946
|
private validateTimezoneString;
|
|
937
947
|
private validateFeedType;
|
|
948
|
+
private refreshAccessToken;
|
|
949
|
+
private calculateNextRefreshDelay;
|
|
950
|
+
private sendHeartbeat;
|
|
938
951
|
}
|
|
939
952
|
|
|
940
953
|
export { AddScripToWatchlist, BaseType, BaseTypeFail, BaseTypeSuccess, BaseTypeWithoutTime, BasketMargin, CancelOrder, ChangePassword, DailyTimeString, DepthSubscriptionUpdate, ExchMessage, Exchange, ExitSNOOrder, FeedType, ForgotPassword, GetClientDetails, GetHSToken, GetLimitsParam, GetListOfPredefinedMW, GetListOfPredefinedMWScrip, GetSecurityInfo, GetTokenExpiry, GetUserDetails, GetWatchlist, GetWatchlistNames, HistoricData, Holdings, IndexList, Limits, Login, LoginWithDevicePin, LogoutResponse, MakeKeysRequired, ModifyOrder, OptionChain, Order, OrderBook, OrderInput, OrderMargin, Path, PlaceOrder, PositionBook, ProductConversion, ProductType, RemoveScripFromWatchlist, RestClient, ScripInfo, SearchScrip, Segment, SetDevicePin, SingleOrderHistory, SingleOrderStatus, TopIndexList, TopIndexListNames, TradeBook, UserCred, ValidateHSToken, WebsocketClient, getQuotes, paths };
|
package/dist/index.d.ts
CHANGED
|
@@ -813,7 +813,13 @@ declare class RestClient {
|
|
|
813
813
|
getHistoricData(option: {
|
|
814
814
|
exch: string;
|
|
815
815
|
token: string;
|
|
816
|
+
/**
|
|
817
|
+
* time in seconds since 1st Jan 1970
|
|
818
|
+
*/
|
|
816
819
|
st?: string;
|
|
820
|
+
/**
|
|
821
|
+
* time in seconds since 1st Jan 1970
|
|
822
|
+
*/
|
|
817
823
|
et?: string;
|
|
818
824
|
}): Promise<HistoricData>;
|
|
819
825
|
getOptionChain(option: {
|
|
@@ -908,6 +914,8 @@ declare class WebsocketClient extends EventEmitter {
|
|
|
908
914
|
private logger;
|
|
909
915
|
private retryCount;
|
|
910
916
|
private maxRetryAttempt;
|
|
917
|
+
private isHeartbeatStarted;
|
|
918
|
+
private heartbeatInterval;
|
|
911
919
|
/**
|
|
912
920
|
*
|
|
913
921
|
* @param params
|
|
@@ -920,6 +928,7 @@ declare class WebsocketClient extends EventEmitter {
|
|
|
920
928
|
cred?: UserCred;
|
|
921
929
|
logFileType?: LoggerType;
|
|
922
930
|
maxRetryAttempt?: number;
|
|
931
|
+
heartbeatInterval?: number;
|
|
923
932
|
});
|
|
924
933
|
connect(): void;
|
|
925
934
|
private wsOpenEvent;
|
|
@@ -929,12 +938,16 @@ declare class WebsocketClient extends EventEmitter {
|
|
|
929
938
|
subscribe(scrips: string | string[], feedType?: FeedType): void;
|
|
930
939
|
unsubscribe(scrips: string | string[]): void;
|
|
931
940
|
close(): void;
|
|
941
|
+
isWsOpen(): boolean;
|
|
932
942
|
private resubscribe;
|
|
933
943
|
private send;
|
|
934
944
|
private refreshEveryMorning;
|
|
935
945
|
private parseDailyRefreshTime;
|
|
936
946
|
private validateTimezoneString;
|
|
937
947
|
private validateFeedType;
|
|
948
|
+
private refreshAccessToken;
|
|
949
|
+
private calculateNextRefreshDelay;
|
|
950
|
+
private sendHeartbeat;
|
|
938
951
|
}
|
|
939
952
|
|
|
940
953
|
export { AddScripToWatchlist, BaseType, BaseTypeFail, BaseTypeSuccess, BaseTypeWithoutTime, BasketMargin, CancelOrder, ChangePassword, DailyTimeString, DepthSubscriptionUpdate, ExchMessage, Exchange, ExitSNOOrder, FeedType, ForgotPassword, GetClientDetails, GetHSToken, GetLimitsParam, GetListOfPredefinedMW, GetListOfPredefinedMWScrip, GetSecurityInfo, GetTokenExpiry, GetUserDetails, GetWatchlist, GetWatchlistNames, HistoricData, Holdings, IndexList, Limits, Login, LoginWithDevicePin, LogoutResponse, MakeKeysRequired, ModifyOrder, OptionChain, Order, OrderBook, OrderInput, OrderMargin, Path, PlaceOrder, PositionBook, ProductConversion, ProductType, RemoveScripFromWatchlist, RestClient, ScripInfo, SearchScrip, Segment, SetDevicePin, SingleOrderHistory, SingleOrderStatus, TopIndexList, TopIndexListNames, TradeBook, UserCred, ValidateHSToken, WebsocketClient, getQuotes, paths };
|
package/dist/index.js
CHANGED
|
@@ -42,27 +42,6 @@ import totp2 from "totp-generator";
|
|
|
42
42
|
|
|
43
43
|
// src/token-manager.ts
|
|
44
44
|
import totp from "totp-generator";
|
|
45
|
-
|
|
46
|
-
// src/utils/request.ts
|
|
47
|
-
async function request(path, body) {
|
|
48
|
-
const httpBaseUrl = "https://api.shoonya.com/NorenWClientTP/";
|
|
49
|
-
const jData = `jData=${JSON.stringify(body.data)}${body.key ? "&jKey=" + body.key : ""}`;
|
|
50
|
-
try {
|
|
51
|
-
const req = await fetch(httpBaseUrl + paths[path], {
|
|
52
|
-
method: "POST",
|
|
53
|
-
body: jData,
|
|
54
|
-
keepalive: false
|
|
55
|
-
});
|
|
56
|
-
let data = await req.json();
|
|
57
|
-
return data;
|
|
58
|
-
} catch (err) {
|
|
59
|
-
const errMessage = `Error: ${err?.message} in ${path}`;
|
|
60
|
-
throw new Error(errMessage);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
__name(request, "request");
|
|
64
|
-
|
|
65
|
-
// src/token-manager.ts
|
|
66
45
|
var Tokens = class {
|
|
67
46
|
static {
|
|
68
47
|
__name(this, "Tokens");
|
|
@@ -107,6 +86,25 @@ var Tokens = class {
|
|
|
107
86
|
};
|
|
108
87
|
var tokens = new Tokens();
|
|
109
88
|
async function refreshAccessToken(retryCount = 3) {
|
|
89
|
+
let errorMsg = "";
|
|
90
|
+
for (let i = 0; i < retryCount; i++) {
|
|
91
|
+
try {
|
|
92
|
+
logWithDate("trying to fetch the access token");
|
|
93
|
+
const token = await getAccessToken();
|
|
94
|
+
logWithDate("fetched token successfully");
|
|
95
|
+
tokens.updateAccessToken(token);
|
|
96
|
+
logWithDate("updated token successfully");
|
|
97
|
+
return;
|
|
98
|
+
} catch (errMsg) {
|
|
99
|
+
errorMsg += `${errMsg}, `;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Attempted to refresh access token ${retryCount} times. but failed. Errors: ${errorMsg}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
__name(refreshAccessToken, "refreshAccessToken");
|
|
107
|
+
function getAccessToken() {
|
|
110
108
|
const userCred = tokens.getCred();
|
|
111
109
|
const cred = {
|
|
112
110
|
apkversion: "1.0.0",
|
|
@@ -118,25 +116,33 @@ async function refreshAccessToken(retryCount = 3) {
|
|
|
118
116
|
imei: userCred?.imei || "api",
|
|
119
117
|
source: "API"
|
|
120
118
|
};
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
errMsg += `Error: ${err.message}`;
|
|
119
|
+
return new Promise((resolve2, reject) => {
|
|
120
|
+
const jData = `jData=${JSON.stringify(cred)}`;
|
|
121
|
+
fetch("https://api.shoonya.com/NorenWClientTP/QuickAuth", {
|
|
122
|
+
method: "POST",
|
|
123
|
+
body: jData
|
|
124
|
+
}).then((res) => res.json()).then((data) => {
|
|
125
|
+
if (data.stat.toLowerCase() === "ok") {
|
|
126
|
+
logWithDate("refreshed token successfully");
|
|
127
|
+
return resolve2(data.susertoken);
|
|
131
128
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
129
|
+
logWithDate("failed to refresh token due to", data.emsg);
|
|
130
|
+
reject(data.emsg);
|
|
131
|
+
}).catch((err) => {
|
|
132
|
+
errorLogWithDate("Failed to refresh token. Error", err);
|
|
133
|
+
reject(err.message);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
138
136
|
}
|
|
139
|
-
__name(
|
|
137
|
+
__name(getAccessToken, "getAccessToken");
|
|
138
|
+
function logWithDate(...data) {
|
|
139
|
+
console.log("[", (/* @__PURE__ */ new Date()).toISOString(), "]:", ...data);
|
|
140
|
+
}
|
|
141
|
+
__name(logWithDate, "logWithDate");
|
|
142
|
+
function errorLogWithDate(...data) {
|
|
143
|
+
console.error("[", (/* @__PURE__ */ new Date()).toISOString(), "]:", ...data);
|
|
144
|
+
}
|
|
145
|
+
__name(errorLogWithDate, "errorLogWithDate");
|
|
140
146
|
|
|
141
147
|
// src/utils/logger.ts
|
|
142
148
|
import { resolve, dirname } from "node:path";
|
|
@@ -739,6 +745,8 @@ var WebsocketClient = class extends EventEmitter {
|
|
|
739
745
|
logger;
|
|
740
746
|
retryCount = 0;
|
|
741
747
|
maxRetryAttempt;
|
|
748
|
+
isHeartbeatStarted = false;
|
|
749
|
+
heartbeatInterval;
|
|
742
750
|
/**
|
|
743
751
|
*
|
|
744
752
|
* @param params
|
|
@@ -751,9 +759,11 @@ var WebsocketClient = class extends EventEmitter {
|
|
|
751
759
|
cred,
|
|
752
760
|
logging = false,
|
|
753
761
|
logFileType = "SEPARATE",
|
|
754
|
-
maxRetryAttempt = 3
|
|
762
|
+
maxRetryAttempt = 3,
|
|
763
|
+
heartbeatInterval = 15e3
|
|
755
764
|
} = params || {};
|
|
756
765
|
this.maxRetryAttempt = maxRetryAttempt;
|
|
766
|
+
this.heartbeatInterval = heartbeatInterval;
|
|
757
767
|
this.logger = new logger_default(
|
|
758
768
|
"shoonya_ws-client",
|
|
759
769
|
1024 * 1024 * 10,
|
|
@@ -806,6 +816,7 @@ var WebsocketClient = class extends EventEmitter {
|
|
|
806
816
|
if (result.t === "ck" && result.s.toLowerCase() === "ok") {
|
|
807
817
|
setTimeout(() => {
|
|
808
818
|
this.logger.log("|WSClient| Connected with Shoonya WS.");
|
|
819
|
+
this.sendHeartbeat(this.heartbeatInterval);
|
|
809
820
|
this.emit("connected");
|
|
810
821
|
this.resubscribe();
|
|
811
822
|
}, 3e3);
|
|
@@ -835,7 +846,7 @@ var WebsocketClient = class extends EventEmitter {
|
|
|
835
846
|
this.emit("error", error);
|
|
836
847
|
this.ws.close(0);
|
|
837
848
|
};
|
|
838
|
-
wsCloseEvent =
|
|
849
|
+
wsCloseEvent = (code, reason) => {
|
|
839
850
|
this.logger.log(
|
|
840
851
|
`WS Close Event: code: ${code}, reason: ${reason.toString()}`
|
|
841
852
|
);
|
|
@@ -844,19 +855,7 @@ var WebsocketClient = class extends EventEmitter {
|
|
|
844
855
|
return;
|
|
845
856
|
}
|
|
846
857
|
if (code === 1) {
|
|
847
|
-
|
|
848
|
-
this.logger.log("Trying to refresh the Access Token");
|
|
849
|
-
await refreshAccessToken();
|
|
850
|
-
this.logger.log("Access Token Refreshed");
|
|
851
|
-
} catch (err) {
|
|
852
|
-
err instanceof Error && this.logger.log(
|
|
853
|
-
`Failed to update access token. Err: ${err.message}`,
|
|
854
|
-
"ERROR"
|
|
855
|
-
);
|
|
856
|
-
console.error("Failed to refresh access token", err);
|
|
857
|
-
}
|
|
858
|
-
this.emit("tokenRefresh");
|
|
859
|
-
this.connect();
|
|
858
|
+
this.refreshAccessToken();
|
|
860
859
|
return;
|
|
861
860
|
}
|
|
862
861
|
if (this.retryCount === this.maxRetryAttempt) {
|
|
@@ -905,6 +904,9 @@ var WebsocketClient = class extends EventEmitter {
|
|
|
905
904
|
close() {
|
|
906
905
|
this.ws.close(0);
|
|
907
906
|
}
|
|
907
|
+
isWsOpen() {
|
|
908
|
+
return this.ws.OPEN === this.ws.readyState;
|
|
909
|
+
}
|
|
908
910
|
resubscribe() {
|
|
909
911
|
if (this.subscribedTokens.length) {
|
|
910
912
|
const tokensToSub = Array.from(new Set(this.subscribedTokens));
|
|
@@ -925,21 +927,24 @@ var WebsocketClient = class extends EventEmitter {
|
|
|
925
927
|
}
|
|
926
928
|
refreshEveryMorning = (dt) => {
|
|
927
929
|
const { hour, min } = dt;
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
if (lastMin !== min && hour === currentHour && min === currentMin) {
|
|
935
|
-
this.logger.log(
|
|
936
|
-
`refreshing access token at ${currentHour}:${currentMin} - ${lastMin}`
|
|
937
|
-
);
|
|
938
|
-
lastMin = min;
|
|
939
|
-
return this.ws.close(1, "Reconnection");
|
|
930
|
+
const task = /* @__PURE__ */ __name(() => {
|
|
931
|
+
this.logger.log(`time to refresh access token`);
|
|
932
|
+
if (this.isWsOpen()) {
|
|
933
|
+
this.ws.close(1, "Reconnection");
|
|
934
|
+
} else {
|
|
935
|
+
this.refreshAccessToken();
|
|
940
936
|
}
|
|
941
|
-
|
|
942
|
-
|
|
937
|
+
const delay2 = this.calculateNextRefreshDelay(hour, min);
|
|
938
|
+
this.logger.log(
|
|
939
|
+
`Token will refresh at ${new Date(Date.now() + delay2).toISOString()}`
|
|
940
|
+
);
|
|
941
|
+
setTimeout(task, delay2);
|
|
942
|
+
}, "task");
|
|
943
|
+
const delay = this.calculateNextRefreshDelay(hour, min);
|
|
944
|
+
this.logger.log(
|
|
945
|
+
`Token will refresh at ${new Date(Date.now() + delay).toISOString()}`
|
|
946
|
+
);
|
|
947
|
+
setTimeout(task, delay);
|
|
943
948
|
};
|
|
944
949
|
parseDailyRefreshTime(time) {
|
|
945
950
|
const [offsetType] = time[5];
|
|
@@ -984,6 +989,42 @@ var WebsocketClient = class extends EventEmitter {
|
|
|
984
989
|
}
|
|
985
990
|
return null;
|
|
986
991
|
}
|
|
992
|
+
refreshAccessToken() {
|
|
993
|
+
this.logger.log("Trying to refresh the Access Token");
|
|
994
|
+
refreshAccessToken().then(() => {
|
|
995
|
+
this.logger.log("Access Token Refreshed");
|
|
996
|
+
this.emit("tokenRefresh");
|
|
997
|
+
this.connect();
|
|
998
|
+
}).catch((err) => {
|
|
999
|
+
this.logger.log(
|
|
1000
|
+
`Failed to update access token. Err: ${err.message}`,
|
|
1001
|
+
"ERROR"
|
|
1002
|
+
);
|
|
1003
|
+
console.error("Failed to refresh access token", err);
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
calculateNextRefreshDelay(hour, minute) {
|
|
1007
|
+
const dateNow = /* @__PURE__ */ new Date();
|
|
1008
|
+
const targetDate = new Date(dateNow);
|
|
1009
|
+
targetDate.setUTCHours(hour, minute, 0, 0);
|
|
1010
|
+
if (targetDate <= dateNow) {
|
|
1011
|
+
targetDate.setUTCDate(targetDate.getUTCDate() + 1);
|
|
1012
|
+
}
|
|
1013
|
+
while ([0, 6].includes(targetDate.getUTCDay())) {
|
|
1014
|
+
targetDate.setUTCDate(targetDate.getUTCDate() + 1);
|
|
1015
|
+
}
|
|
1016
|
+
return targetDate.getTime() - dateNow.getTime();
|
|
1017
|
+
}
|
|
1018
|
+
sendHeartbeat(interval) {
|
|
1019
|
+
if (this.isHeartbeatStarted)
|
|
1020
|
+
return;
|
|
1021
|
+
this.isHeartbeatStarted = true;
|
|
1022
|
+
setInterval(() => {
|
|
1023
|
+
if (this.isWsOpen()) {
|
|
1024
|
+
this.ws.ping("ping");
|
|
1025
|
+
}
|
|
1026
|
+
}, interval);
|
|
1027
|
+
}
|
|
987
1028
|
};
|
|
988
1029
|
export {
|
|
989
1030
|
RestClient,
|