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 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
- let errorMsg = "";
157
- for (let i = 0; i < retryCount; i++) {
158
- try {
159
- const req = await request("login", { data: cred });
160
- tokens.updateAccessToken(req.susertoken);
161
- return;
162
- } catch (err) {
163
- let errMsg = "Couldn't Refresh the Access Token. ";
164
- if (err instanceof Error) {
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
- errorMsg += `${errMsg}, `;
168
- }
169
- }
170
- throw new Error(
171
- `Attempted to refresh access token ${retryCount} times. but failed. Errors: ${errorMsg}`
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(refreshAccessToken, "refreshAccessToken");
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 = async (code, reason) => {
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
- try {
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
- let lastMin = -1;
957
- this.logger.log("Initialized Infinite Loop");
958
- setInterval(() => {
959
- const date = /* @__PURE__ */ new Date();
960
- const currentMin = date.getUTCMinutes();
961
- const currentHour = date.getUTCHours();
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
- lastMin = -1;
970
- }, 5e4);
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
- let errorMsg = "";
122
- for (let i = 0; i < retryCount; i++) {
123
- try {
124
- const req = await request("login", { data: cred });
125
- tokens.updateAccessToken(req.susertoken);
126
- return;
127
- } catch (err) {
128
- let errMsg = "Couldn't Refresh the Access Token. ";
129
- if (err instanceof Error) {
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
- errorMsg += `${errMsg}, `;
133
- }
134
- }
135
- throw new Error(
136
- `Attempted to refresh access token ${retryCount} times. but failed. Errors: ${errorMsg}`
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(refreshAccessToken, "refreshAccessToken");
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 = async (code, reason) => {
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
- try {
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
- let lastMin = -1;
929
- this.logger.log("Initialized Infinite Loop");
930
- setInterval(() => {
931
- const date = /* @__PURE__ */ new Date();
932
- const currentMin = date.getUTCMinutes();
933
- const currentHour = date.getUTCHours();
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
- lastMin = -1;
942
- }, 5e4);
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shoonya-sdk",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "Wrapper around Shoonya API",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",