Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.0.10: fix() missing js extensions for some imports, add linter checks via eslint plugin, update ws client deferred types #21

Merged
merged 2 commits into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ module.exports = {
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin', 'simple-import-sort'],
plugins: [
'@typescript-eslint/eslint-plugin',
'simple-import-sort',
'require-extensions',
],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
'plugin:require-extensions/recommended',
],
root: true,
env: {
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/e2etest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ jobs:
- name: Install
run: npm ci --ignore-scripts

- name: Build
- name: Check Lint
run: npx eslint src

- name: Check Build
run: npm run build

- name: Test
Expand Down
6 changes: 3 additions & 3 deletions examples/spot/submitMarketOrder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RestClient } from '../../src'; // For an easy demonstration, import from the src dir. Normally though, see below to import from the npm installed version instead.
import { RestClient } from '../../src/index.js'; // For an easy demonstration, import from the src dir. Normally though, see below to import from the npm installed version instead.
// import { RestClient } from 'gateio-api'; // Import the RestClient from the published version of this SDK, installed via NPM (npm install gateio-api)

// Define the account object with API key and secret
Expand All @@ -9,8 +9,8 @@ const account = {

// Initialize the RestClient with the API credentials
const gateRestClient = new RestClient({
apiKey: account.key,
apiSecret: account.secret,
apiKey: 'yourkeyhere',
apiSecret: 'yoursecrethere',
});

async function submitSpotOrder() {
Expand Down
17 changes: 15 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gateio-api",
"version": "1.0.9",
"version": "1.0.10",
"description": "Complete & robust Node.js SDK for Gate.io's REST APIs, WebSockets & WebSocket APIs, with TypeScript declarations.",
"scripts": {
"clean": "rm -rf dist/*",
Expand Down Expand Up @@ -39,6 +39,7 @@
"eslint": "^8.29.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-require-extensions": "^0.1.3",
"eslint-plugin-simple-import-sort": "^12.0.0",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
Expand Down
18 changes: 10 additions & 8 deletions src/WebsocketClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
WsMarket,
WsTopicRequest,
} from './lib/websocket/websocket-util.js';
import { DeferredPromise } from './lib/websocket/WsStore.types.js';
import { WSConnectedResult } from './lib/websocket/WsStore.types.js';
import {
WSAPIRequest,
WsOperation,
Expand All @@ -43,7 +43,7 @@ export class WebsocketClient extends BaseWebsocketClient<WsKey> {
*
* Returns array of promises that individually resolve when each connection is successfully opened.
*/
public connectAll(): (DeferredPromise['promise'] | undefined)[] {
public connectAll(): Promise<WSConnectedResult | undefined>[] {
return [
this.connect(WS_KEY_MAP.spotV4),
this.connect(WS_KEY_MAP.perpFuturesUSDTV4),
Expand Down Expand Up @@ -254,15 +254,11 @@ export class WebsocketClient extends BaseWebsocketClient<WsKey> {
wsKey,
});
if (!wsKey) {
throw new Error(
'Cannot send PONG due to no known websocket for this wsKey',
);
throw new Error('Cannot send PONG, no wsKey provided');
}
const wsState = this.getWsStore().get(wsKey);
if (!wsState || !wsState?.ws) {
throw new Error(
`${wsKey} socket not connected yet, call "connectAll()" first then try again when the "open" event arrives`,
);
throw new Error(`Cannot send pong, ${wsKey} socket not connected yet`);
}

// Send a protocol layer pong
Expand Down Expand Up @@ -422,6 +418,12 @@ export class WebsocketClient extends BaseWebsocketClient<WsKey> {
`!! Unhandled string event type "${eventAction}. Defaulting to "update" channel...`,
parsed,
);
} else {
// TODO: test meee
this.logger.error(
`!! Unhandled non-string event type "${eventAction}. Defaulting to "update" channel...`,
parsed,
);
}

results.push({
Expand Down
22 changes: 7 additions & 15 deletions src/lib/BaseWSClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from './websocket/websocket-util.js';
import { WsStore } from './websocket/WsStore.js';
import {
DeferredPromise,
WSConnectedResult,
WsConnectionStateEnum,
} from './websocket/WsStore.types.js';

Expand Down Expand Up @@ -169,7 +169,7 @@ export abstract class BaseWebsocketClient<
/**
* Request connection of all dependent (public & private) websockets, instead of waiting for automatic connection by library
*/
protected abstract connectAll(): (DeferredPromise['promise'] | undefined)[];
protected abstract connectAll(): Promise<WSConnectedResult | undefined>[];

protected isPrivateWsKey(wsKey: TWSKey): boolean {
return this.getPrivateWSKeys().includes(wsKey);
Expand Down Expand Up @@ -221,10 +221,8 @@ export abstract class BaseWebsocketClient<
WsConnectionStateEnum.CONNECTED,
);

// TODO: test this, spam loads of subscribe topic calls in intervals (and add a log line on this logic)
const isConnectionInProgress =
this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTING) ||
this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.RECONNECTING);
this.wsStore.isConnectionAttemptInProgress(wsKey);

// start connection process if it hasn't yet begun. Topics are automatically subscribed to on-connect
if (!isConnected && !isConnectionInProgress) {
Expand Down Expand Up @@ -362,19 +360,17 @@ export abstract class BaseWebsocketClient<
*/
protected async connect(
wsKey: TWSKey,
): Promise<DeferredPromise['promise'] | undefined> {
): Promise<WSConnectedResult | undefined> {
try {
if (this.wsStore.isWsOpen(wsKey)) {
this.logger.error(
'Refused to connect to ws with existing active connection',
{ ...WS_LOGGER_CATEGORY, wsKey },
);
return this.wsStore.getConnectionInProgressPromise(wsKey);
return { wsKey };
}

if (
this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTING)
) {
if (this.wsStore.isConnectionAttemptInProgress(wsKey)) {
this.logger.error(
'Refused to connect to ws, connection attempt already active',
{ ...WS_LOGGER_CATEGORY, wsKey },
Expand Down Expand Up @@ -459,7 +455,6 @@ export abstract class BaseWebsocketClient<
...WS_LOGGER_CATEGORY,
wsKey,
});
// TODO:!

const request = await this.getWsAuthRequestEvent(wsKey);

Expand All @@ -473,10 +468,7 @@ export abstract class BaseWebsocketClient<

private reconnectWithDelay(wsKey: TWSKey, connectionDelayMs: number) {
this.clearTimers(wsKey);
if (
this.wsStore.getConnectionState(wsKey) !==
WsConnectionStateEnum.CONNECTING
) {
if (!this.wsStore.isConnectionAttemptInProgress(wsKey)) {
this.setWsState(wsKey, WsConnectionStateEnum.RECONNECTING);
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/requestUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import WebSocket from 'isomorphic-ws';

import { GateBaseUrlKey } from '../types/shared';
import { GateBaseUrlKey } from '../types/shared.js';

export interface RestClientOptions {
/** Your API key */
Expand Down
18 changes: 16 additions & 2 deletions src/lib/websocket/WsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import WebSocket from 'isomorphic-ws';
import { DefaultLogger } from '../logger.js';
import {
DeferredPromise,
WSConnectedResult,
WsConnectionStateEnum,
WsStoredState,
} from './WsStore.types.js';
Expand Down Expand Up @@ -254,7 +255,7 @@ export class WsStore<
/** Get promise designed to track a connection attempt in progress. Resolves once connected. */
getConnectionInProgressPromise(
wsKey: WsKey,
): DeferredPromise<unknown> | undefined {
): DeferredPromise<WSConnectedResult> | undefined {
return this.getDeferredPromise(
wsKey,
DEFERRED_PROMISE_REF.CONNECTION_IN_PROGRESS,
Expand All @@ -269,7 +270,7 @@ export class WsStore<
createConnectionInProgressPromise(
wsKey: WsKey,
throwIfExists: boolean,
): DeferredPromise<unknown> {
): DeferredPromise<WSConnectedResult> {
return this.createDeferredPromise(
wsKey,
DEFERRED_PROMISE_REF.CONNECTION_IN_PROGRESS,
Expand Down Expand Up @@ -307,6 +308,19 @@ export class WsStore<
return this.getConnectionState(key) === state;
}

/**
* Check if we're currently in the process of opening a connection for any reason. Safer than only checking "CONNECTING" as the state
* @param key
* @returns
*/
isConnectionAttemptInProgress(key: WsKey): boolean {
const isConnectionInProgress =
this.isConnectionState(key, WsConnectionStateEnum.CONNECTING) ||
this.isConnectionState(key, WsConnectionStateEnum.RECONNECTING);

return isConnectionInProgress;
}

/* subscribed topics */

getTopics(key: WsKey): Set<TWSTopicSubscribeEventArgs> {
Expand Down
4 changes: 4 additions & 0 deletions src/lib/websocket/WsStore.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export interface DeferredPromise<TSuccess = any, TError = any> {
promise?: Promise<TSuccess>;
}

export interface WSConnectedResult {
wsKey: string;
}

export interface WsStoredState<TWSTopicSubscribeEvent extends string | object> {
/** The currently active websocket connection */
ws?: WebSocket;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/websocket/websocket-util.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { WSAPIRequest } from '../../types/websockets/requests';
import { WSAPIRequest } from '../../types/websockets/requests.js';
import {
FuturesWSAPITopic,
SpotWSAPITopic,
} from '../../types/websockets/wsAPI';
} from '../../types/websockets/wsAPI.js';

/**
* Should be one WS key per unique URL. Some URLs may need a suffix.
Expand Down
3 changes: 0 additions & 3 deletions src/types/request/rebate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,3 @@ export interface PartnerTransactionReq {
limit?: number;
offset?: number;
}



4 changes: 2 additions & 2 deletions src/types/websockets/requests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CHANNEL_ID } from '../../lib/requestUtils';
import { WSAPITopic } from './wsAPI';
import { CHANNEL_ID } from '../../lib/requestUtils.js';
import { WSAPITopic } from './wsAPI.js';

export type WsOperation = 'subscribe' | 'unsubscribe' | 'auth';

Expand Down
6 changes: 3 additions & 3 deletions src/types/websockets/wsAPI.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { WsKey } from '../../lib/websocket/websocket-util';
import { WsKey } from '../../lib/websocket/websocket-util.js';
import {
GetFuturesOrdersReq,
SubmitFuturesOrderReq,
UpdateFuturesOrderReq,
} from '../request/futures';
} from '../request/futures.js';
import {
CancelSpotBatchOrdersReq,
DeleteSpotOrderReq,
GetSpotOrderReq,
SubmitSpotOrderReq,
UpdateSpotOrderReq,
} from '../request/spot';
} from '../request/spot.js';

export type SpotWSAPITopic =
| 'spot.login'
Expand Down