From b9930cd9400fe8350f88ce8eadc597dc026689b8 Mon Sep 17 00:00:00 2001 From: Devin Moss Date: Wed, 8 Nov 2023 10:41:32 -0800 Subject: [PATCH] Engines enforcement (#6) * 2.1.2 * Enforce minimum node and npm versions * Specify min tls in market data and account streamers * 3.0.0 * Specify node 20 and 21 in github workflow --- .github/workflows/check-build.yml | 4 +- .npmrc | 1 + lib/account-streamer.ts | 14 +++-- lib/market-data-streamer.ts | 11 ++-- lib/services/tastytrade-http-client.ts | 7 ++- lib/utils/constants.ts | 1 + package-lock.json | 83 ++++++++++++++++++++++---- package.json | 10 +++- 8 files changed, 106 insertions(+), 25 deletions(-) create mode 100644 .npmrc create mode 100644 lib/utils/constants.ts diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 31f53aa..123169a 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -6,7 +6,7 @@ jobs: strategy: matrix: - node-version: [16.x] + node-version: [20.x, 21.x] steps: - uses: actions/checkout@v3 @@ -17,7 +17,7 @@ jobs: cache: 'npm' - run: npm install - run: npm run lint - - run: npm run test + - run: npm run unit-test env: BASE_URL: ${{ secrets.BASE_URL }} API_USERNAME: ${{ secrets.API_USERNAME }} diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/lib/account-streamer.ts b/lib/account-streamer.ts index 0fd077a..d08c44d 100644 --- a/lib/account-streamer.ts +++ b/lib/account-streamer.ts @@ -3,6 +3,7 @@ import _ from 'lodash' import type { JsonMap, JsonValue } from './utils/json-util' import { JsonBuilder } from './utils/json-util' import TastytradeSession from './models/tastytrade-session' +import { MinTlsVersion } from './utils/constants' export enum STREAMER_STATE { Open = 0, @@ -123,7 +124,10 @@ export class AccountStreamer { return this.startPromise } - const websocket = (this.websocket = new WebSocket(this.url)) + this.websocket = new WebSocket(this.url, [], { + minVersion: MinTlsVersion // TLS Config + }) + const websocket = this.websocket this.lastCloseEvent = null this.lastErrorEvent = null websocket.addEventListener('open', this.handleOpen) @@ -300,7 +304,7 @@ export class AccountStreamer { this.queued = [] } - private readonly handleOpen = (event: Event) => { + private readonly handleOpen = (event: WebSocket.Event) => { if (this.startResolve === null) { return } @@ -315,7 +319,7 @@ export class AccountStreamer { this.scheduleHeartbeatTimer() } - private readonly handleClose = (event: CloseEvent) => { + private readonly handleClose = (event: WebSocket.CloseEvent) => { this.logger.info('AccountStreamer closed', event) if (this.websocket === null) { return @@ -326,7 +330,7 @@ export class AccountStreamer { this.teardown() } - private readonly handleError = (event: Event) => { + private readonly handleError = (event: WebSocket.ErrorEvent) => { if (this.websocket === null) { return } @@ -344,7 +348,7 @@ export class AccountStreamer { this.teardown() } - private readonly handleMessage = (event: MessageEvent) => { + private readonly handleMessage = (event: WebSocket.MessageEvent) => { const json = JSON.parse(event.data as string) as JsonMap if (json.results !== undefined) { diff --git a/lib/market-data-streamer.ts b/lib/market-data-streamer.ts index 855953f..66eaf6e 100644 --- a/lib/market-data-streamer.ts +++ b/lib/market-data-streamer.ts @@ -1,6 +1,7 @@ import WebSocket from 'isomorphic-ws' import _ from 'lodash' import { v4 as uuidv4 } from 'uuid' +import { MinTlsVersion } from './utils/constants' export enum MarketDataSubscriptionType { Candle = 'Candle', @@ -89,7 +90,9 @@ export default class MarketDataStreamer { } this.token = token - this.webSocket = new WebSocket(url) + this.webSocket = new WebSocket(url, [], { + minVersion: MinTlsVersion // TLS Config + }) this.webSocket.onopen = this.onOpen.bind(this) this.webSocket.onerror = this.onError.bind(this) this.webSocket.onmessage = this.handleMessageReceived.bind(this) @@ -343,9 +346,9 @@ export default class MarketDataStreamer { this.errorListeners.forEach(listener => listener(error)) } - private handleMessageReceived(data: string) { - const messageData = _.get(data, 'data', data) - const jsonData = JSON.parse(messageData) + private handleMessageReceived(data: WebSocket.MessageEvent) { + const messageData = _.get(data, 'data', '{}') + const jsonData = JSON.parse(messageData as string) switch (jsonData.type) { case 'AUTH_STATE': this.handleAuthStateMessage(jsonData) diff --git a/lib/services/tastytrade-http-client.ts b/lib/services/tastytrade-http-client.ts index e331002..a410fbe 100644 --- a/lib/services/tastytrade-http-client.ts +++ b/lib/services/tastytrade-http-client.ts @@ -3,6 +3,8 @@ import axios from "axios" import qs from 'qs' import { recursiveDasherizeKeys } from "../utils/json-util" import _ from 'lodash' +import https from 'https' +import { MinTlsVersion } from "../utils/constants" const ParamsSerializer = { serialize: function (queryParams: object) { @@ -12,9 +14,11 @@ const ParamsSerializer = { export default class TastytradeHttpClient{ public readonly session: TastytradeSession + private readonly httpsAgent: https.Agent constructor(private readonly baseUrl: string) { this.session = new TastytradeSession() + this.httpsAgent = new https.Agent({ minVersion: MinTlsVersion }) } private getDefaultHeaders(): any { @@ -37,7 +41,8 @@ export default class TastytradeHttpClient{ data: dasherizedData, headers: mergedHeaders, params: dasherizedParams, - paramsSerializer: ParamsSerializer + paramsSerializer: ParamsSerializer, + httpsAgent: this.httpsAgent }, _.isEmpty) return axios.request(config) diff --git a/lib/utils/constants.ts b/lib/utils/constants.ts new file mode 100644 index 0000000..e368e1f --- /dev/null +++ b/lib/utils/constants.ts @@ -0,0 +1 @@ +export const MinTlsVersion = 'TLSv1.2' \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7076aed..94764a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tastytrade/api", - "version": "2.1.1", + "version": "3.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@tastytrade/api", - "version": "2.1.1", + "version": "3.0.0", "license": "MIT", "dependencies": { "@types/lodash": "^4.14.182", @@ -19,9 +19,11 @@ "ws": "^8.13.0" }, "devDependencies": { + "@types/axios": "^0.14.0", "@types/jest": "^29.5.0", - "@types/node": "17.0.27", + "@types/node": "20.9.0", "@types/uuid": "^9.0.2", + "@types/ws": "^8.5.9", "@typescript-eslint/eslint-plugin": "^5.57.1", "@typescript-eslint/parser": "^5.57.1", "dotenv": "^16.0.3", @@ -29,6 +31,10 @@ "jest": "^29.5.0", "ts-jest": "^29.0.5", "typescript": "4.6.3" + }, + "engines": { + "node": ">=20.0.0", + "npm": ">=9.0.0" } }, "node_modules/@ampproject/remapping": { @@ -1216,6 +1222,16 @@ "@sinonjs/commons": "^2.0.0" } }, + "node_modules/@types/axios": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", + "integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==", + "deprecated": "This is a stub types definition for axios (https://github.com/mzabriskie/axios). axios provides its own type definitions, so you don't need @types/axios installed!", + "dev": true, + "dependencies": { + "axios": "*" + } + }, "node_modules/@types/babel__core": { "version": "7.20.0", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", @@ -1312,10 +1328,13 @@ "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==" }, "node_modules/@types/node": { - "version": "17.0.27", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.27.tgz", - "integrity": "sha512-4/Ke7bbWOasuT3kceBZFGakP1dYN2XFd8v2l9bqF2LNWrmeU07JLpp56aEeG6+Q3olqO5TvXpW0yaiYnZJ5CXg==", - "dev": true + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/prettier": { "version": "2.7.2", @@ -1346,6 +1365,15 @@ "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", "dev": true }, + "node_modules/@types/ws": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", + "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.24", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", @@ -4695,6 +4723,12 @@ "node": ">=4.2.0" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/update-browserslist-db": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", @@ -5827,6 +5861,15 @@ "@sinonjs/commons": "^2.0.0" } }, + "@types/axios": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", + "integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==", + "dev": true, + "requires": { + "axios": "*" + } + }, "@types/babel__core": { "version": "7.20.0", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", @@ -5923,10 +5966,13 @@ "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==" }, "@types/node": { - "version": "17.0.27", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.27.tgz", - "integrity": "sha512-4/Ke7bbWOasuT3kceBZFGakP1dYN2XFd8v2l9bqF2LNWrmeU07JLpp56aEeG6+Q3olqO5TvXpW0yaiYnZJ5CXg==", - "dev": true + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } }, "@types/prettier": { "version": "2.7.2", @@ -5957,6 +6003,15 @@ "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", "dev": true }, + "@types/ws": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", + "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { "version": "17.0.24", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", @@ -8363,6 +8418,12 @@ "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", "dev": true }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "update-browserslist-db": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", diff --git a/package.json b/package.json index 17acd0f..f656a90 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,15 @@ { "name": "@tastytrade/api", - "version": "2.1.1", + "version": "3.0.0", "main": "dist/tastytrade-api.js", "typings": "dist/tastytrade-api.d.ts", "repository": "https://github.com/tastytrade/tastytrade-api-js", "license": "MIT", "description": "Typescript impelementation of tastytrade api", + "engines": { + "npm": ">=9.0.0", + "node": ">=20.0.0" + }, "scripts": { "build": "tsc -p tsconfig.json", "test": "jest -i --restoreMocks", @@ -26,9 +30,11 @@ "ws": "^8.13.0" }, "devDependencies": { + "@types/axios": "^0.14.0", "@types/jest": "^29.5.0", - "@types/node": "17.0.27", + "@types/node": "20.9.0", "@types/uuid": "^9.0.2", + "@types/ws": "^8.5.9", "@typescript-eslint/eslint-plugin": "^5.57.1", "@typescript-eslint/parser": "^5.57.1", "dotenv": "^16.0.3",