From 62aad4e2b8a54a468e87a15bd9104ce4015c4c57 Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Mon, 13 Nov 2023 17:31:18 +0000 Subject: [PATCH 01/10] feat(): add V2 REST client with initial endpoints --- src/rest-client-v2.ts | 890 +++++++++++++++++++++++++++++++++++ src/spot-client.ts | 3 +- src/types/request/shared.ts | 5 + src/types/response/shared.ts | 4 +- src/util/requestUtils.ts | 1 + src/util/type-guards.ts | 11 + src/util/websocket-util.ts | 29 +- 7 files changed, 938 insertions(+), 5 deletions(-) create mode 100644 src/rest-client-v2.ts diff --git a/src/rest-client-v2.ts b/src/rest-client-v2.ts new file mode 100644 index 0000000..5b3232f --- /dev/null +++ b/src/rest-client-v2.ts @@ -0,0 +1,890 @@ +import { + NewBatchSpotOrder, + NewSpotOrder, + NewWalletTransfer, + Pagination, + APIResponse, + CoinBalance, + SymbolRules, + NewSpotSubTransfer, + NewSpotWithdraw, + CancelSpotOrderV2, + BatchCancelSpotOrderV2, + SpotOrderResult, + NewSpotPlanOrder, + ModifySpotPlanOrder, + CancelSpotPlanOrderParams, + GetSpotPlanOrdersParams, + SpotPlanOrder, + GetHistoricPlanOrdersParams, + SpotMarketTrade, + GetHistoricTradesParams, + VIPFeeRate, + SpotKlineInterval, + MarginType, +} from './types'; +import { REST_CLIENT_TYPE_ENUM, assertMarginType } from './util'; +import BaseRestClient from './util/BaseRestClient'; + +/** + * REST API client for all V2 endpoints + */ +export class RestClientV2 extends BaseRestClient { + getClientType() { + return REST_CLIENT_TYPE_ENUM.v2; + } + + async fetchServerTime(): Promise { + const res = await this.getServerTime(); + return Number(res.data); + } + + /** + * + * + * Common + * + * + */ + + /** + * + * * Common | Notice + * + */ + + getAnnouncements(): Promise> { + return this.get(`/api/v2/public/annoucements`); + } + + /** + * + * * Common | Public + * + */ + + getServerTime(): Promise> { + return this.get(`/api/v2/public/time`); + } + + getTradeRate(params: object): Promise> { + return this.getPrivate(`/api/v2/common/trade-rate`, params); + } + + /** + * + * * Common | Tax + * + */ + + getSpotTransactionRecords(params: object): Promise> { + return this.getPrivate(`/api/v2/tax/spot-record`, params); + } + + getFuturesTransactionRecords(params: object): Promise> { + return this.getPrivate(`/api/v2/tax/future-record`, params); + } + + getMarginTransactionRecords(params: object): Promise> { + return this.getPrivate(`/api/v2/tax/margin-record`, params); + } + + getP2PTransactionRecords(params: object): Promise> { + return this.getPrivate(`/api/v2/tax/p2p-record`, params); + } + + /** + * + * * Common | P2P + * + */ + + getP2PMerchantList(params?: object): Promise> { + return this.getPrivate(`/api/v2/p2p/merchantList`, params); + } + + getP2PMerchantInfo(): Promise> { + return this.getPrivate(`/api/v2/p2p/merchantInfo`); + } + + getP2PMerchantOrders(params: object): Promise> { + return this.getPrivate(`/api/v2/p2p/orderList`, params); + } + + getP2PMerchantAdvertisementList(params: object): Promise> { + return this.getPrivate(`/api/v2/p2p/advList`, params); + } + + /** + * + * * Common | Virtual Subaccount + * + */ + + createVirtualSubaccount(params: object): Promise> { + return this.postPrivate(`/api/v2/user/create-virtual-subaccount`, params); + } + + modifyVirtualSubaccount(params: object): Promise> { + return this.postPrivate(`/api/v2/user/modify-virtual-subaccount`, params); + } + + batchCreateVirtualSubaccountAndAPIKey( + params: object, + ): Promise> { + return this.postPrivate( + '/api/v2/user/batch-create-subaccount-and-apikey', + params, + ); + } + + getVirtualSubaccounts(params?: object): Promise> { + return this.getPrivate(`/api/v2/user/virtual-subaccount-list`, params); + } + + createVirtualSubaccountAPIKey(params: object): Promise> { + return this.postPrivate( + '/api/v2/user/create-virtual-subaccount-apikey', + params, + ); + } + + modifyVirtualSubaccountAPIKey(params: object): Promise> { + return this.postPrivate( + '/api/v2/user/modify-virtual-subaccount-apikey', + params, + ); + } + + getVirtualSubaccountAPIKeys(params: object): Promise> { + return this.getPrivate( + '/api/v2/user/virtual-subaccount-apikey-list', + params, + ); + } + + /** + * + * * Common | Convert + * + */ + + getConvertCoins(): Promise> { + return this.getPrivate(`/api/v2/convert/currencies`); + } + + getConvertQuotedPrice(params: object): Promise> { + return this.getPrivate(`/api/v2/convert/quoted-price`, params); + } + + convert(params: object): Promise> { + return this.postPrivate(`/api/v2/convert/trade`, params); + } + + getConvertHistory(params: object): Promise> { + return this.getPrivate(`/api/v2/convert/convert-record`, params); + } + + /** + * + * + * Spot + * + * + */ + + /** + * + * * Spot | Market + * + */ + + getSpotCoinInfo(params?: object): Promise> { + return this.getPrivate(`/api/v2/spot/public/coins`, params); + } + + getSpotSymbolInfo(params?: object): Promise> { + return this.getPrivate(`/api/v2/spot/public/symbols`, params); + } + + getSpotVIPFeeRate(): Promise> { + return this.getPrivate(`/api/v2/spot/market/vip-fee-rate`); + } + + getSpotTicker(params?: object): Promise> { + return this.getPrivate(`/api/v2/spot/market/tickers`, params); + } + + getSpotMergeDepth(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/market/merge-depth`, params); + } + + getSpotOrderBookDepth(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/market/orderbook`, params); + } + + getSpotCandlestickData(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/market/candles`, params); + } + + getSpotHistoricCandlestickData(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/market/history-candles`, params); + } + + getSpotRecentTrades(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/market/fills`, params); + } + + getSpotHistoricTrades(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/market/fills-history`, params); + } + + /** + * + * * Spot | Trade + * + */ + + spotSubmitOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/trade/place-order`, params); + } + + spotCancelOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/trade/cancel-order`, params); + } + + spotBatchSubmitOrders(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/trade/batch-orders`, params); + } + + spotBatchCancelOrders(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/trade/batch-cancel-order`, params); + } + + spotCancelSymbolOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/trade/cancel-symbol-order`, params); + } + + getSpotOrder(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/trade/orderInfo`, params); + } + + getSpotOpenOrders(params?: object): Promise> { + return this.getPrivate(`/api/v2/spot/trade/unfilled-orders`, params); + } + + getSpotHistoricOrders(params?: object): Promise> { + return this.getPrivate(`/api/v2/spot/trade/history-orders`, params); + } + + getSpotFills(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/trade/fills`, params); + } + + /** + * + * * Spot | Trigger Orders + * + */ + + spotSubmitPlanOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/trade/place-plan-order`, params); + } + + spotModifyPlanOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/trade/modify-plan-order`, params); + } + + spotCancelPlanOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/trade/cancel-plan-order`, params); + } + + getSpotCurrentPlanOrders(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/trade/current-plan-order`, params); + } + + getSpotHistoricPlanOrders(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/trade/history-plan-order`, params); + } + + spotCancelPlanOrders(params?: object): Promise> { + return this.postPrivate( + '/api/v2/spot/trade/batch-cancel-plan-order', + params, + ); + } + + /** + * + * * Spot | Account + * + */ + + getSpotAccount(): Promise> { + return this.getPrivate(`/api/v2/spot/account/info`); + } + + getSpotAccountAssets(params?: object): Promise> { + return this.getPrivate(`/api/v2/spot/account/assets`, params); + } + + getSpotSubAccountAssets(): Promise> { + return this.getPrivate(`/api/v2/spot/account/subaccount-assets`); + } + + getSpotAccountBills(params?: object): Promise> { + return this.getPrivate(`/api/v2/spot/account/bills`, params); + } + + spotTransfer(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/wallet/transfer`, params); + } + + spotSubTransfer(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/wallet/subaccount-transfer`, params); + } + + getSpotTransferHistory(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/account/transferRecords`, params); + } + + spotWithdraw(params: object): Promise> { + return this.postPrivate(`/api/v2/spot/wallet/withdrawal`, params); + } + + getSpotDepositAddress(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/wallet/deposit-address`, params); + } + + getSpotDepositHistory(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/wallet/deposit-records`, params); + } + + getSpotWithdrawalHistory(params: object): Promise> { + return this.getPrivate(`/api/v2/spot/wallet/withdrawal-records`, params); + } + + /** + * + * + * Futures + * + * + */ + + /** + * + * * Futures | Market + * + */ + + getFuturesVIPFeeRate(): Promise> { + return this.get(`/api/v2/mix/market/vip-fee-rate`); + } + + getFuturesMergeDepth(params: object): Promise> { + return this.get(`/api/v2/mix/market/merge-depth`, params); + } + + getFuturesTicker(params: object): Promise> { + return this.get(`/api/v2/mix/market/ticker`, params); + } + + getFuturesAllTickers(params: object): Promise> { + return this.get(`/api/v2/mix/market/tickers`, params); + } + + getFuturesRecentTrades(params: object): Promise> { + return this.get(`/api/v2/mix/market/fills`, params); + } + + getFuturesHistoricTrades(params: object): Promise> { + return this.get(`/api/v2/mix/market/fills-history`, params); + } + + getFuturesCandlestickData(params: object): Promise> { + return this.get(`/api/v2/mix/market/candles`, params); + } + + getFuturesHistoricCandlestickData(params: object): Promise> { + return this.get(`/api/v2/mix/market/history-candles`, params); + } + + getFuturesHistoricIndexPriceCandlestick( + params: object, + ): Promise> { + return this.get(`/api/v2/mix/market/history-index-candles`, params); + } + + getFuturesHistoricMarkPriceCandlestick( + params: object, + ): Promise> { + return this.get(`/api/v2/mix/market/history-mark-candles`, params); + } + + getFuturesOpenInterest(params: object): Promise> { + return this.get(`/api/v2/mix/market/open-interest`, params); + } + + getFuturesNextFundingTime(params: object): Promise> { + return this.get(`/api/v2/mix/market/funding-time`, params); + } + + getFuturesSymbolPrice(params: object): Promise> { + return this.get(`/api/v2/mix/market/symbol-price`, params); + } + + getFuturesHistoricFundingRates(params: object): Promise> { + return this.get(`/api/v2/mix/market/history-fund-rate`, params); + } + + getFuturesCurrentFundingRate(params: object): Promise> { + return this.get(`/api/v2/mix/market/current-fund-rate`, params); + } + + getFuturesContractConfig(params: object): Promise> { + return this.get(`/api/v2/mix/market/contracts`, params); + } + + /** + * + * * Futures | Account + * + */ + + getFuturesAccountAsset(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/account/account`, params); + } + + getFuturesAccountAssets(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/account/accounts`, params); + } + + getFuturesSubAccountAssets(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/account/sub-account-assets`, params); + } + + getFuturesOpenCount(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/account/open-count`, params); + } + + setFuturesLeverage(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/account/set-leverage`, params); + } + + adjustFuturesPositionMargin(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/account/set-margin`, params); + } + + setFuturesMarginMode(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/account/set-margin-mode`, params); + } + + setFuturesPositionMode(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/account/set-position-mode`, params); + } + + getFuturesAccountBills(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/account/bill`, params); + } + + /** + * + * * Futures | Position + * + */ + + getFuturesPositionTier(params: object): Promise> { + return this.get(`/api/v2/mix/market/query-position-lever`, params); + } + + getFuturesPosition(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/position/single-position`, params); + } + + getFuturesPositions(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/position/all-position`, params); + } + + getFuturesHistoricPositions(params?: object): Promise> { + return this.getPrivate(`/api/v2/mix/position/history-position`, params); + } + + /** + * + * * Futures | Trade + * + */ + + futuresSubmitOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/place-order`, params); + } + + futuresSubmitReverseal(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/click-backhand`, params); + } + + futuresBatchSubmitOrders(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/batch-place-order`, params); + } + + futuresModifyOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/modify-order`, params); + } + + futuresCancelOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/cancel-order`, params); + } + + futuresBatchCancelOrders(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/batch-cancel-orders`, params); + } + + futuresFlashClosePositions(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/close-positions`, params); + } + + getFuturesOrderDetail(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/order/detail`, params); + } + + getFuturesOrderFillDetails(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/order/fills`, params); + } + + getFuturesHistoricOrderFills(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/order/fill-history`, params); + } + + getFuturesOpenOrders(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/order/orders-pending`, params); + } + + getFuturesHistoricOrders(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/order/orders-history`, params); + } + + /** + * + * * Futures | Trigger Orders + * + */ + + futuresSubmitTPSLOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/place-tpsl-order`, params); + } + + futuresSubmitPlanOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/place-plan-order`, params); + } + + futuresModifyTPSLPOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/modify-tpsl-order`, params); + } + + futuresModifyPlanOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/modify-plan-order`, params); + } + + futuresCancelPlanOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/cancel-plan-order`, params); + } + + getFuturesPlanOrders(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/order/orders-plan-pending`, params); + } + + getFuturesHistoricPlanOrders(params: object): Promise> { + return this.getPrivate(`/api/v2/mix/order/orders-plan-history`, params); + } + + /** + * + * + * Broker + * + * + */ + + // TODO: not yet implemented + + /** + * + * + * Margin + * + * + */ + + /** + * + * * Margin | Common + * + */ + + getMarginCurrencies(): Promise> { + return this.get(`/api/v2/margin/currencies`); + } + + /** + * + * * Margin | Cross/Isolated | Order Record + * + */ + + getMarginBorrowHistory( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + `/api/v2/margin/${marginType}/borrow-history`, + params, + ); + } + + getMarginRepayHistory( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + `/api/v2/margin/${marginType}/repay-history`, + params, + ); + } + + getMarginInterestHistory( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + `/api/v2/margin/${marginType}/interest-history`, + params, + ); + } + + getMarginLiquidationHistory( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + `/api/v2/margin/${marginType}/liquidation-history`, + params, + ); + } + + getMarginFinancialHistory( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + `/api/v2/margin/${marginType}/financial-records`, + params, + ); + } + + /** + * + * * Margin | Cross/Isolated | Account + * + */ + + getMarginAccountAssets( + marginType: MarginType, + params?: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + `/api/v2/margin/${marginType}/account/assets`, + params, + ); + } + + marginBorrow( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.postPrivate( + `/api/v2/margin/${marginType}/account/borrow`, + params, + ); + } + + marginRepay( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.postPrivate( + `/api/v2/margin/${marginType}/account/repay`, + params, + ); + } + + getMarginRiskRate(marginType: MarginType): Promise> { + assertMarginType(marginType); + return this.getPrivate(`/api/v2/margin/${marginType}/account/risk-rate`); + } + + getMarginMaxBorrowable( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + '/api/v2/margin/${marginType}/account/max-borrowable-amount', + params, + ); + } + + getMarginMaxTransferable( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + '/api/v2/margin/${marginType}/account/max-transfer-out-amount', + params, + ); + } + + getMarginInterestRateAndMaxBorrowable( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + '/api/v2/margin/${marginType}/interest-rate-and-limit', + params, + ); + } + + getMarginTierConfiguration( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate(`/api/v2/margin/${marginType}/tier-data`, params); + } + + marginFlashRepay( + marginType: MarginType, + params?: object, + ): Promise> { + assertMarginType(marginType); + return this.postPrivate( + '/api/v2/margin/${marginType}/account/flash-repay', + params, + ); + } + + getMarginFlashRepayResult( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + '/api/v2/margin/${marginType}/account/query-flash-repay-status', + params, + ); + } + + /** + * + * * Margin | Cross/Isolated | Trade + * + */ + + marginSubmitOrder( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.postPrivate(`/api/v2/margin/${marginType}/place-order`, params); + } + + marginBatchSubmitOrders( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.postPrivate( + `/api/v2/margin/${marginType}/batch-place-order`, + params, + ); + } + + marginCancelOrder( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.postPrivate( + `/api/v2/margin/${marginType}/cancel-order`, + params, + ); + } + + marginBatchCancelOrders( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.postPrivate( + '/api/v2/margin/${marginType}/batch-cancel-order', + params, + ); + } + + getMarginOpenOrders( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate(`/api/v2/margin/${marginType}/open-orders`, params); + } + + getMarginHistoricOrders( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate( + `/api/v2/margin/${marginType}/history-orders`, + params, + ); + } + + getMarginHistoricOrderFills( + marginType: MarginType, + params: object, + ): Promise> { + assertMarginType(marginType); + return this.getPrivate(`/api/v2/margin/${marginType}/fills`, params); + } + + /** + * + * + * Copy Trading + * + * + */ + + // TODO: not yet implemented + + /** + * + * + * Earn + * + * + */ + + // TODO: not yet implemented +} diff --git a/src/spot-client.ts b/src/spot-client.ts index 97b6257..69b72f9 100644 --- a/src/spot-client.ts +++ b/src/spot-client.ts @@ -27,6 +27,8 @@ import BaseRestClient from './util/BaseRestClient'; /** * REST API client + * + * @deprecated use RestClientV2 instead */ export class SpotClient extends BaseRestClient { getClientType() { @@ -43,7 +45,6 @@ export class SpotClient extends BaseRestClient { * Public * */ - /** Get Server Time */ getServerTime(): Promise> { return this.get('/api/spot/v1/public/time'); diff --git a/src/types/request/shared.ts b/src/types/request/shared.ts index 3a63c26..6d21f7a 100644 --- a/src/types/request/shared.ts +++ b/src/types/request/shared.ts @@ -17,3 +17,8 @@ export interface GetHistoricTradesParams { startTime?: string; endTime?: string; } + +/** + * The margin type, used directly in building the endpoint URL + */ +export type MarginType = 'crossed' | 'isolated'; diff --git a/src/types/response/shared.ts b/src/types/response/shared.ts index d99b11c..e59ec99 100644 --- a/src/types/response/shared.ts +++ b/src/types/response/shared.ts @@ -1,8 +1,8 @@ export interface APIResponse { code: string; - data: T; - msg: 'success' | string; requestTime: number; + msg: 'success' | string; + data: T; } export interface VIPFeeRate { diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index d93f396..6f831f0 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -98,4 +98,5 @@ export const REST_CLIENT_TYPE_ENUM = { spot: 'spot', futures: 'futures', broker: 'broker', + v2: 'v2', } as const; diff --git a/src/util/type-guards.ts b/src/util/type-guards.ts index b74709a..fbf9967 100644 --- a/src/util/type-guards.ts +++ b/src/util/type-guards.ts @@ -1,4 +1,5 @@ import { + MarginType, WsAccountSnapshotUMCBL, WsBaseEvent, WSPositionSnapshotUMCBL, @@ -71,3 +72,13 @@ export function isWsFuturesPositionsSnapshotEvent( ): event is WSPositionSnapshotUMCBL { return isWsPositionsSnapshotEvent(event) && event.arg.instType === 'umcbl'; } + +/** + * Simple guard for non-TypeScript users, throw a runtime error if value doesn't match type + */ +export function assertMarginType(marginType: string): marginType is MarginType { + if (marginType !== 'isolated' && marginType !== 'crossed') { + throw new Error(`MarginType should be one of: crossed | isolated`); + } + return true; +} diff --git a/src/util/websocket-util.ts b/src/util/websocket-util.ts index 6b9645b..2637169 100644 --- a/src/util/websocket-util.ts +++ b/src/util/websocket-util.ts @@ -19,17 +19,40 @@ type NetworkMap< export const WS_BASE_URL_MAP: Record< WsKey, - Record<'all', NetworkMap<'livenet'>> + Record<'all' | 'public' | 'private', NetworkMap<'livenet'>> > = { mixv1: { all: { livenet: 'wss://ws.bitget.com/mix/v1/stream', }, + public: { + livenet: 'N/A', + }, + private: { + livenet: 'N/A', + }, }, spotv1: { all: { livenet: 'wss://ws.bitget.com/spot/v1/stream', }, + public: { + livenet: 'N/A', + }, + private: { + livenet: 'N/A', + }, + }, + v2: { + all: { + livenet: 'N/A', + }, + public: { + livenet: 'wss://ws.bitget.com/v2/ws/public', + }, + private: { + livenet: 'wss://ws.bitget.com/v2/ws/private', + }, }, }; @@ -37,6 +60,7 @@ export const WS_BASE_URL_MAP: Record< export const WS_KEY_MAP = { spotv1: 'spotv1', mixv1: 'mixv1', + v2: 'v2', } as const; /** Any WS keys in this list will trigger auth on connect, if credentials are available */ @@ -88,7 +112,8 @@ export function getWsKeyForTopic( export function getMaxTopicsPerSubscribeEvent(wsKey: WsKey): number | null { switch (wsKey) { case 'mixv1': - case 'spotv1': { + case 'spotv1': + case 'v2': { // Technically there doesn't seem to be a documented cap, but there is a size limit per request. Doesn't hurt to batch requests. return 15; } From c45dd5d38229351b974f4bffb98c5bb55e73cd1d Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 14 Nov 2023 11:07:00 +0000 Subject: [PATCH 02/10] feat(): mark optional params --- src/rest-client-v2.ts | 36 ++++++++++++++++++------------------ src/util/websocket-util.ts | 30 ++++++++++-------------------- src/websocket-client.ts | 6 ++++++ 3 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/rest-client-v2.ts b/src/rest-client-v2.ts index 5b3232f..1c047e3 100644 --- a/src/rest-client-v2.ts +++ b/src/rest-client-v2.ts @@ -382,10 +382,6 @@ export class RestClientV2 extends BaseRestClient { return this.get(`/api/v2/mix/market/vip-fee-rate`); } - getFuturesMergeDepth(params: object): Promise> { - return this.get(`/api/v2/mix/market/merge-depth`, params); - } - getFuturesTicker(params: object): Promise> { return this.get(`/api/v2/mix/market/ticker`, params); } @@ -394,12 +390,8 @@ export class RestClientV2 extends BaseRestClient { return this.get(`/api/v2/mix/market/tickers`, params); } - getFuturesRecentTrades(params: object): Promise> { - return this.get(`/api/v2/mix/market/fills`, params); - } - - getFuturesHistoricTrades(params: object): Promise> { - return this.get(`/api/v2/mix/market/fills-history`, params); + getFuturesMergeDepth(params: object): Promise> { + return this.get(`/api/v2/mix/market/merge-depth`, params); } getFuturesCandlestickData(params: object): Promise> { @@ -422,6 +414,14 @@ export class RestClientV2 extends BaseRestClient { return this.get(`/api/v2/mix/market/history-mark-candles`, params); } + getFuturesRecentTrades(params: object): Promise> { + return this.get(`/api/v2/mix/market/fills`, params); + } + + getFuturesHistoricTrades(params: object): Promise> { + return this.get(`/api/v2/mix/market/fills-history`, params); + } + getFuturesOpenInterest(params: object): Promise> { return this.get(`/api/v2/mix/market/open-interest`, params); } @@ -472,7 +472,7 @@ export class RestClientV2 extends BaseRestClient { return this.postPrivate(`/api/v2/mix/account/set-leverage`, params); } - adjustFuturesPositionMargin(params: object): Promise> { + setFuturesPositionMargin(params: object): Promise> { return this.postPrivate(`/api/v2/mix/account/set-margin`, params); } @@ -520,7 +520,11 @@ export class RestClientV2 extends BaseRestClient { return this.postPrivate(`/api/v2/mix/order/place-order`, params); } - futuresSubmitReverseal(params: object): Promise> { + futuresCancelOrder(params: object): Promise> { + return this.postPrivate(`/api/v2/mix/order/cancel-order`, params); + } + + futuresSubmitReversal(params: object): Promise> { return this.postPrivate(`/api/v2/mix/order/click-backhand`, params); } @@ -532,10 +536,6 @@ export class RestClientV2 extends BaseRestClient { return this.postPrivate(`/api/v2/mix/order/modify-order`, params); } - futuresCancelOrder(params: object): Promise> { - return this.postPrivate(`/api/v2/mix/order/cancel-order`, params); - } - futuresBatchCancelOrders(params: object): Promise> { return this.postPrivate(`/api/v2/mix/order/batch-cancel-orders`, params); } @@ -544,11 +544,11 @@ export class RestClientV2 extends BaseRestClient { return this.postPrivate(`/api/v2/mix/order/close-positions`, params); } - getFuturesOrderDetail(params: object): Promise> { + getFuturesOrder(params: object): Promise> { return this.getPrivate(`/api/v2/mix/order/detail`, params); } - getFuturesOrderFillDetails(params: object): Promise> { + getFuturesFills(params: object): Promise> { return this.getPrivate(`/api/v2/mix/order/fills`, params); } diff --git a/src/util/websocket-util.ts b/src/util/websocket-util.ts index 2637169..231bf3e 100644 --- a/src/util/websocket-util.ts +++ b/src/util/websocket-util.ts @@ -19,38 +19,25 @@ type NetworkMap< export const WS_BASE_URL_MAP: Record< WsKey, - Record<'all' | 'public' | 'private', NetworkMap<'livenet'>> + Record<'all', NetworkMap<'livenet'>> > = { mixv1: { all: { livenet: 'wss://ws.bitget.com/mix/v1/stream', }, - public: { - livenet: 'N/A', - }, - private: { - livenet: 'N/A', - }, }, spotv1: { all: { livenet: 'wss://ws.bitget.com/spot/v1/stream', }, - public: { - livenet: 'N/A', - }, - private: { - livenet: 'N/A', - }, }, - v2: { + v2Public: { all: { - livenet: 'N/A', - }, - public: { livenet: 'wss://ws.bitget.com/v2/ws/public', }, - private: { + }, + v2Private: { + all: { livenet: 'wss://ws.bitget.com/v2/ws/private', }, }, @@ -60,13 +47,15 @@ export const WS_BASE_URL_MAP: Record< export const WS_KEY_MAP = { spotv1: 'spotv1', mixv1: 'mixv1', - v2: 'v2', + v2Public: 'v2Public', + v2Private: 'v2Private', } as const; /** Any WS keys in this list will trigger auth on connect, if credentials are available */ export const WS_AUTH_ON_CONNECT_KEYS: WsKey[] = [ WS_KEY_MAP.spotv1, WS_KEY_MAP.mixv1, + WS_KEY_MAP.v2Private, ]; /** Any WS keys in this list will ALWAYS skip the authentication process, even if credentials are available */ @@ -113,7 +102,8 @@ export function getMaxTopicsPerSubscribeEvent(wsKey: WsKey): number | null { switch (wsKey) { case 'mixv1': case 'spotv1': - case 'v2': { + case 'v2Public': + case 'v2Private': { // Technically there doesn't seem to be a documented cap, but there is a size limit per request. Doesn't hurt to batch requests. return 15; } diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 2ac02e6..1b634ee 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -646,6 +646,12 @@ export class WebsocketClient extends EventEmitter { case WS_KEY_MAP.mixv1: { return WS_BASE_URL_MAP.mixv1.all[networkKey]; } + case WS_KEY_MAP.v2Private: { + return WS_BASE_URL_MAP.v2Private.all[networkKey]; + } + case WS_KEY_MAP.v2Public: { + return WS_BASE_URL_MAP.v2Public.all[networkKey]; + } default: { this.logger.error('getWsUrl(): Unhandled wsKey: ', { ...LOGGER_CATEGORY, From dc6e0ff52aa472709b072f1cc22728f25c415043 Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 14 Nov 2023 11:14:15 +0000 Subject: [PATCH 03/10] chore(): temporarily skip flaky ws test --- jest.config.js | 5 +++-- test/ws.private.test.ts | 11 +++++++++-- test/ws.util.ts | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/jest.config.js b/jest.config.js index 32b968b..95f997f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,7 +6,7 @@ module.exports = { __PROD__: false }, testEnvironment: 'node', - preset: "ts-jest", + preset: 'ts-jest', verbose: true, // report individual test bail: false, // enable to stop test when an error occur, detectOpenHandles: false, @@ -16,6 +16,7 @@ module.exports = { collectCoverageFrom: [ 'src/**/*.ts' ], + testTimeout: 10000, coverageThreshold: { // coverage strategy global: { @@ -25,4 +26,4 @@ module.exports = { statements: -10 } } -}; \ No newline at end of file +}; diff --git a/test/ws.private.test.ts b/test/ws.private.test.ts index 7354069..6c75ecd 100644 --- a/test/ws.private.test.ts +++ b/test/ws.private.test.ts @@ -4,9 +4,14 @@ import { WS_ERROR_ENUM, WS_KEY_MAP, } from '../src'; -import { getSilentLogger, waitForSocketEvent } from './ws.util'; +import { + getSilentLogger, + listenToSocketEvents, + logAllEvents, + waitForSocketEvent, +} from './ws.util'; -describe('Private Spot Websocket Client', () => { +describe.skip('Private Spot Websocket Client', () => { const API_KEY = process.env.API_KEY_COM; const API_SECRET = process.env.API_SECRET_COM; const API_PASS = process.env.API_PASS_COM; @@ -29,6 +34,7 @@ describe('Private Spot Websocket Client', () => { getSilentLogger('expect401'), ); + logAllEvents(badClient); // const wsOpenPromise = waitForSocketEvent(badClient, 'open'); const wsResponsePromise = waitForSocketEvent(badClient, 'response'); // const wsUpdatePromise = waitForSocketEvent(wsClient, 'update'); @@ -66,6 +72,7 @@ describe('Private Spot Websocket Client', () => { ); wsClient.connectAll(); // logAllEvents(wsClient); + logAllEvents(wsClient); }); afterAll(() => { diff --git a/test/ws.util.ts b/test/ws.util.ts index 1a83175..a3a65cd 100644 --- a/test/ws.util.ts +++ b/test/ws.util.ts @@ -24,7 +24,7 @@ export const fullLogger = { export function waitForSocketEvent( wsClient: WebsocketClient, event: WsClientEvent, - timeoutMs: number = 4.5 * 1000, + timeoutMs: number = 10 * 1000, ) { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { From b1b3ee926ec847597bfb6954923f6c6a94bd2446 Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 14 Nov 2023 11:20:53 +0000 Subject: [PATCH 04/10] feat(): mark other V1 REST clients as deprecated --- src/broker-client.ts | 5 ++++- src/futures-client.ts | 5 ++++- src/spot-client.ts | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/broker-client.ts b/src/broker-client.ts index d96816b..bad3bdc 100644 --- a/src/broker-client.ts +++ b/src/broker-client.ts @@ -9,7 +9,10 @@ import { REST_CLIENT_TYPE_ENUM } from './util'; import BaseRestClient from './util/BaseRestClient'; /** - * REST API client for broker APIs + * REST API client for the V1 bitget Broker APIs. These are the previous generation of Bitget's APIs and should be considered deprecated. + * These will be removed in a future release, once Bitget formally deprecates them. + * + * @deprecated use RestClientV2 instead */ export class BrokerClient extends BaseRestClient { getClientType() { diff --git a/src/futures-client.ts b/src/futures-client.ts index f1a9a84..d6d82b4 100644 --- a/src/futures-client.ts +++ b/src/futures-client.ts @@ -31,7 +31,10 @@ import { REST_CLIENT_TYPE_ENUM } from './util'; import BaseRestClient from './util/BaseRestClient'; /** - * REST API client + * REST API client for the V1 bitget Futures APIs. These are the previous generation of Bitget's APIs and should be considered deprecated. + * These will be removed in a future release, once Bitget formally deprecates them. + * + * @deprecated use RestClientV2 instead */ export class FuturesClient extends BaseRestClient { getClientType() { diff --git a/src/spot-client.ts b/src/spot-client.ts index 69b72f9..a500a26 100644 --- a/src/spot-client.ts +++ b/src/spot-client.ts @@ -26,7 +26,8 @@ import { REST_CLIENT_TYPE_ENUM } from './util'; import BaseRestClient from './util/BaseRestClient'; /** - * REST API client + * REST API client for the V1 bitget Spot APIs. These are the previous generation of Bitget's APIs and should be considered deprecated. + * These will be removed in a future release, once Bitget formally deprecates them. * * @deprecated use RestClientV2 instead */ From ea8fac777eb68d0136c908ba98c2c4dddc5e3e31 Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 14 Nov 2023 12:15:26 +0000 Subject: [PATCH 05/10] feat(): update basic spot/futures examples with initial types --- examples/deprecated-V1-REST/README.md | 8 + .../rest-private-futures.ts | 38 ++++ .../deprecated-V1-REST/rest-private-spot.ts | 27 +++ .../deprecated-V1-REST/rest-public-futures.ts | 30 +++ .../deprecated-V1-REST/rest-public-spot.ts | 19 ++ .../deprecated-V1-REST/rest-trade-futures.ts | 175 ++++++++++++++++++ .../deprecated-V1-REST/rest-trade-spot.ts | 104 +++++++++++ examples/rest-private-futures.ts | 19 +- examples/rest-private-spot.ts | 8 +- examples/rest-public-futures.ts | 24 ++- examples/rest-public-spot.ts | 14 +- src/index.ts | 1 + src/rest-client-v2.ts | 42 ++--- src/types/request/index.ts | 8 +- .../request/{broker.ts => v1/brokerV1.ts} | 0 .../request/{futures.ts => v1/futuresV1.ts} | 2 +- src/types/request/{spot.ts => v1/spotV1.ts} | 2 +- src/types/request/v2/futures.ts | 32 ++++ src/types/request/v2/spot.ts | 9 + 19 files changed, 499 insertions(+), 63 deletions(-) create mode 100644 examples/deprecated-V1-REST/README.md create mode 100644 examples/deprecated-V1-REST/rest-private-futures.ts create mode 100644 examples/deprecated-V1-REST/rest-private-spot.ts create mode 100644 examples/deprecated-V1-REST/rest-public-futures.ts create mode 100644 examples/deprecated-V1-REST/rest-public-spot.ts create mode 100644 examples/deprecated-V1-REST/rest-trade-futures.ts create mode 100644 examples/deprecated-V1-REST/rest-trade-spot.ts rename src/types/request/{broker.ts => v1/brokerV1.ts} (100%) rename src/types/request/{futures.ts => v1/futuresV1.ts} (98%) rename src/types/request/{spot.ts => v1/spotV1.ts} (98%) create mode 100644 src/types/request/v2/futures.ts create mode 100644 src/types/request/v2/spot.ts diff --git a/examples/deprecated-V1-REST/README.md b/examples/deprecated-V1-REST/README.md new file mode 100644 index 0000000..5a60185 --- /dev/null +++ b/examples/deprecated-V1-REST/README.md @@ -0,0 +1,8 @@ +# Deprecated V1 REST API Examples + +These examples are for Bitget's V1 APIs, the previous generation of their API offering. + +If you're building new functionality, you should look at using the V2 APIs via the RestClientV2 class in this SDK. This covers all the newer functionality offered by Bitget's APIs - with significant upgrades on all aspects of their APIs: +https://www.bitget.com/api-doc/common/release-note + +The V1 REST clients will remain function until Bitget formally deprecates their V1 API. diff --git a/examples/deprecated-V1-REST/rest-private-futures.ts b/examples/deprecated-V1-REST/rest-private-futures.ts new file mode 100644 index 0000000..b52717d --- /dev/null +++ b/examples/deprecated-V1-REST/rest-private-futures.ts @@ -0,0 +1,38 @@ +import { FuturesClient, WebsocketClient } from '../../src/index'; + +// or +// import { SpotClient } from 'bitget-api'; + +// read from environmental variables +const API_KEY = process.env.API_KEY_COM; +const API_SECRET = process.env.API_SECRET_COM; +const API_PASS = process.env.API_PASS_COM; + +const client = new FuturesClient({ + apiKey: API_KEY, + apiSecret: API_SECRET, + apiPass: API_PASS, + // apiKey: 'apiKeyHere', + // apiSecret: 'apiSecretHere', + // apiPass: 'apiPassHere', +}); + +/** This is a simple script wrapped in a immediately invoked function expression, designed to check for any available BTC balance and immediately sell the full amount for USDT */ +(async () => { + try { + const now = new Date(); + const toTime = new Date(now.getTime() - 24 * 60 * 60 * 1000); + + console.log( + await client.getAccountBill({ + symbol: 'BTCUSDT_UMCBL', + marginCoin: 'USDT', + startTime: now.getTime() + '', // should be sent as a string + endTime: toTime.getTime() + '', // should be sent as a string + pageSize: 100, + }), + ); + } catch (e) { + console.error('request failed: ', e); + } +})(); diff --git a/examples/deprecated-V1-REST/rest-private-spot.ts b/examples/deprecated-V1-REST/rest-private-spot.ts new file mode 100644 index 0000000..f897ada --- /dev/null +++ b/examples/deprecated-V1-REST/rest-private-spot.ts @@ -0,0 +1,27 @@ +import { SpotClient, WebsocketClient } from '../../src/index'; + +// or +// import { SpotClient } from 'bitget-api'; + +// read from environmental variables +const API_KEY = process.env.API_KEY_COM; +const API_SECRET = process.env.API_SECRET_COM; +const API_PASS = process.env.API_PASS_COM; + +const client = new SpotClient({ + apiKey: API_KEY, + apiSecret: API_SECRET, + apiPass: API_PASS, + // apiKey: 'apiKeyHere', + // apiSecret: 'apiSecretHere', + // apiPass: 'apiPassHere', +}); + +/** This is a simple script wrapped in a immediately invoked function expression, designed to check for any available BTC balance and immediately sell the full amount for USDT */ +(async () => { + try { + console.log(await client.getApiKeyInfo()); + } catch (e) { + console.error('request failed: ', e); + } +})(); diff --git a/examples/deprecated-V1-REST/rest-public-futures.ts b/examples/deprecated-V1-REST/rest-public-futures.ts new file mode 100644 index 0000000..423c6f9 --- /dev/null +++ b/examples/deprecated-V1-REST/rest-public-futures.ts @@ -0,0 +1,30 @@ +import { FuturesClient, WebsocketClient } from '../../src/index'; + +// or +// import { SpotClient } from 'bitget-api'; + +const futuresClient = new FuturesClient(); + +const symbol = 'BTCUSDT_UMCBL'; + +(async () => { + try { + // Fetch the last 1000 1min candles for a symbol + const timestampNow = Date.now(); + const msPerCandle = 60 * 1000; // 60 seconds x 1000 + const candlesToFetch = 1000; + const msFor1kCandles = candlesToFetch * msPerCandle; + const startTime = timestampNow - msFor1kCandles; + + const response = await futuresClient.getCandles( + symbol, + '1m', + startTime.toString(), + timestampNow.toString(), + candlesToFetch.toString(), + ); + console.log('getCandles returned ' + response.length + ' candles'); + } catch (e) { + console.error('request failed: ', e); + } +})(); diff --git a/examples/deprecated-V1-REST/rest-public-spot.ts b/examples/deprecated-V1-REST/rest-public-spot.ts new file mode 100644 index 0000000..2b83809 --- /dev/null +++ b/examples/deprecated-V1-REST/rest-public-spot.ts @@ -0,0 +1,19 @@ +import { SpotClient } from '../../src/index'; + +// or +// import { SpotClient } from 'bitget-api'; + +const spotClient = new SpotClient(); + +const symbol = 'BTCUSDT_SPBL'; + +(async () => { + try { + const response = await spotClient.getCandles(symbol, '1min', { + limit: '1000', + }); + console.log('getCandles: ', response.data.length); + } catch (e) { + console.error('request failed: ', e); + } +})(); diff --git a/examples/deprecated-V1-REST/rest-trade-futures.ts b/examples/deprecated-V1-REST/rest-trade-futures.ts new file mode 100644 index 0000000..bb6fb2c --- /dev/null +++ b/examples/deprecated-V1-REST/rest-trade-futures.ts @@ -0,0 +1,175 @@ +import { + FuturesClient, + isWsFuturesAccountSnapshotEvent, + isWsFuturesPositionsSnapshotEvent, + NewFuturesOrder, + WebsocketClient, +} from '../../src'; + +// or +// import { +// FuturesClient, +// isWsFuturesAccountSnapshotEvent, +// isWsFuturesPositionsSnapshotEvent, +// NewFuturesOrder, +// WebsocketClient, +// } from 'bitget-api'; + +// read from environmental variables +const API_KEY = process.env.API_KEY_COM; +const API_SECRET = process.env.API_SECRET_COM; +const API_PASS = process.env.API_PASS_COM; + +const client = new FuturesClient({ + apiKey: API_KEY, + apiSecret: API_SECRET, + apiPass: API_PASS, + // apiKey: 'apiKeyHere', + // apiSecret: 'apiSecretHere', + // apiPass: 'apiPassHere', +}); + +const wsClient = new WebsocketClient({ + apiKey: API_KEY, + apiSecret: API_SECRET, + apiPass: API_PASS, +}); + +function logWSEvent(type, data) { + console.log(new Date(), `WS ${type} event: `, data); +} + +// simple sleep function +function promiseSleep(milliseconds) { + return new Promise((resolve) => setTimeout(resolve, milliseconds)); +} + +// WARNING: for sensitive math you should be using a library such as decimal.js! +function roundDown(value, decimals) { + return Number( + Math.floor(parseFloat(value + 'e' + decimals)) + 'e-' + decimals, + ); +} + +/** WS event handler that uses type guards to narrow down event type */ +async function handleWsUpdate(event) { + if (isWsFuturesAccountSnapshotEvent(event)) { + console.log(new Date(), 'ws update (account balance):', event); + return; + } + + if (isWsFuturesPositionsSnapshotEvent(event)) { + console.log(new Date(), 'ws update (positions):', event); + return; + } + + logWSEvent('update (unhandled)', event); +} + +/** + * This is a simple script wrapped in a immediately invoked function expression (to execute the below workflow immediately). + * + * It is designed to: + * - open a private websocket channel to log account events + * - check for any available USDT balance in the futures account + * - immediately open a minimum sized long position on BTCUSDT + * - check active positions + * - immediately send closing orders for any active futures positions + * - check positions again + * + * The corresponding UI for this is at https://www.bitget.com/en/mix/usdt/BTCUSDT_UMCBL + */ +(async () => { + try { + // Add event listeners to log websocket events on account + wsClient.on('update', (data) => handleWsUpdate(data)); + + wsClient.on('open', (data) => logWSEvent('open', data)); + wsClient.on('response', (data) => logWSEvent('response', data)); + wsClient.on('reconnect', (data) => logWSEvent('reconnect', data)); + wsClient.on('reconnected', (data) => logWSEvent('reconnected', data)); + wsClient.on('authenticated', (data) => logWSEvent('authenticated', data)); + wsClient.on('exception', (data) => logWSEvent('exception', data)); + + // Subscribe to private account topics + wsClient.subscribeTopic('UMCBL', 'account'); + // : position updates + wsClient.subscribeTopic('UMCBL', 'positions'); + // : order updates + wsClient.subscribeTopic('UMCBL', 'orders'); + + // wait briefly for ws to be ready (could also use the response or authenticated events, to make sure topics are subscribed to before starting) + await promiseSleep(2.5 * 1000); + + const symbol = 'BTCUSDT_UMCBL'; + const marginCoin = 'USDT'; + + const balanceResult = await client.getAccount(symbol, marginCoin); + const accountBalance = balanceResult.data; + // const balances = allBalances.filter((bal) => Number(bal.available) != 0); + const usdtAmount = accountBalance.available; + console.log('USDT balance: ', usdtAmount); + + if (!usdtAmount) { + console.error('No USDT to trade'); + return; + } + + const symbolRulesResult = await client.getSymbols('umcbl'); + const bitcoinUSDFuturesRule = symbolRulesResult.data.find( + (row) => row.symbol === symbol, + ); + + console.log('symbol rules: ', bitcoinUSDFuturesRule); + if (!bitcoinUSDFuturesRule) { + console.error('Failed to get trading rules for ' + symbol); + return; + } + + const order: NewFuturesOrder = { + marginCoin, + orderType: 'market', + side: 'open_long', + size: bitcoinUSDFuturesRule.minTradeNum, + symbol, + } as const; + + console.log('placing order: ', order); + + const result = await client.submitOrder(order); + + console.log('order result: ', result); + + const positionsResult = await client.getPositions('umcbl'); + const positionsToClose = positionsResult.data.filter( + (pos) => pos.total !== '0', + ); + + console.log('open positions to close: ', positionsToClose); + + // Loop through any active positions and send a closing market order on each position + for (const position of positionsToClose) { + const closingSide = + position.holdSide === 'long' ? 'close_long' : 'close_short'; + const closingOrder: NewFuturesOrder = { + marginCoin: position.marginCoin, + orderType: 'market', + side: closingSide, + size: position.available, + symbol: position.symbol, + }; + + console.log('closing position with market order: ', closingOrder); + + const result = await client.submitOrder(closingOrder); + console.log('position closing order result: ', result); + } + + console.log( + 'positions after closing all: ', + await client.getPositions('umcbl'), + ); + } catch (e) { + console.error('request failed: ', e); + } +})(); diff --git a/examples/deprecated-V1-REST/rest-trade-spot.ts b/examples/deprecated-V1-REST/rest-trade-spot.ts new file mode 100644 index 0000000..bfa171a --- /dev/null +++ b/examples/deprecated-V1-REST/rest-trade-spot.ts @@ -0,0 +1,104 @@ +import { SpotClient, WebsocketClient } from '../../src/index'; + +// or +// import { SpotClient } from 'bitget-api'; + +// read from environmental variables +const API_KEY = process.env.API_KEY_COM; +const API_SECRET = process.env.API_SECRET_COM; +const API_PASS = process.env.API_PASS_COM; + +const client = new SpotClient({ + apiKey: API_KEY, + apiSecret: API_SECRET, + apiPass: API_PASS, + // apiKey: 'apiKeyHere', + // apiSecret: 'apiSecretHere', + // apiPass: 'apiPassHere', +}); + +const wsClient = new WebsocketClient({ + apiKey: API_KEY, + apiSecret: API_SECRET, + apiPass: API_PASS, +}); + +function logWSEvent(type, data) { + console.log(new Date(), `WS ${type} event: `, data); +} + +// simple sleep function +function promiseSleep(milliseconds) { + return new Promise((resolve) => setTimeout(resolve, milliseconds)); +} + +// WARNING: for sensitive math you should be using a library such as decimal.js! +function roundDown(value, decimals) { + return Number( + Math.floor(parseFloat(value + 'e' + decimals)) + 'e-' + decimals, + ); +} + +/** This is a simple script wrapped in a immediately invoked function expression, designed to check for any available BTC balance and immediately sell the full amount for USDT */ +(async () => { + try { + // Add event listeners to log websocket events on account + wsClient.on('update', (data) => logWSEvent('update', data)); + wsClient.on('open', (data) => logWSEvent('open', data)); + wsClient.on('response', (data) => logWSEvent('response', data)); + wsClient.on('reconnect', (data) => logWSEvent('reconnect', data)); + wsClient.on('reconnected', (data) => logWSEvent('reconnected', data)); + wsClient.on('authenticated', (data) => logWSEvent('authenticated', data)); + wsClient.on('exception', (data) => logWSEvent('exception', data)); + + // Subscribe to private account topics + wsClient.subscribeTopic('SPBL', 'account'); + wsClient.subscribeTopic('SPBL', 'orders'); + + // wait briefly for ws to be ready (could also use the response or authenticated events, to make sure topics are subscribed to before starting) + await promiseSleep(2.5 * 1000); + + const balanceResult = await client.getBalance(); + const allBalances = balanceResult.data; + // const balances = allBalances.filter((bal) => Number(bal.available) != 0); + const balanceBTC = allBalances.find((bal) => bal.coinName === 'BTC'); + const btcAmount = balanceBTC ? Number(balanceBTC.available) : 0; + // console.log('balance: ', JSON.stringify(balances, null, 2)); + console.log('BTC balance result: ', balanceBTC); + + if (!btcAmount) { + console.error('No BTC to trade'); + return; + } + + console.log(`BTC available: ${btcAmount}`); + const symbol = 'BTCUSDT_SPBL'; + + const symbolsResult = await client.getSymbols(); + const btcRules = symbolsResult.data.find((rule) => rule.symbol === symbol); + console.log('btc trading rules: ', btcRules); + if (!btcRules) { + return console.log('no rules found for trading ' + symbol); + } + + const quantityScale = Number(btcRules.quantityScale); + // const quantityRoundedDown = btcAmount - btcAmount % 0.01 + const quantity = roundDown(btcAmount, quantityScale); + + const order = { + symbol: symbol, + side: 'sell', + force: 'normal', + orderType: 'market', + quantity: String(quantity), + } as const; + + console.log('submitting order: ', order); + + const sellResult = await client.submitOrder(order); + + console.log('sell result: ', sellResult); + } catch (e) { + console.error('request failed: ', e); + } +})(); diff --git a/examples/rest-private-futures.ts b/examples/rest-private-futures.ts index dcd1ac7..49a9b2f 100644 --- a/examples/rest-private-futures.ts +++ b/examples/rest-private-futures.ts @@ -1,14 +1,14 @@ -import { FuturesClient, WebsocketClient } from '../src/index'; +import { RestClientV2, WebsocketClient } from '../src/index'; // or -// import { SpotClient } from 'bitget-api'; +// import { RestClientV2 } from 'bitget-api'; // read from environmental variables const API_KEY = process.env.API_KEY_COM; const API_SECRET = process.env.API_SECRET_COM; const API_PASS = process.env.API_PASS_COM; -const client = new FuturesClient({ +const client = new RestClientV2({ apiKey: API_KEY, apiSecret: API_SECRET, apiPass: API_PASS, @@ -21,15 +21,14 @@ const client = new FuturesClient({ (async () => { try { const now = new Date(); - const toTime = new Date(now.getTime() - 24 * 60 * 60 * 1000); + const fromTime = new Date(now.getTime() - 24 * 60 * 60 * 1000); console.log( - await client.getAccountBill({ - symbol: 'BTCUSDT_UMCBL', - marginCoin: 'USDT', - startTime: now.getTime() + '', // should be sent as a string - endTime: toTime.getTime() + '', // should be sent as a string - pageSize: 100, + await client.getFuturesAccountBills({ + productType: 'USDT-FUTURES', + startTime: fromTime.getTime() + '', // should be sent as a string + endTime: now.getTime() + '', // should be sent as a string + limit: '100', }), ); } catch (e) { diff --git a/examples/rest-private-spot.ts b/examples/rest-private-spot.ts index e7da2e2..94f50db 100644 --- a/examples/rest-private-spot.ts +++ b/examples/rest-private-spot.ts @@ -1,14 +1,14 @@ -import { SpotClient, WebsocketClient } from '../src/index'; +import { RestClientV2, WebsocketClient } from '../src/index'; // or -// import { SpotClient } from 'bitget-api'; +// import { RestClientV2 } from 'bitget-api'; // read from environmental variables const API_KEY = process.env.API_KEY_COM; const API_SECRET = process.env.API_SECRET_COM; const API_PASS = process.env.API_PASS_COM; -const client = new SpotClient({ +const client = new RestClientV2({ apiKey: API_KEY, apiSecret: API_SECRET, apiPass: API_PASS, @@ -20,7 +20,7 @@ const client = new SpotClient({ /** This is a simple script wrapped in a immediately invoked function expression, designed to check for any available BTC balance and immediately sell the full amount for USDT */ (async () => { try { - console.log(await client.getApiKeyInfo()); + console.log(await client.getSpotAccount()); } catch (e) { console.error('request failed: ', e); } diff --git a/examples/rest-public-futures.ts b/examples/rest-public-futures.ts index 224bb7c..d9ef39b 100644 --- a/examples/rest-public-futures.ts +++ b/examples/rest-public-futures.ts @@ -1,11 +1,11 @@ -import { FuturesClient, SpotClient } from '../src/index'; +import { RestClientV2, SpotClient } from '../src/index'; // or // import { SpotClient } from 'bitget-api'; -const futuresClient = new FuturesClient(); +const restClient = new RestClientV2(); -const symbol = 'BTCUSDT_UMCBL'; +const symbol = 'BTCUSDT'; (async () => { try { @@ -16,14 +16,18 @@ const symbol = 'BTCUSDT_UMCBL'; const msFor1kCandles = candlesToFetch * msPerCandle; const startTime = timestampNow - msFor1kCandles; - const response = await futuresClient.getCandles( + const response = await restClient.getFuturesCandles({ symbol, - '1m', - startTime.toString(), - timestampNow.toString(), - candlesToFetch.toString(), - ); - console.log('getCandles returned ' + response.length + ' candles'); + productType: 'USDT-FUTURES', + granularity: '1m', + startTime: startTime.toString(), + endTime: timestampNow.toString(), + limit: candlesToFetch.toString(), + }); + + console.table(response.data); + + console.log('getCandles returned ' + response.data.length + ' candles'); } catch (e) { console.error('request failed: ', e); } diff --git a/examples/rest-public-spot.ts b/examples/rest-public-spot.ts index 02083ce..5e282eb 100644 --- a/examples/rest-public-spot.ts +++ b/examples/rest-public-spot.ts @@ -1,17 +1,19 @@ -import { SpotClient } from '../src/index'; +import { RestClientV2 } from '../src/index'; // or -// import { SpotClient } from 'bitget-api'; +// import { RestClientV2 } from 'bitget-api'; -const spotClient = new SpotClient(); - -const symbol = 'BTCUSDT_SPBL'; +const restClient = new RestClientV2(); (async () => { try { - const response = await spotClient.getCandles(symbol, '1min', { + const response = await restClient.getSpotCandles({ + symbol: 'BTCUSDT', + granularity: '1min', limit: '1000', }); + + console.table(response.data); console.log('getCandles: ', response.data.length); } catch (e) { console.error('request failed: ', e); diff --git a/src/index.ts b/src/index.ts index 73d9820..b4583e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +export * from './rest-client-v2'; export * from './broker-client'; export * from './futures-client'; export * from './spot-client'; diff --git a/src/rest-client-v2.ts b/src/rest-client-v2.ts index 1c047e3..0d3dc6d 100644 --- a/src/rest-client-v2.ts +++ b/src/rest-client-v2.ts @@ -1,27 +1,9 @@ import { - NewBatchSpotOrder, - NewSpotOrder, - NewWalletTransfer, - Pagination, APIResponse, - CoinBalance, - SymbolRules, - NewSpotSubTransfer, - NewSpotWithdraw, - CancelSpotOrderV2, - BatchCancelSpotOrderV2, - SpotOrderResult, - NewSpotPlanOrder, - ModifySpotPlanOrder, - CancelSpotPlanOrderParams, - GetSpotPlanOrdersParams, - SpotPlanOrder, - GetHistoricPlanOrdersParams, - SpotMarketTrade, - GetHistoricTradesParams, - VIPFeeRate, - SpotKlineInterval, MarginType, + FuturesAccountBillRequestV2, + FuturesCandlesRequestV2, + SpotCandlesRequestV2, } from './types'; import { REST_CLIENT_TYPE_ENUM, assertMarginType } from './util'; import BaseRestClient from './util/BaseRestClient'; @@ -223,11 +205,11 @@ export class RestClientV2 extends BaseRestClient { return this.getPrivate(`/api/v2/spot/market/orderbook`, params); } - getSpotCandlestickData(params: object): Promise> { + getSpotCandles(params: SpotCandlesRequestV2): Promise> { return this.getPrivate(`/api/v2/spot/market/candles`, params); } - getSpotHistoricCandlestickData(params: object): Promise> { + getSpotHistoricCandles(params: object): Promise> { return this.getPrivate(`/api/v2/spot/market/history-candles`, params); } @@ -394,21 +376,23 @@ export class RestClientV2 extends BaseRestClient { return this.get(`/api/v2/mix/market/merge-depth`, params); } - getFuturesCandlestickData(params: object): Promise> { + getFuturesCandles( + params: FuturesCandlesRequestV2, + ): Promise> { return this.get(`/api/v2/mix/market/candles`, params); } - getFuturesHistoricCandlestickData(params: object): Promise> { + getFuturesHistoricCandles(params: object): Promise> { return this.get(`/api/v2/mix/market/history-candles`, params); } - getFuturesHistoricIndexPriceCandlestick( + getFuturesHistoricIndexPriceCandles( params: object, ): Promise> { return this.get(`/api/v2/mix/market/history-index-candles`, params); } - getFuturesHistoricMarkPriceCandlestick( + getFuturesHistoricMarkPriceCandles( params: object, ): Promise> { return this.get(`/api/v2/mix/market/history-mark-candles`, params); @@ -484,7 +468,9 @@ export class RestClientV2 extends BaseRestClient { return this.postPrivate(`/api/v2/mix/account/set-position-mode`, params); } - getFuturesAccountBills(params: object): Promise> { + getFuturesAccountBills( + params: FuturesAccountBillRequestV2, + ): Promise> { return this.getPrivate(`/api/v2/mix/account/bill`, params); } diff --git a/src/types/request/index.ts b/src/types/request/index.ts index 8fd41c1..a092236 100644 --- a/src/types/request/index.ts +++ b/src/types/request/index.ts @@ -1,4 +1,6 @@ -export * from './broker'; -export * from './futures'; +export * from './v1/brokerV1'; +export * from './v1/futuresV1'; +export * from './v1/spotV1'; +export * from './v2/futures'; +export * from './v2/spot'; export * from './shared'; -export * from './spot'; diff --git a/src/types/request/broker.ts b/src/types/request/v1/brokerV1.ts similarity index 100% rename from src/types/request/broker.ts rename to src/types/request/v1/brokerV1.ts diff --git a/src/types/request/futures.ts b/src/types/request/v1/futuresV1.ts similarity index 98% rename from src/types/request/futures.ts rename to src/types/request/v1/futuresV1.ts index ce316e6..e93fc0d 100644 --- a/src/types/request/futures.ts +++ b/src/types/request/v1/futuresV1.ts @@ -1,4 +1,4 @@ -import { OrderTimeInForce } from './shared'; +import { OrderTimeInForce } from '../shared'; export type FuturesProductType = | 'umcbl' diff --git a/src/types/request/spot.ts b/src/types/request/v1/spotV1.ts similarity index 98% rename from src/types/request/spot.ts rename to src/types/request/v1/spotV1.ts index aec63c4..79b758b 100644 --- a/src/types/request/spot.ts +++ b/src/types/request/v1/spotV1.ts @@ -1,4 +1,4 @@ -import { OrderTimeInForce } from './shared'; +import { OrderTimeInForce } from '../shared'; export type WalletType = 'spot' | 'mix_usdt' | 'mix_usd'; diff --git a/src/types/request/v2/futures.ts b/src/types/request/v2/futures.ts new file mode 100644 index 0000000..5499289 --- /dev/null +++ b/src/types/request/v2/futures.ts @@ -0,0 +1,32 @@ +import { FuturesKlineInterval } from '../v1/futuresV1'; + +export type FuturesProductTypeV2 = + | 'USDT-FUTURES' + | 'COIN-FUTURES' + | 'USDC-FUTURES' + | 'SUSDT-FUTURES' + | 'SCOIN-FUTURES' + | 'SUSDC-FUTURES'; + +export type FuturesKlineTypeV2 = 'MARKET' | 'MARK' | 'INDEX'; + +export interface FuturesAccountBillRequestV2 { + productType: FuturesProductTypeV2; + symbol?: string; + coin?: string; + businessType?: string; + idLessThan?: string; + startTime?: string; + endTime?: string; + limit?: string; +} + +export interface FuturesCandlesRequestV2 { + symbol: string; + productType: FuturesProductTypeV2; + granularity: FuturesKlineInterval; + startTime?: string; + endTime?: string; + kLineType?: FuturesKlineTypeV2; + limit?: string; +} diff --git a/src/types/request/v2/spot.ts b/src/types/request/v2/spot.ts new file mode 100644 index 0000000..6fdeaaa --- /dev/null +++ b/src/types/request/v2/spot.ts @@ -0,0 +1,9 @@ +import { SpotKlineInterval } from '../v1/spotV1'; + +export interface SpotCandlesRequestV2 { + symbol: string; + granularity: SpotKlineInterval; + startTime?: string; + endTime?: string; + limit?: string; +} From 8346e88a3b4101f2dce32ba19d1237f5bd4f09d6 Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 14 Nov 2023 15:39:53 +0000 Subject: [PATCH 06/10] feat(): v2 public websockets & reusable base class for websockets --- .../deprecated-V1-Websockets/ws-public.ts | 76 +++ examples/ws-public.ts | 55 +- src/index.ts | 1 + src/types/websockets/client.ts | 70 +- src/util/BaseWSClient.ts | 630 ++++++++++++++++++ src/util/WsStore.ts | 83 +-- src/util/WsStore.types.ts | 28 + src/util/websocket-util.ts | 31 +- src/websocket-client-v2.ts | 141 ++++ src/websocket-client.ts | 25 +- test/ws.util.ts | 11 +- 11 files changed, 1054 insertions(+), 97 deletions(-) create mode 100644 examples/deprecated-V1-Websockets/ws-public.ts create mode 100644 src/util/BaseWSClient.ts create mode 100644 src/util/WsStore.types.ts create mode 100644 src/websocket-client-v2.ts diff --git a/examples/deprecated-V1-Websockets/ws-public.ts b/examples/deprecated-V1-Websockets/ws-public.ts new file mode 100644 index 0000000..fb2af37 --- /dev/null +++ b/examples/deprecated-V1-Websockets/ws-public.ts @@ -0,0 +1,76 @@ +import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../../src'; + +// or +// import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bitget-api'; + +(async () => { + const logger = { + ...DefaultLogger, + silly: (...params) => console.log('silly', ...params), + }; + + const wsClient = new WebsocketClient( + { + // restOptions: { + // optionally provide rest options, e.g. to pass through a proxy + // }, + }, + logger, + ); + + wsClient.on('update', (data) => { + console.log('WS raw message received ', data); + // console.log('WS raw message received ', JSON.stringify(data, null, 2)); + }); + + wsClient.on('open', (data) => { + console.log('WS connection opened:', data.wsKey); + }); + wsClient.on('response', (data) => { + console.log('WS response: ', JSON.stringify(data, null, 2)); + }); + wsClient.on('reconnect', ({ wsKey }) => { + console.log('WS automatically reconnecting.... ', wsKey); + }); + wsClient.on('reconnected', (data) => { + console.log('WS reconnected ', data?.wsKey); + }); + wsClient.on('exception', (data) => { + console.log('WS error', data); + }); + + /** + * Public events + */ + + const symbol = 'BTCUSDT'; + + // Spot public + // tickers + // wsClient.subscribeTopic('SP', 'ticker', symbol); + // // candles + // wsClient.subscribeTopic('SP', 'candle1m', symbol); + // // orderbook updates + wsClient.subscribeTopic('SP', 'books', symbol); + // // trades + // wsClient.subscribeTopic('SP', 'trade', symbol); + + // // Futures public + + // // tickers + // wsClient.subscribeTopic('MC', 'ticker', symbol); + // // candles + // wsClient.subscribeTopic('MC', 'candle1m', symbol); + // // orderbook updates + // wsClient.subscribeTopic('MC', 'books', symbol); + // // trades + // wsClient.subscribeTopic('MC', 'trade', symbol); + + // Topics are tracked per websocket type + // Get a list of subscribed topics (e.g. for spot topics) (after a 5 second delay) + setTimeout(() => { + const publicSpotTopics = wsClient.getWsStore().getTopics(WS_KEY_MAP.spotv1); + + console.log('public spot topics: ', publicSpotTopics); + }, 5 * 1000); +})(); diff --git a/examples/ws-public.ts b/examples/ws-public.ts index 52a8701..19dc48e 100644 --- a/examples/ws-public.ts +++ b/examples/ws-public.ts @@ -1,7 +1,7 @@ -import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src'; +import { DefaultLogger, WS_KEY_MAP, WebsocketClientV2 } from '../src'; // or -// import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bitget-api'; +// import { DefaultLogger, WS_KEY_MAP, WebsocketClientV2 } from 'bitget-api'; (async () => { const logger = { @@ -9,7 +9,7 @@ import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src'; silly: (...params) => console.log('silly', ...params), }; - const wsClient = new WebsocketClient( + const wsClient = new WebsocketClientV2( { // restOptions: { // optionally provide rest options, e.g. to pass through a proxy @@ -45,32 +45,39 @@ import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from '../src'; const symbol = 'BTCUSDT'; - // // Spot public - // // tickers - // wsClient.subscribeTopic('SP', 'ticker', symbol); - // // candles - // wsClient.subscribeTopic('SP', 'candle1m', symbol); - // // orderbook updates - wsClient.subscribeTopic('SP', 'books', symbol); - // // trades - // wsClient.subscribeTopic('SP', 'trade', symbol); + // Spot public - // // Futures public + // tickers + wsClient.subscribeTopic('SPOT', 'ticker', symbol); - // // tickers - // wsClient.subscribeTopic('MC', 'ticker', symbol); - // // candles - // wsClient.subscribeTopic('MC', 'candle1m', symbol); - // // orderbook updates - // wsClient.subscribeTopic('MC', 'books', symbol); - // // trades - // wsClient.subscribeTopic('MC', 'trade', symbol); + // candles + // wsClient.subscribeTopic('SPOT', 'candle1m', symbol); + + // orderbook updates + // wsClient.subscribeTopic('SPOT', 'books', symbol); + + // trades + // wsClient.subscribeTopic('SPOT', 'trade', symbol); + + // Futures public + + // tickers + // wsClient.subscribeTopic('USDT-FUTURES', 'ticker', symbol); + + // candles + // wsClient.subscribeTopic('USDT-FUTURES', 'candle1m', symbol); + + // orderbook updates + // wsClient.subscribeTopic('USDT-FUTURES', 'books', symbol); + + // trades + // wsClient.subscribeTopic('USDT-FUTURES', 'trade', symbol); // Topics are tracked per websocket type - // Get a list of subscribed topics (e.g. for spot topics) (after a 5 second delay) + // Get a list of subscribed topics (e.g. all public topics) (after a 5 second delay) setTimeout(() => { - const publicSpotTopics = wsClient.getWsStore().getTopics(WS_KEY_MAP.spotv1); + const publicTopics = wsClient.getWsStore().getTopics(WS_KEY_MAP.v2Public); - console.log('public spot topics: ', publicSpotTopics); + console.log('public topics: ', publicTopics); }, 5 * 1000); })(); diff --git a/src/index.ts b/src/index.ts index b4583e2..771b2dc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ export * from './broker-client'; export * from './futures-client'; export * from './spot-client'; export * from './websocket-client'; +export * from './websocket-client-v2'; export * from './util/logger'; export * from './util'; export * from './types'; diff --git a/src/types/websockets/client.ts b/src/types/websockets/client.ts index 1d52d3d..6ea8be0 100644 --- a/src/types/websockets/client.ts +++ b/src/types/websockets/client.ts @@ -1,4 +1,9 @@ import { WS_KEY_MAP } from '../../util'; +import { FuturesProductTypeV2 } from '../request'; + +/** A "topic" is always a string */ +export type BitgetInstType = 'SP' | 'SPBL' | 'MC' | 'UMCBL' | 'DMCBL'; +export type BitgetInstTypeV2 = 'SPOT' | FuturesProductTypeV2; export type WsPublicSpotTopic = | 'ticker' @@ -29,9 +34,70 @@ export type WsPublicTopic = WsPublicSpotTopic | WsPublicFuturesTopic; export type WsPrivateTopic = WsPrivateSpotTopic | WsPrivateFuturesTopic; export type WsTopic = WsPublicTopic | WsPrivateTopic; -/** This is used to differentiate between each of the available websocket streams */ -export type WsKey = typeof WS_KEY_MAP[keyof typeof WS_KEY_MAP]; +export type WsPublicTopicV2 = + | 'index-price' // margin only + | 'ticker' + | 'candle1m' + | 'candle5m' + | 'candle15' + | 'candle30m' + | 'candle1H' + | 'candle4H' + | 'candle6H' + | 'candle12H' + | 'candle1D' + | 'candle3D' + | 'candle1W' + | 'candle1M' + | 'candle6Hutc' + | 'candle12Hutc' + | 'candle1Dutc' + | 'candle3Dutc' + | 'candle1Wutc' + | 'candle1Mutc' + | 'trade' + | 'books' + | 'books1' + | 'books5' + | 'books15'; +export type WSPrivateTopicFuturesV2 = + | 'positions' + | 'orders-algo' + | 'positions-history'; + +export type WsPrivateTopicV2 = 'account' | 'orders' | WSPrivateTopicFuturesV2; + +export type WsTopicV2 = WsPublicTopicV2; + +/** This is used to differentiate between each of the available websocket streams */ +export type WsKey = (typeof WS_KEY_MAP)[keyof typeof WS_KEY_MAP]; + +/** + * Event args for subscribing/unsubscribing + */ + +// TODO: generalise so this can be made a reusable module for other clients +export interface WsTopicSubscribeEventArgs { + instType: BitgetInstType; + channel: WsTopic; + /** The symbol, e.g. "BTCUSDT" */ + instId: string; +} + +export type WsTopicSubscribeCommonArgsV2 = { + instType: BitgetInstTypeV2; + channel: WsTopicV2; +}; + +export type WsTopicSubscribePublicArgsV2 = WsTopicSubscribeCommonArgsV2 & { + channel: WsPublicTopicV2; + instId: string; +}; + +export type WsTopicSubscribeEventArgsV2 = WsTopicSubscribePublicArgsV2; + +/** General configuration for the WebsocketClient */ export interface WSClientConfigurableOptions { /** Your API key */ apiKey?: string; diff --git a/src/util/BaseWSClient.ts b/src/util/BaseWSClient.ts new file mode 100644 index 0000000..66a30ae --- /dev/null +++ b/src/util/BaseWSClient.ts @@ -0,0 +1,630 @@ +import EventEmitter from 'events'; +import WebSocket from 'isomorphic-ws'; + +import { WebsocketClientOptions, WSClientConfigurableOptions } from '../types'; +import WsStore from './WsStore'; +import { WsConnectionStateEnum } from './WsStore.types'; +import { DefaultLogger } from './logger'; +import { isWsPong } from './requestUtils'; +import { getWsAuthSignature } from './websocket-util'; + +interface WSClientEventMap { + /** Connection opened. If this connection was previously opened and reconnected, expect the reconnected event instead */ + open: (evt: { wsKey: WsKey; event: any }) => void; + /** Reconnecting a dropped connection */ + reconnect: (evt: { wsKey: WsKey; event: any }) => void; + /** Successfully reconnected a connection that dropped */ + reconnected: (evt: { wsKey: WsKey; event: any }) => void; + /** Connection closed */ + close: (evt: { wsKey: WsKey; event: any }) => void; + /** Received reply to websocket command (e.g. after subscribing to topics) */ + response: (response: any & { wsKey: WsKey }) => void; + /** Received data for topic */ + update: (response: any & { wsKey: WsKey }) => void; + /** Exception from ws client OR custom listeners (e.g. if you throw inside your event handler) */ + exception: (response: any & { wsKey: WsKey }) => void; + /** Confirmation that a connection successfully authenticated */ + authenticated: (event: { wsKey: WsKey; event: any }) => void; +} + +// Type safety for on and emit handlers: https://stackoverflow.com/a/61609010/880837 +export interface BaseWebsocketClient< + TWSKey extends string, + TWSTopicSubscribeEventArgs extends object, +> { + on>( + event: U, + listener: WSClientEventMap[U], + ): this; + + emit>( + event: U, + ...args: Parameters[U]> + ): boolean; +} + +export interface BaseWSClientImpl {} + +const LOGGER_CATEGORY = { category: 'bitget-ws' }; + +export abstract class BaseWebsocketClient< + TWSKey extends string, + TWSTopicSubscribeEventArgs extends object, +> extends EventEmitter { + private wsStore: WsStore; + + protected logger: typeof DefaultLogger; + protected options: WebsocketClientOptions; + + constructor( + options: WSClientConfigurableOptions, + logger?: typeof DefaultLogger, + ) { + super(); + + this.logger = logger || DefaultLogger; + this.wsStore = new WsStore(this.logger); + + this.options = { + pongTimeout: 1000, + pingInterval: 10000, + reconnectTimeout: 500, + recvWindow: 0, + ...options, + }; + } + + protected abstract getWsKeyForTopic( + subscribeEvent: TWSTopicSubscribeEventArgs, + isPrivate?: boolean, + ): TWSKey; + + protected abstract isPrivateChannel( + subscribeEvent: TWSTopicSubscribeEventArgs, + ): boolean; + + protected abstract shouldAuthOnConnect(wsKey: TWSKey): boolean; + protected abstract getWsUrl(wsKey: TWSKey): string; + protected abstract getMaxTopicsPerSubscribeEvent( + wsKey: TWSKey, + ): number | null; + + /** + * Request connection of all dependent (public & private) websockets, instead of waiting for automatic connection by library + */ + abstract connectAll(): Promise[]; + + /** + * Subscribe to topics & track/persist them. They will be automatically resubscribed to if the connection drops/reconnects. + * @param wsTopics topic or list of topics + * @param isPrivateTopic optional - the library will try to detect private topics, you can use this to mark a topic as private (if the topic isn't recognised yet) + */ + public subscribe( + wsTopics: TWSTopicSubscribeEventArgs[] | TWSTopicSubscribeEventArgs, + isPrivateTopic?: boolean, + ) { + const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics]; + + topics.forEach((topic) => { + const wsKey = this.getWsKeyForTopic(topic, isPrivateTopic); + + // Persist this topic to the expected topics list + this.wsStore.addTopic(wsKey, topic); + + // TODO: tidy up unsubscribe too, also in other connectors + + // if connected, send subscription request + if ( + this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTED) + ) { + // if not authenticated, dont sub to private topics yet. + // This'll happen automatically once authenticated + const isAuthenticated = this.wsStore.get(wsKey)?.isAuthenticated; + if (!isAuthenticated) { + return this.requestSubscribeTopics( + wsKey, + topics.filter((topic) => !this.isPrivateChannel(topic)), + ); + } + return this.requestSubscribeTopics(wsKey, topics); + } + + // start connection process if it hasn't yet begun. Topics are automatically subscribed to on-connect + if ( + !this.wsStore.isConnectionState( + wsKey, + WsConnectionStateEnum.CONNECTING, + ) && + !this.wsStore.isConnectionState( + wsKey, + WsConnectionStateEnum.RECONNECTING, + ) + ) { + return this.connect(wsKey); + } + }); + } + + /** + * Unsubscribe from topics & remove them from memory. They won't be re-subscribed to if the connection reconnects. + * @param wsTopics topic or list of topics + * @param isPrivateTopic optional - the library will try to detect private topics, you can use this to mark a topic as private (if the topic isn't recognised yet) + */ + public unsubscribe( + wsTopics: TWSTopicSubscribeEventArgs[] | TWSTopicSubscribeEventArgs, + isPrivateTopic?: boolean, + ) { + const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics]; + topics.forEach((topic) => { + this.wsStore.deleteTopic( + this.getWsKeyForTopic(topic, isPrivateTopic), + topic, + ); + + const wsKey = this.getWsKeyForTopic(topic, isPrivateTopic); + + // unsubscribe request only necessary if active connection exists + if ( + this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTED) + ) { + this.requestUnsubscribeTopics(wsKey, [topic]); + } + }); + } + + /** Get the WsStore that tracks websockets & topics */ + public getWsStore(): WsStore { + return this.wsStore; + } + + public close(wsKey: TWSKey, force?: boolean) { + this.logger.info('Closing connection', { ...LOGGER_CATEGORY, wsKey }); + this.setWsState(wsKey, WsConnectionStateEnum.CLOSING); + this.clearTimers(wsKey); + + const ws = this.getWs(wsKey); + ws?.close(); + if (force) { + ws?.terminate(); + } + } + + public closeAll(force?: boolean) { + this.wsStore.getKeys().forEach((key: TWSKey) => { + this.close(key, force); + }); + } + + /** + * Request connection to a specific websocket, instead of waiting for automatic connection. + */ + protected async connect(wsKey: TWSKey): Promise { + try { + if (this.wsStore.isWsOpen(wsKey)) { + this.logger.error( + 'Refused to connect to ws with existing active connection', + { ...LOGGER_CATEGORY, wsKey }, + ); + return this.wsStore.getWs(wsKey); + } + + if ( + this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTING) + ) { + this.logger.error( + 'Refused to connect to ws, connection attempt already active', + { ...LOGGER_CATEGORY, wsKey }, + ); + return; + } + + if ( + !this.wsStore.getConnectionState(wsKey) || + this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.INITIAL) + ) { + this.setWsState(wsKey, WsConnectionStateEnum.CONNECTING); + } + + const url = this.getWsUrl(wsKey); // + authParams; + const ws = this.connectToWsUrl(url, wsKey); + + return this.wsStore.setWs(wsKey, ws); + } catch (err) { + this.parseWsError('Connection failed', err, wsKey); + this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!); + } + } + + private parseWsError(context: string, error: any, wsKey: TWSKey) { + if (!error.message) { + this.logger.error(`${context} due to unexpected error: `, error); + this.emit('response', { ...error, wsKey }); + this.emit('exception', { ...error, wsKey }); + return; + } + + switch (error.message) { + case 'Unexpected server response: 401': + this.logger.error(`${context} due to 401 authorization failure.`, { + ...LOGGER_CATEGORY, + wsKey, + }); + break; + + default: + this.logger.error( + `${context} due to unexpected response error: "${ + error?.msg || error?.message || error + }"`, + { ...LOGGER_CATEGORY, wsKey, error }, + ); + break; + } + + this.emit('response', { ...error, wsKey }); + this.emit('exception', { ...error, wsKey }); + } + + /** Get a signature, build the auth request and send it */ + private async sendAuthRequest(wsKey: TWSKey): Promise { + try { + const { apiKey, apiSecret, apiPass, recvWindow } = this.options; + + const { signature, expiresAt } = await getWsAuthSignature( + apiKey, + apiSecret, + apiPass, + recvWindow, + ); + + this.logger.info(`Sending auth request...`, { + ...LOGGER_CATEGORY, + wsKey, + }); + + const request = { + op: 'login', + args: [ + { + apiKey: this.options.apiKey, + passphrase: this.options.apiPass, + timestamp: expiresAt, + sign: signature, + }, + ], + }; + // console.log('ws auth req', request); + + return this.tryWsSend(wsKey, JSON.stringify(request)); + } catch (e) { + this.logger.silly(e, { ...LOGGER_CATEGORY, wsKey }); + } + } + + private reconnectWithDelay(wsKey: TWSKey, connectionDelayMs: number) { + this.clearTimers(wsKey); + if ( + this.wsStore.getConnectionState(wsKey) !== + WsConnectionStateEnum.CONNECTING + ) { + this.setWsState(wsKey, WsConnectionStateEnum.RECONNECTING); + } + + this.wsStore.get(wsKey, true).activeReconnectTimer = setTimeout(() => { + this.logger.info('Reconnecting to websocket', { + ...LOGGER_CATEGORY, + wsKey, + }); + this.connect(wsKey); + }, connectionDelayMs); + } + + private ping(wsKey: TWSKey) { + if (this.wsStore.get(wsKey, true).activePongTimer) { + return; + } + + this.clearPongTimer(wsKey); + + this.logger.silly('Sending ping', { ...LOGGER_CATEGORY, wsKey }); + this.tryWsSend(wsKey, 'ping'); + + this.wsStore.get(wsKey, true).activePongTimer = setTimeout(() => { + this.logger.info('Pong timeout - closing socket to reconnect', { + ...LOGGER_CATEGORY, + wsKey, + }); + this.getWs(wsKey)?.terminate(); + delete this.wsStore.get(wsKey, true).activePongTimer; + }, this.options.pongTimeout); + } + + private clearTimers(wsKey: TWSKey) { + this.clearPingTimer(wsKey); + this.clearPongTimer(wsKey); + const wsState = this.wsStore.get(wsKey); + if (wsState?.activeReconnectTimer) { + clearTimeout(wsState.activeReconnectTimer); + } + } + + // Send a ping at intervals + private clearPingTimer(wsKey: TWSKey) { + const wsState = this.wsStore.get(wsKey); + if (wsState?.activePingTimer) { + clearInterval(wsState.activePingTimer); + wsState.activePingTimer = undefined; + } + } + + // Expect a pong within a time limit + private clearPongTimer(wsKey: TWSKey) { + const wsState = this.wsStore.get(wsKey); + if (wsState?.activePongTimer) { + clearTimeout(wsState.activePongTimer); + wsState.activePongTimer = undefined; + } + } + + /** + * @private Use the `subscribe(topics)` method to subscribe to topics. Send WS message to subscribe to topics. + */ + private requestSubscribeTopics( + wsKey: TWSKey, + topics: TWSTopicSubscribeEventArgs[], + ) { + if (!topics.length) { + return; + } + + const maxTopicsPerEvent = this.getMaxTopicsPerSubscribeEvent(wsKey); + if (maxTopicsPerEvent && topics.length > maxTopicsPerEvent) { + this.logger.silly( + `Subscribing to topics in batches of ${maxTopicsPerEvent}`, + ); + for (var i = 0; i < topics.length; i += maxTopicsPerEvent) { + const batch = topics.slice(i, i + maxTopicsPerEvent); + this.logger.silly(`Subscribing to batch of ${batch.length}`); + this.requestSubscribeTopics(wsKey, batch); + } + this.logger.silly( + `Finished batch subscribing to ${topics.length} topics`, + ); + return; + } + + const wsMessage = JSON.stringify({ + op: 'subscribe', + args: topics, + }); + + this.tryWsSend(wsKey, wsMessage); + } + + /** + * @private Use the `unsubscribe(topics)` method to unsubscribe from topics. Send WS message to unsubscribe from topics. + */ + private requestUnsubscribeTopics( + wsKey: TWSKey, + topics: TWSTopicSubscribeEventArgs[], + ) { + if (!topics.length) { + return; + } + + const maxTopicsPerEvent = this.getMaxTopicsPerSubscribeEvent(wsKey); + if (maxTopicsPerEvent && topics.length > maxTopicsPerEvent) { + this.logger.silly( + `Unsubscribing to topics in batches of ${maxTopicsPerEvent}`, + ); + for (var i = 0; i < topics.length; i += maxTopicsPerEvent) { + const batch = topics.slice(i, i + maxTopicsPerEvent); + this.logger.silly(`Unsubscribing to batch of ${batch.length}`); + this.requestUnsubscribeTopics(wsKey, batch); + } + this.logger.silly( + `Finished batch unsubscribing to ${topics.length} topics`, + ); + return; + } + + const wsMessage = JSON.stringify({ + op: 'unsubscribe', + args: topics, + }); + + this.tryWsSend(wsKey, wsMessage); + } + + public tryWsSend(wsKey: TWSKey, wsMessage: string) { + try { + this.logger.silly(`Sending upstream ws message: `, { + ...LOGGER_CATEGORY, + wsMessage, + wsKey, + }); + if (!wsKey) { + throw new Error( + 'Cannot send message due to no known websocket for this wsKey', + ); + } + const ws = this.getWs(wsKey); + if (!ws) { + throw new Error( + `${wsKey} socket not connected yet, call "connectAll()" first then try again when the "open" event arrives`, + ); + } + ws.send(wsMessage); + } catch (e) { + this.logger.error(`Failed to send WS message`, { + ...LOGGER_CATEGORY, + wsMessage, + wsKey, + exception: e, + }); + } + } + + private connectToWsUrl(url: string, wsKey: TWSKey): WebSocket { + this.logger.silly(`Opening WS connection to URL: ${url}`, { + ...LOGGER_CATEGORY, + wsKey, + }); + + const agent = this.options.requestOptions?.agent; + const ws = new WebSocket(url, undefined, agent ? { agent } : undefined); + ws.onopen = (event) => this.onWsOpen(event, wsKey); + ws.onmessage = (event) => this.onWsMessage(event, wsKey); + ws.onerror = (event) => this.parseWsError('websocket error', event, wsKey); + ws.onclose = (event) => this.onWsClose(event, wsKey); + + return ws; + } + + private async onWsOpen(event, wsKey: TWSKey) { + if ( + this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTING) + ) { + this.logger.info('Websocket connected', { + ...LOGGER_CATEGORY, + wsKey, + }); + this.emit('open', { wsKey, event }); + } else if ( + this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.RECONNECTING) + ) { + this.logger.info('Websocket reconnected', { ...LOGGER_CATEGORY, wsKey }); + this.emit('reconnected', { wsKey, event }); + } + + this.setWsState(wsKey, WsConnectionStateEnum.CONNECTED); + + // Some websockets require an auth packet to be sent after opening the connection + if (this.shouldAuthOnConnect(wsKey)) { + await this.sendAuthRequest(wsKey); + } + + // Reconnect to topics known before it connected + // Private topics will be resubscribed to once reconnected + const topics = [...this.wsStore.getTopics(wsKey)]; + const publicTopics = topics.filter( + (topic) => !this.isPrivateChannel(topic), + ); + this.requestSubscribeTopics(wsKey, publicTopics); + + this.wsStore.get(wsKey, true)!.activePingTimer = setInterval( + () => this.ping(wsKey), + this.options.pingInterval, + ); + } + + /** Handle subscription to private topics _after_ authentication successfully completes asynchronously */ + private onWsAuthenticated(wsKey: TWSKey) { + const wsState = this.wsStore.get(wsKey, true); + wsState.isAuthenticated = true; + + const topics = [...this.wsStore.getTopics(wsKey)]; + const privateTopics = topics.filter((topic) => + this.isPrivateChannel(topic), + ); + + if (privateTopics.length) { + this.subscribe(privateTopics, true); + } + } + + private onWsMessage(event: unknown, wsKey: TWSKey) { + try { + // any message can clear the pong timer - wouldn't get a message if the ws wasn't working + this.clearPongTimer(wsKey); + + if (isWsPong(event)) { + this.logger.silly('Received pong', { ...LOGGER_CATEGORY, wsKey }); + return; + } + + const msg = JSON.parse((event && event['data']) || event); + const emittableEvent = { ...msg, wsKey }; + + if (typeof msg === 'object') { + if (typeof msg['code'] === 'number') { + if (msg.event === 'login' && msg.code === 0) { + this.logger.info(`Successfully authenticated WS client`, { + ...LOGGER_CATEGORY, + wsKey, + }); + this.emit('response', emittableEvent); + this.emit('authenticated', emittableEvent); + this.onWsAuthenticated(wsKey); + return; + } + } + + if (msg['event']) { + if (msg.event === 'error') { + this.logger.error(`WS Error received`, { + ...LOGGER_CATEGORY, + wsKey, + message: msg || 'no message', + // messageType: typeof msg, + // messageString: JSON.stringify(msg), + event, + }); + this.emit('exception', emittableEvent); + this.emit('response', emittableEvent); + return; + } + return this.emit('response', emittableEvent); + } + + if (msg['arg']) { + return this.emit('update', emittableEvent); + } + } + + this.logger.warning('Unhandled/unrecognised ws event message', { + ...LOGGER_CATEGORY, + message: msg || 'no message', + // messageType: typeof msg, + // messageString: JSON.stringify(msg), + event, + wsKey, + }); + + // fallback emit anyway + return this.emit('update', emittableEvent); + } catch (e) { + this.logger.error('Failed to parse ws event message', { + ...LOGGER_CATEGORY, + error: e, + event, + wsKey, + }); + } + } + + private onWsClose(event: unknown, wsKey: TWSKey) { + this.logger.info('Websocket connection closed', { + ...LOGGER_CATEGORY, + wsKey, + }); + + if ( + this.wsStore.getConnectionState(wsKey) !== WsConnectionStateEnum.CLOSING + ) { + this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!); + this.emit('reconnect', { wsKey, event }); + } else { + this.setWsState(wsKey, WsConnectionStateEnum.INITIAL); + this.emit('close', { wsKey, event }); + } + } + + private getWs(wsKey: TWSKey) { + return this.wsStore.getWs(wsKey); + } + + private setWsState(wsKey: TWSKey, state: WsConnectionStateEnum) { + this.wsStore.setConnectionState(wsKey, state); + } +} diff --git a/src/util/WsStore.ts b/src/util/WsStore.ts index 1151dea..19f990d 100644 --- a/src/util/WsStore.ts +++ b/src/util/WsStore.ts @@ -1,50 +1,8 @@ import WebSocket from 'isomorphic-ws'; -import { WsPrivateTopic, WsTopic } from '../types'; import { DefaultLogger } from './logger'; +import { WsConnectionStateEnum, WsStoredState } from './WsStore.types'; -export enum WsConnectionStateEnum { - INITIAL = 0, - CONNECTING = 1, - CONNECTED = 2, - CLOSING = 3, - RECONNECTING = 4, - // ERROR = 5, -} -/** A "topic" is always a string */ - -export type BitgetInstType = 'SP' | 'SPBL' | 'MC' | 'UMCBL' | 'DMCBL'; - -// TODO: generalise so this can be made a reusable module for other clients -export interface WsTopicSubscribeEventArgs { - instType: BitgetInstType; - channel: WsTopic; - /** The symbol, e.g. "BTCUSDT" */ - instId: string; -} - -type WsTopicList = Set; - -interface WsStoredState { - /** The currently active websocket connection */ - ws?: WebSocket; - /** The current lifecycle state of the connection (enum) */ - connectionState?: WsConnectionStateEnum; - /** A timer that will send an upstream heartbeat (ping) when it expires */ - activePingTimer?: ReturnType | undefined; - /** A timer tracking that an upstream heartbeat was sent, expecting a reply before it expires */ - activePongTimer?: ReturnType | undefined; - /** If a reconnection is in progress, this will have the timer for the delayed reconnect */ - activeReconnectTimer?: ReturnType | undefined; - /** - * All the topics we are expected to be subscribed to (and we automatically resubscribe to if the connection drops) - * - * A "Set" and a deep object match are used to ensure we only subscribe to a topic once (tracking a list of unique topics we're expected to be connected to) - */ - subscribedTopics: WsTopicList; - isAuthenticated?: boolean; -} - -function isDeepObjectMatch(object1: any, object2: any) { +function isDeepObjectMatch(object1: object, object2: object) { for (const key in object1) { if (object1[key] !== object2[key]) { return false; @@ -53,8 +11,12 @@ function isDeepObjectMatch(object1: any, object2: any) { return true; } -export default class WsStore { - private wsState: Record = {}; +export default class WsStore< + WsKey extends string, + TWSTopicSubscribeEventArgs extends object, +> { + private wsState: Record> = + {}; private logger: typeof DefaultLogger; constructor(logger: typeof DefaultLogger) { @@ -62,9 +24,18 @@ export default class WsStore { } /** Get WS stored state for key, optionally create if missing */ - get(key: WsKey, createIfMissing?: true): WsStoredState; - get(key: WsKey, createIfMissing?: false): WsStoredState | undefined; - get(key: WsKey, createIfMissing?: boolean): WsStoredState | undefined { + get( + key: WsKey, + createIfMissing?: true, + ): WsStoredState; + get( + key: WsKey, + createIfMissing?: false, + ): WsStoredState | undefined; + get( + key: WsKey, + createIfMissing?: boolean, + ): WsStoredState | undefined { if (this.wsState[key]) { return this.wsState[key]; } @@ -78,7 +49,7 @@ export default class WsStore { return Object.keys(this.wsState) as WsKey[]; } - create(key: WsKey): WsStoredState | undefined { + create(key: WsKey): WsStoredState | undefined { if (this.hasExistingActiveConnection(key)) { this.logger.warning( 'WsStore setConnection() overwriting existing open connection: ', @@ -86,7 +57,7 @@ export default class WsStore { ); } this.wsState[key] = { - subscribedTopics: new Set(), + subscribedTopics: new Set(), connectionState: WsConnectionStateEnum.INITIAL, }; return this.get(key); @@ -151,11 +122,11 @@ export default class WsStore { /* subscribed topics */ - getTopics(key: WsKey): WsTopicList { + getTopics(key: WsKey): Set { return this.get(key, true).subscribedTopics; } - getTopicsByKey(): Record { + getTopicsByKey(): Record> { const result = {}; for (const refKey in this.wsState) { result[refKey] = this.getTopics(refKey as WsKey); @@ -164,7 +135,7 @@ export default class WsStore { } // Since topics are objects we can't rely on the set to detect duplicates - getMatchingTopic(key: WsKey, topic: WsTopicSubscribeEventArgs) { + getMatchingTopic(key: WsKey, topic: TWSTopicSubscribeEventArgs) { // if (typeof topic === 'string') { // return this.getMatchingTopic(key, { channel: topic }); // } @@ -177,7 +148,7 @@ export default class WsStore { } } - addTopic(key: WsKey, topic: WsTopicSubscribeEventArgs) { + addTopic(key: WsKey, topic: TWSTopicSubscribeEventArgs) { // if (typeof topic === 'string') { // return this.addTopic(key, { // instType: 'sp', @@ -193,7 +164,7 @@ export default class WsStore { return this.getTopics(key).add(topic); } - deleteTopic(key: WsKey, topic: WsTopicSubscribeEventArgs) { + deleteTopic(key: WsKey, topic: TWSTopicSubscribeEventArgs) { // Check if we're subscribed to a topic like this const storedTopic = this.getMatchingTopic(key, topic); if (storedTopic) { diff --git a/src/util/WsStore.types.ts b/src/util/WsStore.types.ts new file mode 100644 index 0000000..5eb95d6 --- /dev/null +++ b/src/util/WsStore.types.ts @@ -0,0 +1,28 @@ +export enum WsConnectionStateEnum { + INITIAL = 0, + CONNECTING = 1, + CONNECTED = 2, + CLOSING = 3, + RECONNECTING = 4, + // ERROR = 5, +} + +export interface WsStoredState { + /** The currently active websocket connection */ + ws?: WebSocket; + /** The current lifecycle state of the connection (enum) */ + connectionState?: WsConnectionStateEnum; + /** A timer that will send an upstream heartbeat (ping) when it expires */ + activePingTimer?: ReturnType | undefined; + /** A timer tracking that an upstream heartbeat was sent, expecting a reply before it expires */ + activePongTimer?: ReturnType | undefined; + /** If a reconnection is in progress, this will have the timer for the delayed reconnect */ + activeReconnectTimer?: ReturnType | undefined; + /** + * All the topics we are expected to be subscribed to on this connection (and we automatically resubscribe to if the connection drops) + * + * A "Set" and a deep-object-match are used to ensure we only subscribe to a topic once (tracking a list of unique topics we're expected to be connected to) + */ + subscribedTopics: Set; + isAuthenticated?: boolean; +} diff --git a/src/util/websocket-util.ts b/src/util/websocket-util.ts index 231bf3e..fe069d9 100644 --- a/src/util/websocket-util.ts +++ b/src/util/websocket-util.ts @@ -1,6 +1,11 @@ -import { WsKey } from '../types'; +import { + BitgetInstType, + WsKey, + WsPrivateTopicV2, + WsTopicSubscribeEventArgs, + WsTopicSubscribePublicArgsV2, +} from '../types'; import { signMessage } from './node-support'; -import { BitgetInstType, WsTopicSubscribeEventArgs } from './WsStore'; /** * Some exchanges have two livenet environments, some have test environments, some dont. This allows easy flexibility for different exchanges. @@ -67,10 +72,21 @@ export const PUBLIC_WS_KEYS = [] as WsKey[]; */ export const PRIVATE_TOPICS = ['account', 'orders', 'positions', 'ordersAlgo']; +export const PRIVATE_TOPICS_V2: WsPrivateTopicV2[] = [ + 'account', + 'orders', + 'orders-algo', + 'positions', + 'positions-history', +]; + export function isPrivateChannel( channel: TChannel, ): boolean { - return PRIVATE_TOPICS.includes(channel); + return ( + PRIVATE_TOPICS.includes(channel) || + PRIVATE_TOPICS_V2.includes(channel as any) + ); } export function getWsKeyForTopic( @@ -97,6 +113,15 @@ export function getWsKeyForTopic( } } +export function getWsKeyForTopicV2( + subscribeEvent: WsTopicSubscribePublicArgsV2, + isPrivate?: boolean, +): WsKey { + return isPrivate || isPrivateChannel(subscribeEvent.channel) + ? WS_KEY_MAP.v2Private + : WS_KEY_MAP.v2Public; +} + /** Force subscription requests to be sent in smaller batches, if a number is returned */ export function getMaxTopicsPerSubscribeEvent(wsKey: WsKey): number | null { switch (wsKey) { diff --git a/src/websocket-client-v2.ts b/src/websocket-client-v2.ts new file mode 100644 index 0000000..e13e0d6 --- /dev/null +++ b/src/websocket-client-v2.ts @@ -0,0 +1,141 @@ +import WebSocket from 'isomorphic-ws'; + +import { + BitgetInstTypeV2, + WebsocketClientOptions, + WsKey, + WsTopicSubscribeEventArgsV2, + WsTopicV2, +} from './types'; + +import { + WS_AUTH_ON_CONNECT_KEYS, + WS_KEY_MAP, + DefaultLogger, + WS_BASE_URL_MAP, + neverGuard, + getMaxTopicsPerSubscribeEvent, + isPrivateChannel, +} from './util'; + +import { BaseWebsocketClient } from './util/BaseWSClient'; + +const LOGGER_CATEGORY = { category: 'bitget-ws' }; + +export class WebsocketClientV2 extends BaseWebsocketClient< + WsKey, + WsTopicSubscribeEventArgsV2 +> { + protected logger: typeof DefaultLogger; + protected options: WebsocketClientOptions; + + protected getWsKeyForTopic( + subscribeEvent: WsTopicSubscribeEventArgsV2, + isPrivate?: boolean, + ): WsKey { + return isPrivate || isPrivateChannel(subscribeEvent.channel) + ? WS_KEY_MAP.v2Private + : WS_KEY_MAP.v2Public; + } + + protected isPrivateChannel( + subscribeEvent: WsTopicSubscribeEventArgsV2, + ): boolean { + return isPrivateChannel(subscribeEvent.channel); + } + + protected shouldAuthOnConnect(wsKey: WsKey): boolean { + return WS_AUTH_ON_CONNECT_KEYS.includes(wsKey as WsKey); + } + + protected getWsUrl(wsKey: WsKey): string { + if (this.options.wsUrl) { + return this.options.wsUrl; + } + + const networkKey = 'livenet'; + + switch (wsKey) { + case WS_KEY_MAP.spotv1: + case WS_KEY_MAP.mixv1: { + throw new Error( + `Use the WebsocketClient instead of WebsocketClientV2 for V1 websockets`, + ); + } + case WS_KEY_MAP.v2Private: { + return WS_BASE_URL_MAP.v2Private.all[networkKey]; + } + case WS_KEY_MAP.v2Public: { + return WS_BASE_URL_MAP.v2Public.all[networkKey]; + } + default: { + this.logger.error('getWsUrl(): Unhandled wsKey: ', { + ...LOGGER_CATEGORY, + wsKey, + }); + throw neverGuard(wsKey, `getWsUrl(): Unhandled wsKey`); + } + } + } + + protected getMaxTopicsPerSubscribeEvent(wsKey: WsKey): number | null { + return getMaxTopicsPerSubscribeEvent(wsKey); + } + + /** + * Request connection of all dependent (public & private) websockets, instead of waiting for automatic connection by library + */ + public connectAll(): Promise[] { + return [ + this.connect(WS_KEY_MAP.v2Private), + this.connect(WS_KEY_MAP.v2Public), + ]; + } + + /** + * Subscribe to a PUBLIC topic + * @param instType instrument type (refer to API docs). + * @param topic topic name (e.g. "ticker"). + * @param instId instrument ID (e.g. "BTCUSDT"). Use "default" for private topics. + */ + public subscribeTopic( + instType: BitgetInstTypeV2, + topic: WsTopicV2, + instId: string = 'default', + ) { + return this.subscribe({ + instType, + instId, + channel: topic, + }); + } + + // public subscribeTopicV2( + // instType: BitgetInstTypeV2, + // topic: WsTopicV2, + // instId: string = 'default', + // ) { + // if (isPrivateChannel(topic)) { + // } + // } + + /** + * Unsubscribe from a topic + * @param instType instrument type (refer to API docs). + * @param topic topic name (e.g. "ticker"). + * @param instId instrument ID (e.g. "BTCUSDT"). Use "default" for private topics to get all symbols. + * + * @deprecated, use `subscribe(topics, isPrivate) instead + */ + public unsubscribeTopic( + instType: BitgetInstTypeV2, + topic: WsTopicV2, + instId: string = 'default', + ) { + return this.unsubscribe({ + instType, + instId, + channel: topic, + }); + } +} diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 1b634ee..cf7ab28 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -1,21 +1,19 @@ import { EventEmitter } from 'events'; import WebSocket from 'isomorphic-ws'; -import WsStore, { - BitgetInstType, - WsTopicSubscribeEventArgs, -} from './util/WsStore'; +import WsStore from './util/WsStore'; import { + BitgetInstType, WebsocketClientOptions, WSClientConfigurableOptions, WsKey, WsTopic, + WsTopicSubscribeEventArgs, } from './types'; import { isWsPong, - WsConnectionStateEnum, WS_AUTH_ON_CONNECT_KEYS, WS_KEY_MAP, DefaultLogger, @@ -26,6 +24,7 @@ import { isPrivateChannel, getWsAuthSignature, } from './util'; +import { WsConnectionStateEnum } from './util/WsStore.types'; const LOGGER_CATEGORY = { category: 'bitget-ws' }; @@ -70,10 +69,13 @@ export declare interface WebsocketClient { ): boolean; } +/** + * @deprecated use WebsocketClientV2 instead + */ export class WebsocketClient extends EventEmitter { private logger: typeof DefaultLogger; private options: WebsocketClientOptions; - private wsStore: WsStore; + private wsStore: WsStore; constructor( options: WSClientConfigurableOptions, @@ -143,7 +145,6 @@ export class WebsocketClient extends EventEmitter { } }); } - /** * Unsubscribe from topics & remove them from memory. They won't be re-subscribed to if the connection reconnects. * @param wsTopics topic or list of topics @@ -646,11 +647,9 @@ export class WebsocketClient extends EventEmitter { case WS_KEY_MAP.mixv1: { return WS_BASE_URL_MAP.mixv1.all[networkKey]; } - case WS_KEY_MAP.v2Private: { - return WS_BASE_URL_MAP.v2Private.all[networkKey]; - } + case WS_KEY_MAP.v2Private: case WS_KEY_MAP.v2Public: { - return WS_BASE_URL_MAP.v2Public.all[networkKey]; + throw new Error(`Use the WebsocketClientV2 for V2 websockets`); } default: { this.logger.error('getWsUrl(): Unhandled wsKey: ', { @@ -667,6 +666,8 @@ export class WebsocketClient extends EventEmitter { * @param instType instrument type (refer to API docs). * @param topic topic name (e.g. "ticker"). * @param instId instrument ID (e.g. "BTCUSDT"). Use "default" for private topics. + * + * @deprecated use WebsocketClientV2 instead */ public subscribeTopic( instType: BitgetInstType, @@ -685,6 +686,8 @@ export class WebsocketClient extends EventEmitter { * @param instType instrument type (refer to API docs). * @param topic topic name (e.g. "ticker"). * @param instId instrument ID (e.g. "BTCUSDT"). Use "default" for private topics to get all symbols. + * + * @deprecated use WebsocketClientV2 instead */ public unsubscribeTopic( instType: BitgetInstType, diff --git a/test/ws.util.ts b/test/ws.util.ts index a3a65cd..2e3e449 100644 --- a/test/ws.util.ts +++ b/test/ws.util.ts @@ -1,4 +1,4 @@ -import { WebsocketClient, WsClientEvent } from '../src'; +import { WebsocketClient } from '../src'; export function getSilentLogger(logHint?: string) { return { @@ -20,6 +20,15 @@ export const fullLogger = { error: (...params) => console.error('error', ...params), }; +type WsClientEvent = + | 'open' + | 'update' + | 'close' + | 'exception' + | 'reconnect' + | 'reconnected' + | 'response'; + /** Resolves a promise if an event is seen before a timeout (defaults to 4.5 seconds) */ export function waitForSocketEvent( wsClient: WebsocketClient, From de9fb8f2cdccb117725af18b09b21b0bd15d00ca Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 14 Nov 2023 15:43:03 +0000 Subject: [PATCH 07/10] chore(): move v1 websocket example --- .../deprecated-V1-Websockets/ws-private.ts | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 examples/deprecated-V1-Websockets/ws-private.ts diff --git a/examples/deprecated-V1-Websockets/ws-private.ts b/examples/deprecated-V1-Websockets/ws-private.ts new file mode 100644 index 0000000..e5acc1a --- /dev/null +++ b/examples/deprecated-V1-Websockets/ws-private.ts @@ -0,0 +1,75 @@ +import { WebsocketClient, DefaultLogger } from '../../src'; + +// or +// import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bitget-api'; + +(async () => { + const logger = { + ...DefaultLogger, + silly: (...params) => console.log('silly', ...params), + }; + + logger.info(`Starting private V1 websocket`); + + const API_KEY = process.env.API_KEY_COM; + const API_SECRET = process.env.API_SECRET_COM; + const API_PASS = process.env.API_PASS_COM; + + const wsClient = new WebsocketClient( + { + apiKey: API_KEY, + apiSecret: API_SECRET, + apiPass: API_PASS, + // restOptions: { + // optionally provide rest options, e.g. to pass through a proxy + // }, + }, + logger, + ); + + wsClient.on('update', (data) => { + console.log('WS raw message received ', data); + // console.log('WS raw message received ', JSON.stringify(data, null, 2)); + }); + + wsClient.on('open', (data) => { + console.log('WS connection opened:', data.wsKey); + }); + wsClient.on('response', (data) => { + console.log('WS response: ', JSON.stringify(data, null, 2)); + }); + wsClient.on('reconnect', ({ wsKey }) => { + console.log('WS automatically reconnecting.... ', wsKey); + }); + wsClient.on('reconnected', (data) => { + console.log('WS reconnected ', data?.wsKey); + }); + // auth happens async after the ws connection opens + wsClient.on('authenticated', (data) => { + console.log('WS authenticated', data); + // wsClient.subscribePublicSpotTickers(['BTCUSDT', 'LTCUSDT']); + }); + wsClient.on('exception', (data) => { + console.log('WS error', data); + }); + + /** + * Private account updates + */ + + // spot private + // : account updates + wsClient.subscribeTopic('SPBL', 'account'); + // : order updates + // wsClient.subscribeTopic('SPBL', 'orders'); + + // futures private + // : account updates + // wsClient.subscribeTopic('UMCBL', 'account'); + // // : position updates + // wsClient.subscribeTopic('UMCBL', 'positions'); + // // : order updates + // wsClient.subscribeTopic('UMCBL', 'orders'); + // // : plan order updates + // wsClient.subscribeTopic('UMCBL', 'ordersAlgo'); +})(); From a54058a205e86f5fc05ab24b8d9fa11ab8fdcd3d Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 14 Nov 2023 16:13:29 +0000 Subject: [PATCH 08/10] feat(): finish implementing v2 private websockets --- examples/ws-private.ts | 30 ++++++++------ src/types/websockets/client.ts | 57 +++++++++++++++++++++------ src/util/websocket-util.ts | 6 ++- src/websocket-client-v2.ts | 72 +++++++++++++++++++++++----------- 4 files changed, 117 insertions(+), 48 deletions(-) diff --git a/examples/ws-private.ts b/examples/ws-private.ts index fa74781..f9975cf 100644 --- a/examples/ws-private.ts +++ b/examples/ws-private.ts @@ -1,7 +1,7 @@ -import { WebsocketClient, DefaultLogger } from '../src'; +import { WebsocketClientV2, DefaultLogger } from '../src'; // or -// import { DefaultLogger, WS_KEY_MAP, WebsocketClient } from 'bitget-api'; +// import { DefaultLogger, WS_KEY_MAP, WebsocketClientV2 } from 'bitget-api'; (async () => { const logger = { @@ -13,7 +13,7 @@ import { WebsocketClient, DefaultLogger } from '../src'; const API_SECRET = process.env.API_SECRET_COM; const API_PASS = process.env.API_PASS_COM; - const wsClient = new WebsocketClient( + const wsClient = new WebsocketClientV2( { apiKey: API_KEY, apiSecret: API_SECRET, @@ -57,17 +57,21 @@ import { WebsocketClient, DefaultLogger } from '../src'; // spot private // : account updates - // wsClient.subscribeTopic('SPBL', 'account'); - // : order updates - // wsClient.subscribeTopic('SPBL', 'orders'); + // wsClient.subscribeTopic('SPOT', 'account'); + + // : order updates (note: symbol is required) + // wsClient.subscribeTopic('SPOT', 'orders', 'BTCUSDT'); // futures private // : account updates - // wsClient.subscribeTopic('UMCBL', 'account'); - // // : position updates - // wsClient.subscribeTopic('UMCBL', 'positions'); - // // : order updates - // wsClient.subscribeTopic('UMCBL', 'orders'); - // // : plan order updates - // wsClient.subscribeTopic('UMCBL', 'ordersAlgo'); + // wsClient.subscribeTopic('USDT-FUTURES', 'account'); + + // : position updates + // wsClient.subscribeTopic('USDT-FUTURES', 'positions'); + + // : order updates + // wsClient.subscribeTopic('USDT-FUTURES', 'orders'); + + // : plan order updates + wsClient.subscribeTopic('USDT-FUTURES', 'orders-algo'); })(); diff --git a/src/types/websockets/client.ts b/src/types/websockets/client.ts index 6ea8be0..f1c414a 100644 --- a/src/types/websockets/client.ts +++ b/src/types/websockets/client.ts @@ -66,9 +66,19 @@ export type WSPrivateTopicFuturesV2 = | 'orders-algo' | 'positions-history'; -export type WsPrivateTopicV2 = 'account' | 'orders' | WSPrivateTopicFuturesV2; +export type WSPrivateTopicMarginV2 = + | 'orders-crossed' + | 'account-crossed' + | 'account-isolated' + | 'orders-isolated'; -export type WsTopicV2 = WsPublicTopicV2; +export type WsPrivateTopicV2 = + | 'account' + | 'orders' + | WSPrivateTopicFuturesV2 + | WSPrivateTopicMarginV2; + +export type WsTopicV2 = WsPublicTopicV2 | WsPrivateTopicV2; /** This is used to differentiate between each of the available websocket streams */ export type WsKey = (typeof WS_KEY_MAP)[keyof typeof WS_KEY_MAP]; @@ -77,25 +87,50 @@ export type WsKey = (typeof WS_KEY_MAP)[keyof typeof WS_KEY_MAP]; * Event args for subscribing/unsubscribing */ -// TODO: generalise so this can be made a reusable module for other clients export interface WsTopicSubscribeEventArgs { instType: BitgetInstType; channel: WsTopic; - /** The symbol, e.g. "BTCUSDT" */ - instId: string; + instId?: string; } -export type WsTopicSubscribeCommonArgsV2 = { +export type WsTopicSubscribePublicArgsV2 = { instType: BitgetInstTypeV2; - channel: WsTopicV2; -}; - -export type WsTopicSubscribePublicArgsV2 = WsTopicSubscribeCommonArgsV2 & { channel: WsPublicTopicV2; + /** The symbol, e.g. "BTCUSDT" */ instId: string; }; -export type WsTopicSubscribeEventArgsV2 = WsTopicSubscribePublicArgsV2; +export type WsInstIdChannelsV2 = + | 'orders' + | WSPrivateTopicFuturesV2 + | 'orders-crossed' + | 'orders-isolated'; + +export type WsTopicSubscribePrivateInstIdArgsV2 = { + instType: BitgetInstTypeV2; + channel: WsInstIdChannelsV2; + /** The symbol, e.g. "BTCUSDT" */ + instId?: string; +}; + +export type WsCoinChannelsV2 = + | 'account' + | 'account-crossed' + | 'account-isolated'; + +export type WsTopicSubscribePrivateCoinArgsV2 = { + instType: BitgetInstTypeV2; + channel: WsCoinChannelsV2; + coin: 'default' | string; +}; + +export type WsTopicSubscribePrivateArgsV2 = + | WsTopicSubscribePrivateInstIdArgsV2 + | WsTopicSubscribePrivateCoinArgsV2; + +export type WsTopicSubscribeEventArgsV2 = + | WsTopicSubscribePublicArgsV2 + | WsTopicSubscribePrivateArgsV2; /** General configuration for the WebsocketClient */ export interface WSClientConfigurableOptions { diff --git a/src/util/websocket-util.ts b/src/util/websocket-util.ts index fe069d9..36ffc27 100644 --- a/src/util/websocket-util.ts +++ b/src/util/websocket-util.ts @@ -75,9 +75,13 @@ export const PRIVATE_TOPICS = ['account', 'orders', 'positions', 'ordersAlgo']; export const PRIVATE_TOPICS_V2: WsPrivateTopicV2[] = [ 'account', 'orders', - 'orders-algo', 'positions', + 'orders-algo', 'positions-history', + 'orders-crossed', + 'account-crossed', + 'account-isolated', + 'orders-isolated', ]; export function isPrivateChannel( diff --git a/src/websocket-client-v2.ts b/src/websocket-client-v2.ts index e13e0d6..c124d28 100644 --- a/src/websocket-client-v2.ts +++ b/src/websocket-client-v2.ts @@ -3,8 +3,13 @@ import WebSocket from 'isomorphic-ws'; import { BitgetInstTypeV2, WebsocketClientOptions, + WsCoinChannelsV2, + WsInstIdChannelsV2, WsKey, + WsPublicTopicV2, WsTopicSubscribeEventArgsV2, + WsTopicSubscribePrivateCoinArgsV2, + WsTopicSubscribePrivateInstIdArgsV2, WsTopicV2, } from './types'; @@ -22,6 +27,12 @@ import { BaseWebsocketClient } from './util/BaseWSClient'; const LOGGER_CATEGORY = { category: 'bitget-ws' }; +const COIN_CHANNELS: WsTopicV2[] = [ + 'account', + 'account-crossed', + 'account-isolated', +]; + export class WebsocketClientV2 extends BaseWebsocketClient< WsKey, WsTopicSubscribeEventArgsV2 @@ -92,6 +103,38 @@ export class WebsocketClientV2 extends BaseWebsocketClient< ]; } + /** Some private channels use `coin` instead of `instId`. This method handles building the sub/unsub request */ + private getSubRequest( + instType: BitgetInstTypeV2, + topic: WsTopicV2, + coin: string = 'default', + ): WsTopicSubscribeEventArgsV2 { + if (isPrivateChannel(topic)) { + if (COIN_CHANNELS.includes(topic)) { + const subscribeRequest: WsTopicSubscribePrivateCoinArgsV2 = { + instType, + channel: topic as WsCoinChannelsV2, + coin, + }; + return subscribeRequest; + } + + const subscribeRequest: WsTopicSubscribePrivateInstIdArgsV2 = { + instType, + channel: topic as WsInstIdChannelsV2, + instId: coin, + }; + + return subscribeRequest; + } + + return { + instType, + channel: topic as WsPublicTopicV2, + instId: coin, + }; + } + /** * Subscribe to a PUBLIC topic * @param instType instrument type (refer to API docs). @@ -101,41 +144,24 @@ export class WebsocketClientV2 extends BaseWebsocketClient< public subscribeTopic( instType: BitgetInstTypeV2, topic: WsTopicV2, - instId: string = 'default', + coin: string = 'default', ) { - return this.subscribe({ - instType, - instId, - channel: topic, - }); + const subRequest = this.getSubRequest(instType, topic, coin); + return this.subscribe(subRequest); } - // public subscribeTopicV2( - // instType: BitgetInstTypeV2, - // topic: WsTopicV2, - // instId: string = 'default', - // ) { - // if (isPrivateChannel(topic)) { - // } - // } - /** * Unsubscribe from a topic * @param instType instrument type (refer to API docs). * @param topic topic name (e.g. "ticker"). * @param instId instrument ID (e.g. "BTCUSDT"). Use "default" for private topics to get all symbols. - * - * @deprecated, use `subscribe(topics, isPrivate) instead */ public unsubscribeTopic( instType: BitgetInstTypeV2, topic: WsTopicV2, - instId: string = 'default', + coin: string = 'default', ) { - return this.unsubscribe({ - instType, - instId, - channel: topic, - }); + const subRequest = this.getSubRequest(instType, topic, coin); + return this.unsubscribe(subRequest); } } From a4319a2decb4e45a9b9ce1e922cc1d93ecc025c1 Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 14 Nov 2023 16:25:25 +0000 Subject: [PATCH 09/10] v2.0.0: feat() V2 REST & Websocket support for bitget --- README.md | 74 +++++++++++++++++++++++++++++----------------------- package.json | 4 +-- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 8ba3f1e..a3063d7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [1]: https://www.npmjs.com/package/bitget-api -Node.js connector for the Bitget APIs and WebSockets: +Updated & performant JavaScript & Node.js SDK for the Bitget V2 REST APIs and WebSockets: - Complete integration with all Bitget APIs. - TypeScript support (with type declarations for most API requests & responses). @@ -45,7 +45,7 @@ Check out my related projects: Most methods pass values as-is into HTTP requests. These can be populated using parameters specified by Bitget's API documentation, or check the type definition in each class within this repository (see table below for convenient links to each class). -- [Bitget API Documentation](https://www.bitget.com/docs-v5/en/#rest-api). +- [Bitget API Documentation](https://www.bitget.com/api-doc/common/intro). ## Structure @@ -65,10 +65,12 @@ The version on npm is the output from the `build` command and can be used in pro Each REST API group has a dedicated REST client. To avoid confusion, here are the available REST clients and the corresponding API groups: | Class | Description | |:------------------------------------: |:---------------------------------------------------------------------------------------------: | -| [SpotClient](src/spot-client.ts) | [Spot APIs](https://bitgetlimited.github.io/apidoc/en/spot/#introduction) | -| [FuturesClient](src/futures-client.ts) | [Futures APIs](https://bitgetlimited.github.io/apidoc/en/mix/#introduction) | -| [BrokerClient](src/broker-client.ts) | [Broker APIs](https://bitgetlimited.github.io/apidoc/en/broker/#introduction) | -| [WebsocketClient](src/websocket-client.ts) | Universal client for all Bitget's Websockets | +| [RestClientV2](src/rest-client-v2.ts) | [V2 REST APIs](https://www.bitget.com/api-doc/common/intro) | +| [WebsocketClient](src/websocket-client-v2.ts) | Universal client for all Bitget's V2 Websockets | +| [~~SpotClient~~ (deprecated, use RestClientV2)](src/spot-client.ts) | [~~Spot APIs~~](https://bitgetlimited.github.io/apidoc/en/spot/#introduction) | +| [~~FuturesClient~~ (deprecated, use RestClientV2)](src/futures-client.ts) | [~~Futures APIs~~](https://bitgetlimited.github.io/apidoc/en/mix/#introduction) | +| [~~BrokerClient~~ (deprecated, use RestClientV2)](src/broker-client.ts) | [~~Broker APIs~~](https://bitgetlimited.github.io/apidoc/en/broker/#introduction) | +| [~~WebsocketClient~~ (deprecated, use WebsocketClientV2)](src/websocket-client.ts) | ~~Universal client for all Bitget's V1 Websockets~~ | Examples for using each client can be found in: @@ -81,54 +83,57 @@ If you're missing an example, you're welcome to request one. Priority will be gi First, create API credentials on Bitget's website. -All REST clients have can be used in a similar way. However, method names, parameters and responses may vary depending on the API category you're using! +All REST endpoints should be included in the [RestClientV2](src/rest-client-v2.ts) class. If any endpoints are missing or need improved types, pull requests are very welcome. You can also open an issue on this repo to request an improvement. Priority will be given to [github sponsors](https://github.com/sponsors/tiagosiebler). -Not sure which function to call or which parameters to use? Click the class name in the table above to look at all the function names (they are in the same order as the official API docs), and check the API docs for a list of endpoints/paramters/responses. +Not sure which function to call or which parameters to use? Click the class name in the table above to look at all the function names (they are in the same order as the official API docs), and check the API docs for a list of endpoints/parameters/responses. + +If you found the method you're looking for in the API docs, you can also search for the endpoint in the [RestClientV2](src/rest-client-v2.ts) class. ```javascript -const { - SpotClient, - FuturesClient, - BrokerClient, -} = require('bitget-api'); +const { RestClientV2 } = require('bitget-api'); const API_KEY = 'xxx'; const API_SECRET = 'yyy'; const API_PASS = 'zzz'; -const client = new SpotClient({ +const client = new RestClientV2( + { apiKey: API_KEY, apiSecret: API_SECRET, apiPass: API_PASS, -}, + }, // requestLibraryOptions ); // For public-only API calls, simply don't provide a key & secret or set them to undefined -// const client = new SpotClient(); +// const client = new RestClientV2(); - -client.getApiKeyInfo() - .then(result => { - console.log("getApiKeyInfo result: ", result); +client + .getSpotAccount() + .then((result) => { + console.log('getSpotAccount result: ', result); }) - .catch(err => { - console.error("getApiKeyInfo error: ", err); + .catch((err) => { + console.error('getSpotAccount error: ', err); }); -const symbol = 'BTCUSDT_SPBL'; -client.getCandles(symbol, '1min'); - .then(result => { - console.log("getCandles result: ", result); +client + .getSpotCandles({ + symbol: 'BTCUSDT', + granularity: '1min', + limit: '1000', }) - .catch(err => { - console.error("getCandles error: ", err); + .then((result) => { + console.log('getCandles result: ', result); + }) + .catch((err) => { + console.error('getCandles error: ', err); }); ``` #### WebSockets -For more examples, including how to use websockets with bitget, check the [examples](./examples/) and [test](./test/) folders. +For more examples, including how to use websockets with Bitget, check the [examples](./examples/) and [test](./test/) folders. --- @@ -141,16 +146,19 @@ Pass a custom logger which supports the log methods `silly`, `debug`, `notice`, ```javascript const { WebsocketClient, DefaultLogger } = require('bitget-api'); -// Disable all logging on the silly level -DefaultLogger.silly = () => {}; +// Disable all logging on the silly level (less console logs) +const customLogger = { + ...DefaultLogger, + silly: () => {}, +}; -const ws = new WebsocketClient( +const ws = new WebsocketClientV2( { apiKey: 'API_KEY', apiSecret: 'API_SECRET', apiPass: 'API_PASS', }, - DefaultLogger, + customLogger, ); ``` diff --git a/package.json b/package.json index f5b3cbb..ff258db 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bitget-api", - "version": "1.1.4", - "description": "Node.js connector for Bitget REST APIs and WebSockets, with TypeScript & end-to-end tests.", + "version": "2.0.0", + "description": "Node.js & JavaScript SDK for Bitget REST APIs & WebSockets, with TypeScript & end-to-end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", "files": [ From 1cfdd66d254c228d8d59ff62ff942db68acd5afc Mon Sep 17 00:00:00 2001 From: Tiago Siebler Date: Tue, 14 Nov 2023 16:28:10 +0000 Subject: [PATCH 10/10] feat(): bump core dependencies (axios) --- package-lock.json | 999 ++++++++++++++++++++++++++-------------------- package.json | 2 +- 2 files changed, 573 insertions(+), 428 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41daefd..6bdb8f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "bitget-api", - "version": "1.1.2", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bitget-api", - "version": "1.1.2", + "version": "2.0.0", "license": "MIT", "dependencies": { - "axios": "^0.27.2", + "axios": "^1.6.1", "isomorphic-ws": "^5.0.0", "ws": "^8.9.0" }, @@ -45,17 +45,89 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.19.3", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.3.tgz", @@ -96,13 +168,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.3.tgz", - "integrity": "sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", "dev": true, "dependencies": { - "@babel/types": "^7.19.3", + "@babel/types": "^7.23.3", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -142,34 +215,34 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -228,30 +301,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -281,13 +354,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -366,9 +439,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.3.tgz", - "integrity": "sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -555,33 +628,33 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.3.tgz", - "integrity": "sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.3", - "@babel/types": "^7.19.3", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -599,13 +672,13 @@ } }, "node_modules/@babel/types": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.3.tgz", - "integrity": "sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1112,9 +1185,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", @@ -1122,9 +1195,9 @@ } }, "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", @@ -1142,13 +1215,13 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@nodelib/fs.scandir": { @@ -1278,9 +1351,9 @@ } }, "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, "node_modules/@types/graceful-fs": { @@ -1366,148 +1439,148 @@ "dev": true }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" } }, @@ -1566,9 +1639,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1578,9 +1651,9 @@ } }, "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, "peerDependencies": { "acorn": "^8" @@ -1714,12 +1787,13 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/babel-jest": { @@ -2206,9 +2280,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -2240,9 +2314,9 @@ } }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", "dev": true }, "node_modules/escalade": { @@ -3598,9 +3672,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4330,6 +4404,11 @@ "node": ">= 6" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -4524,9 +4603,9 @@ "dev": true }, "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.8", @@ -4542,18 +4621,18 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dev": true, "dependencies": { "randombytes": "^2.1.0" @@ -4835,13 +4914,13 @@ } }, "node_modules/terser": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", - "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", + "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -4853,16 +4932,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" }, "engines": { "node": ">= 10.13.0" @@ -5025,9 +5104,9 @@ } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5059,9 +5138,9 @@ } }, "node_modules/ts-loader/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5191,22 +5270,22 @@ } }, "node_modules/webpack": { - "version": "5.74.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -5215,9 +5294,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -5412,9 +5491,9 @@ "dev": true }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5543,12 +5622,71 @@ } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/compat-data": { @@ -5581,13 +5719,14 @@ } }, "@babel/generator": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.3.tgz", - "integrity": "sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", "dev": true, "requires": { - "@babel/types": "^7.19.3", + "@babel/types": "^7.23.3", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -5617,28 +5756,28 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { @@ -5682,24 +5821,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -5720,13 +5859,13 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -5789,9 +5928,9 @@ } }, "@babel/parser": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.3.tgz", - "integrity": "sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -5921,30 +6060,30 @@ } }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.3.tgz", - "integrity": "sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.3", - "@babel/types": "^7.19.3", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -5958,13 +6097,13 @@ } }, "@babel/types": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.3.tgz", - "integrity": "sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -6353,9 +6492,9 @@ "dev": true }, "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", @@ -6363,9 +6502,9 @@ }, "dependencies": { "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "requires": { "@jridgewell/set-array": "^1.0.1", @@ -6382,13 +6521,13 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@nodelib/fs.scandir": { @@ -6509,9 +6648,9 @@ } }, "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, "@types/graceful-fs": { @@ -6597,148 +6736,148 @@ "dev": true }, "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dev": true, "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", "dev": true }, "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.11.6", "@xtuc/long": "4.2.2" } }, @@ -6784,15 +6923,15 @@ "dev": true }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true }, "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, "requires": {} }, @@ -6888,12 +7027,13 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", "requires": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "babel-jest": { @@ -7261,9 +7401,9 @@ "dev": true }, "enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -7286,9 +7426,9 @@ } }, "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", "dev": true }, "escalade": { @@ -8290,9 +8430,9 @@ }, "dependencies": { "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -8840,6 +8980,11 @@ "sisteransi": "^1.0.5" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -8965,9 +9110,9 @@ "dev": true }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "requires": { "@types/json-schema": "^7.0.8", @@ -8976,15 +9121,15 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -9189,13 +9334,13 @@ } }, "terser": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", - "integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", + "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", "dev": true, "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -9213,16 +9358,16 @@ } }, "terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.17", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" }, "dependencies": { "jest-worker": { @@ -9308,9 +9453,9 @@ }, "dependencies": { "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -9331,9 +9476,9 @@ }, "dependencies": { "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -9418,22 +9563,22 @@ } }, "webpack": { - "version": "5.74.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -9442,9 +9587,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -9559,9 +9704,9 @@ "dev": true }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index ff258db..ee04dfc 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "author": "Tiago Siebler (https://github.com/tiagosiebler)", "contributors": [], "dependencies": { - "axios": "^0.27.2", + "axios": "^1.6.1", "isomorphic-ws": "^5.0.0", "ws": "^8.9.0" },