var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { RestClient: () => RestClient, WebsocketClient: () => WebsocketClient, paths: () => paths }); module.exports = __toCommonJS(src_exports); // src/paths.ts var paths = { login: "QuickAuth", logout: "Logout", forgotPassword: "ForgotPassword", changePassword: "Changepwd", watchlist: "MWList", getWatchlist: "MarketWatch", searchScrip: "SearchScrip", userInfo: "UserDetails", clientInfo: "ClientDetails", quotes: "GetQuotes", addScripToWL: "AddMultiScripsToMW", removeScripFromWL: "DeleteMultiMWScrips", securityInfo: "GetSecurityInfo", placeOrder: "PlaceOrder", modifyOrder: "ModifyOrder", cancelOrder: "CancelOrder", exitSNOOrder: "ExitSNOOrder", historicData: "TPSeries", optionChain: "GetOptionChain", orderBook: "OrderBook", holding: "Holdings", limits: "Limits", singleOrderStatus: "SingleOrdStatus", singleOrderHistory: "SingleOrdHist" }; // src/utils/sha256.ts var import_node_crypto = __toESM(require("crypto"), 1); function sha256(data) { const hash = import_node_crypto.default.createHash("sha256"); return hash.update(data, "utf8").digest("hex"); } __name(sha256, "sha256"); // src/rest-client.ts var import_totp_generator2 = __toESM(require("totp-generator"), 1); // src/token-manager.ts var import_totp_generator = __toESM(require("totp-generator"), 1); var Tokens = class { static { __name(this, "Tokens"); } accessToken; userId; accountId; password; apiKey; vendorCode; imei; source = "API"; twoFactor; constructor() { } updateCred(cred) { this.apiKey = cred.apiKey; this.userId = this.accountId = cred.userId; this.imei = cred.imei || "api"; this.vendorCode = cred.vendorCode || cred.userId + "_U"; this.password = cred.password; this.twoFactor = cred.twoFa; } updateAccessToken(newToken) { this.accessToken = newToken; } getAccessToken() { return this.accessToken; } getCred() { return { userId: this.userId, accountId: this.accountId, password: this.password, apiKey: this.apiKey, vendorCode: this.vendorCode, imei: this.imei, source: this.source, twoFa: this.twoFactor }; } }; var tokens = new Tokens(); async function refreshAccessToken(retryCount = 1) { let errorMsg = ""; for (let i = 0; i < retryCount; i++) { try { logWithDate("trying to fetch the access token"); const token = await getAccessToken(); logWithDate("fetched token successfully"); tokens.updateAccessToken(token); logWithDate("updated token successfully"); return; } catch (errMsg) { errorMsg += `${errMsg}, `; } } throw new Error( `Attempted to refresh access token ${retryCount} times. but failed. Errors: ${errorMsg}` ); } __name(refreshAccessToken, "refreshAccessToken"); function getAccessToken() { const userCred = tokens.getCred(); const cred = { apkversion: "1.0.0", uid: userCred.userId, pwd: sha256(userCred.password), factor2: (0, import_totp_generator.default)(userCred.twoFa), vc: userCred?.vendorCode || userCred.userId + "_U", appkey: sha256(`${userCred.userId}|${userCred.apiKey}`), imei: userCred?.imei || "api", source: "API" }; return new Promise((resolve2, reject) => { const jData = `jData=${JSON.stringify(cred)}`; fetch("https://api.shoonya.com/NorenWClientTP/QuickAuth", { method: "POST", body: jData }).then((res) => res.json()).then((data) => { if (data.stat.toLowerCase() === "ok") { logWithDate("refreshed token successfully"); return resolve2(data.susertoken); } logWithDate("failed to refresh token due to", data.emsg); reject(data.emsg); }).catch((err) => { errorLogWithDate("Failed to refresh token. Error", err); reject(err.message); }); }); } __name(getAccessToken, "getAccessToken"); function logWithDate(...data) { console.log("[", (/* @__PURE__ */ new Date()).toISOString(), "]:", ...data); } __name(logWithDate, "logWithDate"); function errorLogWithDate(...data) { console.error("[", (/* @__PURE__ */ new Date()).toISOString(), "]:", ...data); } __name(errorLogWithDate, "errorLogWithDate"); // src/utils/logger.ts var import_node_path = require("path"); var import_node_fs = require("fs"); var Logger = class { static { __name(this, "Logger"); } logPath = ""; maxSize = 0; fileName = ""; logging; type; /** * * @param logFileName name to prepend the logs file with * @param size max size of file (in bytes) */ constructor(logFileName, size, type = "UNIFIED") { this.logPath = (0, import_node_path.resolve)("./logs", `${logFileName}_${Date.now()}.log`); this.maxSize = size; this.fileName = logFileName; this.type = type; } log(msg, logType = "DEBUG") { if (!this.logging) return; const path = this.checkPathExistance(logType); const content = `${logType}:[${(/* @__PURE__ */ new Date()).toISOString()}]:${msg} `; try { (0, import_node_fs.appendFileSync)(path, content); } catch (err) { console.error(`Failed to write to log file: ${err}`); } try { (0, import_node_fs.existsSync)(this.logPath); this.checkLogSize(path); } catch { console.log("Log file not exists"); } } checkLogSize(path) { const dirName = (0, import_node_path.dirname)(path); try { const stats = (0, import_node_fs.statSync)(path); if (stats.size >= this.maxSize) { this.logPath = (0, import_node_path.resolve)(dirName, `${this.fileName}_${Date.now()}.log`); this.deleteOldestFile(path); } } catch (err) { console.log("File not found"); } } deleteOldestFile(path) { const dirName = (0, import_node_path.dirname)(path); const files = (0, import_node_fs.readdirSync)(dirName); const currentDate = /* @__PURE__ */ new Date(); const sevenDaysAgo = (/* @__PURE__ */ new Date()).setDate(currentDate.getDate() - 7); for (const file of files) { const filePath = (0, import_node_path.resolve)(dirName, file); const stats = (0, import_node_fs.statSync)(filePath); if (stats.ctimeMs < sevenDaysAgo) { (0, import_node_fs.unlinkSync)(filePath); } } } checkPathExistance(type) { const path = this.type === "SEPARATE" ? `${this.logPath.split(".")[0]}_${type.toLowerCase()}.log` : this.logPath; const pathExists = (0, import_node_fs.existsSync)(path); if (!pathExists) { (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(this.logPath), { recursive: true }); } return path; } }; var logger_default = Logger; // src/rest-client.ts var RestClient = class { static { __name(this, "RestClient"); } httpBaseUrl = "https://api.shoonya.com/NorenWClientTP/"; userId = ""; accountId = ""; logger; constructor(cred, params) { const { logging = false } = params || {}; tokens.updateCred(cred); this.userId = this.accountId = cred.userId; this.logger = new logger_default("shoonya_rest-client", 1024 * 1024); this.logger.logging = logging; } async request(path, body) { const needsAuth = !tokens.getAccessToken() && !["login", "logout"].includes(path); if (needsAuth) { const cred = tokens.getCred(); await this.login(cred); body.data.uid = cred.userId; body.data.actid = cred.accountId; if ("key" in body) body.key = tokens.getAccessToken(); } const jData = `jData=${JSON.stringify(body.data)}${body.key ? "&jKey=" + body.key : ""}`; try { const req = await fetch(this.httpBaseUrl + paths[path], { method: "POST", body: jData, keepalive: false }); let data = await req.json(); if ("emsg" in data && data.emsg.includes("Session Expired")) { await this.login(tokens.getCred()); this.logger.log( "|RestClient| Refreshed session token as it was expired" ); const newJData = `jData=${JSON.stringify(body.data)}${body.key ? "&jKey=" + tokens.getAccessToken() : ""}`; const newReq = await fetch(this.httpBaseUrl + paths[path], { method: "POST", body: newJData, keepalive: false }); data = await newReq.json(); } return data; } catch (err) { const errMessage = `Error: ${err?.message} in ${path}`; this.logger.log(errMessage, "ERROR"); console.error(errMessage); } } /** * * @param credentials User Credential (username, password, appkeys etc.) * @returns user information and access token */ async login(rawCred) { const cred = { apkversion: "1.0.0", uid: rawCred.userId, pwd: sha256(rawCred.password), factor2: (0, import_totp_generator2.default)(rawCred.twoFa), vc: rawCred?.vendorCode || rawCred.userId + "_U", appkey: sha256(`${rawCred.userId}|${rawCred.apiKey}`), imei: rawCred?.imei || "api", source: "API" }; try { const req = await this.request("login", { data: cred }); if (req.stat === "Not_Ok") return req; tokens.updateAccessToken(req.susertoken); this.userId = this.accountId = rawCred.userId; return req; } catch (err) { const fallBackMessage = "Attempted to login but it failed"; const errMessage = err?.message || fallBackMessage; this.logger.log(errMessage, "ERROR"); console.error("Login Failed", errMessage); } } /** * @param query scrip name (BankNifty, Sensex etc.) * @param exchange exchange name (NSE, BSE etc.) * @returns */ async searchScrip(query, exchange) { const data = { uid: this.userId, stext: query, exch: exchange }; return this.request("searchScrip", { data, key: tokens.getAccessToken() }); } async getWatchlistsName() { return this.request("watchlist", { data: { uid: this.userId }, key: tokens.getAccessToken() }); } async getWatchlist(listName) { if (!tokens.getAccessToken()) { throw "Login First before accessing watchlists"; } return this.request("getWatchlist", { data: { uid: this.userId, wlname: listName }, key: tokens.getAccessToken() }); } async forgetPassword(PAN, DOB) { const data = { uid: this.userId, pan: PAN, dob: DOB }; return this.request("forgotPassword", { data }); } /** * * @param oldPass sha256 of old pass * @param newPass new password in plain text * @returns */ async changePassword(oldPass, newPass) { const data = { uid: this.userId, oldpwd: oldPass, pwd: newPass }; return this.request("changePassword", { data }); } async getUserDetails() { return this.request("userInfo", { data: { uid: this.userId }, key: tokens.getAccessToken() }); } /** * @param brokerId logged in user's brokers Name / Id * @returns */ async getClientDetails(brokerId) { const data = { uid: this.userId, actid: this.accountId, brkname: brokerId }; return this.request("clientInfo", { data, key: tokens.getAccessToken() }); } async getQuotes(exchange, contractToken) { const data = { uid: this.userId, exch: exchange, token: contractToken }; return this.request("quotes", { data, key: tokens.getAccessToken() }); } /** * * @param listName name of watchlist in which user want to add script * @param scrips list of the scrip * @returns */ async addScripToWatchList(listName, scrips) { const data = { uid: this.userId, wlname: listName, scrips: scrips.join("#") }; return this.request("addScripToWL", { data, key: tokens.getAccessToken() }); } /** * * @param listName name of watchlist in which user want to add script * @param scrips list of the scrip * @returns */ async removeScripFromWatchList(listName, scrips) { const data = { uid: this.userId, wlname: listName, scrips: scrips.join("#") }; return this.request("removeScripFromWL", { data, key: tokens.getAccessToken() }); } async getSecurityInfo(exchange, contractToken) { const data = { uid: this.userId, exch: exchange, token: contractToken }; return this.request("securityInfo", { data, key: tokens.getAccessToken() }); } async placeOrder(details) { const data = { uid: details.uid || this.userId, actid: details.actid || this.accountId, ...details }; return this.request("placeOrder", { data, key: tokens.getAccessToken() }); } async modifyOrder(orderDetail) { orderDetail.uid ??= this.userId; orderDetail.actid ??= this.accountId; return this.request("modifyOrder", { data: orderDetail, key: tokens.getAccessToken() }); } async cancelOrder(orderNo) { return this.request("cancelOrder", { data: { uid: this.userId, norenordno: orderNo }, key: tokens.getAccessToken() }); } /** * * @param orderNo Noren order number, which needs to be modified * @param product Allowed for only H and B products (Cover order and bracket order) * @returns */ async exitSNOOrder(orderNo, product) { const data = { norenordno: orderNo, prd: product, uid: this.userId }; return this.request("exitSNOOrder", { data, key: tokens.getAccessToken() }); } async getHistoricData(option) { const data = { uid: this.userId, ...option }; return this.request("historicData", { data, key: tokens.getAccessToken() }); } async getOptionChain(option) { const data = { uid: this.userId, tsym: encodeURIComponent(option.tradingSymbol), exch: option.exchange, strprc: option.midPrice, cnt: option.count }; return this.request("optionChain", { data, key: tokens.getAccessToken() }); } async orderBook(prd) { const data = { uid: this.userId, prd }; return this.request("orderBook", { data, key: tokens.getAccessToken() }); } /** * Its a custom method which will return token number of option * it can be used to start websocket connection to that tokenƒ * * @param params info about symbol * @returns */ async getOptionToken(params) { const { strikePrice, exchange = "NFO", count = "5", optionExpiry = "current" } = params; const optionType = params.optionType.at(0); const symbol = params.symbol.toUpperCase(); let c = 0; for await (const [ expiry, unformattedDate ] of this.getNextFormattedDates()) { const optionToPick = `${symbol}${expiry}${optionType}${strikePrice}`; const optionChain = await this.getOptionChain({ count, exchange, midPrice: strikePrice.toString(), tradingSymbol: optionToPick }); if (optionChain.stat === "Not_Ok") continue; c++; if (optionExpiry === "next" && c < 2) continue; if (optionExpiry === "next-next" && c < 3) continue; const match = optionExpiry.match(/more-than-(\d+)-day/); if (match) { const dateNow = (/* @__PURE__ */ new Date()).getTime(); const optionExpiryDate = unformattedDate.getTime(); const diff = (optionExpiryDate - dateNow) / (1e3 * 60 * 60 * 24); const check = parseInt(match[1], 10); if (diff < check) continue; } for (const { tsym, token } of optionChain.values) { if (tsym === optionToPick) { return { token: Number(token), exchange, tsym }; } } } return null; } getFormattedExpiry(date) { const stringDate = date.toString(); const [_, month, day, year] = stringDate.split(" "); const newMonth = month.toUpperCase(); const newYear = year.slice(2); return `${day}${newMonth}${newYear}`; } getNextFormattedDates() { const multiplier = 3; let currentDate = /* @__PURE__ */ new Date(); let dates = []; for (let i = 0; i <= 7 * multiplier; i++) { const formattedDate = this.getFormattedExpiry(currentDate); const d = [formattedDate, new Date(currentDate)]; dates.push(d); currentDate.setDate(currentDate.getDate() + 1); } return dates; } /** * Product Name * (Select from ‘prarr’ Array provided in User Details response, and if same is allowed for selected, exchange. * Show product display name, for user to select, and send corresponding prd in API call) * * ``` * `C` : CNC * `M` : NRML * `I` : MIS * `B` : Bracket Order * `H` : Cover Order * ``` */ async getAccountHolding(productType) { const data = { uid: this.userId, actid: this.accountId, prd: productType }; return await this.request("holding", { data, key: tokens.getAccessToken() }); } async getAccountLimits(params) { const data = { uid: this.userId, actid: this.accountId, ...params }; return await this.request("limits", { data, key: tokens.getAccessToken() }); } async getSingleOrderStatus(params) { const data = { uid: this.userId, norenordno: params.orderId.toString(), actid: this.accountId, exch: params.exchange }; return this.request("singleOrderStatus", { data, key: tokens.getAccessToken() }); } async getSingleOrderHistory(orderId) { const data = { uid: this.userId, norenordno: orderId.toString() }; return this.request("singleOrderHistory", { data, key: tokens.getAccessToken() }); } /** returns data that user gave through login method */ getUserData() { return { accessToken: tokens.getAccessToken(), userId: this.userId, accountId: this.accountId }; } }; // src/ws-client.ts var import_ws = require("ws"); var import_events = require("events"); // src/utils/array.utils.ts function checkTwoArrayOverlaps(checkingWith, arr, outputType = "boolean") { if (!arr.length) { return outputType === "array" ? [] : false; } if (!checkingWith.length && arr.length) { return outputType === "array" ? arr : false; } if (arr.length === 1) { return outputType === "array" ? checkingWith.includes(arr[0]) ? [] : arr : checkingWith.includes(arr[0]) ? false : true; } const distinctValues = /* @__PURE__ */ new Set(); for (let j = 0; j < arr.length; j++) { if (outputType === "boolean" && checkingWith.includes(arr[j])) { return true; } if (outputType === "array" && !checkingWith.includes(arr[j])) { distinctValues.add(arr[j]); } } return outputType === "array" ? Array.from(distinctValues) : false; } __name(checkTwoArrayOverlaps, "checkTwoArrayOverlaps"); // src/utils/parser.ts function parseKeysToNumber(obj) { const newObj = {}; for (const key in obj) { if (Number.isNaN(Number(obj[key]))) { newObj[key] = String(obj[key]); } else { newObj[key] = Number(obj[key]); } } return newObj; } __name(parseKeysToNumber, "parseKeysToNumber"); // src/utils/colorful-logs.ts var RESET = "\x1B[0m"; var RED = "\x1B[31m"; var GREEN = "\x1B[32m"; var YELLOW = "\x1B[33m"; var BLUE = "\x1B[34m"; var MAGENTA = "\x1B[35m"; var CYAN = "\x1B[36m"; var WHITE = "\x1B[37m"; var cc = { red(...msg) { const data = msg.join(" "); console.log(RED + data + RESET); }, green(...msg) { const data = msg.join(" "); console.log(GREEN + data + RESET); }, yellow(...msg) { const data = msg.join(" "); console.log(YELLOW + data + RESET); }, blue(...msg) { const data = msg.join(" "); console.log(BLUE + data + RESET); }, magenta(...msg) { const data = msg.join(" "); console.log(MAGENTA + data + RESET); }, cyan(...msg) { const data = msg.join(" "); console.log(CYAN + data + RESET); }, log(...msg) { const data = msg.join(" "); console.log(WHITE + data + RESET); } }; // src/ws-client.ts var WebsocketClient = class extends import_events.EventEmitter { static { __name(this, "WebsocketClient"); } wsBaseUrl = "wss://api.shoonya.com/NorenWSTP/"; ws; subscribedTokens = []; logger; retryCount = 0; maxRetryAttempt; isHeartbeatStarted = false; heartbeatInterval; /** * * @param params * @param cred only compulsory when you are not using RestClient. */ constructor(params) { super(); const { dailyRefreshTime = "09:13+05:30", cred, logging = false, logFileType = "SEPARATE", maxRetryAttempt = 3, heartbeatInterval = 15e3 } = params || {}; this.maxRetryAttempt = maxRetryAttempt; this.heartbeatInterval = heartbeatInterval; this.logger = new logger_default( "shoonya_ws-client", 1024 * 1024 * 10, logFileType ); this.logger.logging ||= logging; const storedCred = tokens.getCred(); const c = storedCred.userId ? storedCred : cred; if (!c) { throw new Error("Couldn't Find the Credentials"); } if (!storedCred.userId) { tokens.updateCred(cred); } if (this.validateTimezoneString(dailyRefreshTime)) { const dt = this.parseDailyRefreshTime(dailyRefreshTime); this.refreshEveryMorning(dt); } else { cc.yellow( "WARNING: DAILY REFRESH TIME STRING IS IN WRONG FORMAT. THIS FEATURE WILL NOT WORK" ); } } connect() { if (this.ws && (this.ws.readyState === this.ws.CONNECTING || this.ws.readyState === this.ws.OPEN)) return; this.ws = new import_ws.WebSocket(this.wsBaseUrl); this.ws.on("message", this.wsMessageEvent); this.ws.on("open", this.wsOpenEvent); this.ws.on("close", this.wsCloseEvent); this.ws.on("error", this.wsErrorEvent); } wsOpenEvent = async () => { if (!tokens.getAccessToken()) { await refreshAccessToken(); } const { userId, accountId } = tokens.getCred(); const msg = { t: "c", uid: userId, actid: accountId, source: "API", susertoken: tokens.getAccessToken() }; this.send(msg); this.emit("open"); }; wsMessageEvent = (msg) => { const result = JSON.parse(msg.toString()); if (result.t === "ck" && result.s.toLowerCase() === "ok") { setTimeout(() => { this.logger.log("|WSClient| Connected with Shoonya WS."); this.sendHeartbeat(this.heartbeatInterval); this.emit("connected"); this.resubscribe(); }, 3e3); } if (result.t === "dk") { this.logger.log(`Subscribed to ${result.ts}.`); this.emit("subscribed", result.ts); } if (result.t === "udk") { this.logger.log(`Unsubscribed to ${result.ts}`); this.emit("unsubscribed", result.ts); } if (result.t === "ok") { this.logger.log("Subscribed to order update"); this.emit("subscribedOrderUpdate"); } if (result.t === "uok") { this.logger.log("Unsubscribed from order update"); this.emit("unsubscribedOrderUpdate"); } if (result.t === "om") { this.emit("orderUpdate", result); } if (result.lp && result.t === "df") { this.retryCount = 0; const parsedResult = parseKeysToNumber( result ); this.emit("priceUpdate", parsedResult); } }; wsErrorEvent = (error) => { console.error("WebSocket error:", error); this.logger.log("|WSClient| Error: " + error.message, "ERROR"); this.emit("error", error); this.ws.close(0); }; wsCloseEvent = (code, reason) => { this.logger.log( `WS Close Event: code: ${code}, reason: ${reason.toString()}` ); if (code === 0) { this.logger.log("Websocket closed gracefully"); return; } if (code === 1) { this.refreshAccessToken(); return; } if (this.retryCount === this.maxRetryAttempt) { this.logger.log("Max retry attempt reached. closed the ws"); this.retryCount = 0; return; } this.retryCount++; this.logger.log( `Disconnected due to ${reason.toString()} with error code ${code}. Attempting to reconnect` ); console.log(`Attempt #${this.retryCount} to reconnect again`); this.connect(); }; subscribe(scrips, feedType = "Depth") { const t = this.validateFeedType(feedType); if (!t) { return cc.red("Feed Type should be either Depth or Touchline"); } const scripList = Array.isArray(scrips) ? scrips : [scrips]; const distinctScrips = checkTwoArrayOverlaps( this.subscribedTokens, scripList, "array" ); const msg = { t: "d", // depth subscription k: distinctScrips.join("#") }; this.subscribedTokens.push(...scripList); this.send(msg); } unsubscribe(scrips) { const scripList = Array.isArray(scrips) ? scrips : [scrips]; const msg = { t: "ud", // unsubcribe depth subscription k: scripList.join("#") }; this.subscribedTokens = this.subscribedTokens.filter( (item) => !scripList.includes(item) ); this.send(msg); } close() { this.ws.close(0); } isWsOpen() { return this.ws.OPEN === this.ws.readyState; } resubscribe() { if (this.subscribedTokens.length) { const tokensToSub = Array.from(new Set(this.subscribedTokens)); this.subscribedTokens = []; this.subscribe(tokensToSub); } } subscribeOrderUpdate() { this.send({ t: "o", actid: tokens.getCred().accountId }); } unsubscribeOrderUpdate() { this.send({ t: "uo" // t: "ud" if "uo" doesn't work since there is some issue in docs }); } send(msg) { if (this.ws && this.ws.readyState === this.ws.OPEN) { this.ws.send(JSON.stringify(msg)); } else { this.logger.log( `didn't send the message ${JSON.stringify( msg )} due to ws connection being closed` ); } } refreshEveryMorning = (dt) => { const { hour, min } = dt; const task = /* @__PURE__ */ __name(() => { this.logger.log(`time to refresh access token`); if (this.isWsOpen()) { this.ws.close(1, "Reconnection"); } else { this.refreshAccessToken(); } const delay2 = this.calculateNextRefreshDelay(hour, min); this.logger.log( `Token will refresh at ${new Date(Date.now() + delay2).toISOString()}` ); setTimeout(task, delay2); }, "task"); const delay = this.calculateNextRefreshDelay(hour, min); this.logger.log( `Token will refresh at ${new Date(Date.now() + delay).toISOString()}` ); setTimeout(task, delay); }; parseDailyRefreshTime(time) { const [offsetType] = time[5]; const [hour, min, offsetHour, offsetMin] = time.split(/[-,+,:]/); let parsedHour = 0; let parsedMin = 0; if (offsetType === "+") { parsedHour = Number(hour) - Number(offsetHour); if (parsedHour < 0) parsedHour += 24; parsedMin = Number(min) - Number(offsetMin); if (parsedMin < 0) { parsedMin += 60; parsedHour -= 1; } } if (offsetType === "-") { parsedHour = Number(hour) + Number(offsetHour); if (parsedHour > 23) parsedHour -= 24; parsedMin = Number(min) + Number(offsetMin); if (parsedMin > 59) { parsedMin -= 60; parsedHour += 1; } } return { hour: parsedHour, min: parsedMin }; } validateTimezoneString = (str) => { const regex = /^(?:[01]\d|2[0-3]):(?:[0-5]\d)[+-](?:[01]\d|2[0-3]):(?:[0-5]\d)$/; if (!regex.test(str)) return false; const [hour, minute, offsetHour, offsetMinute] = str.split(/[:+-]/).map(Number); if (hour < 0 || hour > 23 || minute < 0 || minute > 59 || offsetHour < 0 || offsetHour > 14 || ![0, 30].includes(offsetMinute)) { return false; } return true; }; validateFeedType(feedType) { if (feedType === "Depth" || feedType === "Touchline") { return feedType === "Depth" ? "d" : "t"; } return null; } refreshAccessToken() { this.logger.log("Trying to refresh the Access Token"); refreshAccessToken().then(() => { this.logger.log("Access Token Refreshed"); this.emit("tokenRefresh"); this.connect(); }).catch((err) => { this.logger.log( `Failed to update access token. Err: ${err.message}`, "ERROR" ); console.error("Failed to refresh access token", err); }); } calculateNextRefreshDelay(hour, minute) { const dateNow = /* @__PURE__ */ new Date(); const targetDate = new Date(dateNow); targetDate.setUTCHours(hour, minute, 0, 0); if (targetDate <= dateNow) { targetDate.setUTCDate(targetDate.getUTCDate() + 1); } while ([0, 6].includes(targetDate.getUTCDay())) { targetDate.setUTCDate(targetDate.getUTCDate() + 1); } return targetDate.getTime() - dateNow.getTime(); } sendHeartbeat(interval) { if (this.isHeartbeatStarted) return; this.isHeartbeatStarted = true; setInterval(() => { if (this.isWsOpen()) { this.ws.ping("ping"); } }, interval); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { RestClient, WebsocketClient, paths });