@tastytrade/api 5.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +87 -43
  3. package/dist/account-streamer.d.ts +6 -3
  4. package/dist/account-streamer.d.ts.map +1 -1
  5. package/dist/account-streamer.js +21 -15
  6. package/dist/account-streamer.js.map +1 -1
  7. package/dist/logger.d.ts +5 -1
  8. package/dist/logger.d.ts.map +1 -1
  9. package/dist/logger.js +4 -0
  10. package/dist/logger.js.map +1 -1
  11. package/dist/market-data-streamer.d.ts.map +1 -1
  12. package/dist/market-data-streamer.js +1 -0
  13. package/dist/market-data-streamer.js.map +1 -1
  14. package/dist/models/access-token.d.ts +12 -0
  15. package/dist/models/access-token.d.ts.map +1 -0
  16. package/dist/models/access-token.js +35 -0
  17. package/dist/models/access-token.js.map +1 -0
  18. package/dist/models/tastytrade-session.d.ts.map +1 -1
  19. package/dist/models/tastytrade-session.js.map +1 -1
  20. package/dist/quote-streamer.d.ts +58 -0
  21. package/dist/quote-streamer.d.ts.map +1 -0
  22. package/dist/quote-streamer.js +132 -0
  23. package/dist/quote-streamer.js.map +1 -0
  24. package/dist/services/account-status-service.js +0 -1
  25. package/dist/services/account-status-service.js.map +1 -1
  26. package/dist/services/accounts-and-customers-service.js +0 -1
  27. package/dist/services/accounts-and-customers-service.js.map +1 -1
  28. package/dist/services/balances-and-positions-service.js +0 -1
  29. package/dist/services/balances-and-positions-service.js.map +1 -1
  30. package/dist/services/instruments-service.js +1 -2
  31. package/dist/services/instruments-service.js.map +1 -1
  32. package/dist/services/margin-requirements-service.js +0 -1
  33. package/dist/services/margin-requirements-service.js.map +1 -1
  34. package/dist/services/market-metrics-service.js +0 -1
  35. package/dist/services/market-metrics-service.js.map +1 -1
  36. package/dist/services/net-liquidating-value-history-service.js +0 -1
  37. package/dist/services/net-liquidating-value-history-service.js.map +1 -1
  38. package/dist/services/orders-service.js +0 -1
  39. package/dist/services/orders-service.js.map +1 -1
  40. package/dist/services/risk-parameters-service.js +0 -1
  41. package/dist/services/risk-parameters-service.js.map +1 -1
  42. package/dist/services/session-service.d.ts +6 -1
  43. package/dist/services/session-service.d.ts.map +1 -1
  44. package/dist/services/session-service.js +10 -2
  45. package/dist/services/session-service.js.map +1 -1
  46. package/dist/services/symbol-search-service.js +0 -1
  47. package/dist/services/symbol-search-service.js.map +1 -1
  48. package/dist/services/tastytrade-http-client.d.ts +16 -2
  49. package/dist/services/tastytrade-http-client.d.ts.map +1 -1
  50. package/dist/services/tastytrade-http-client.js +71 -14
  51. package/dist/services/tastytrade-http-client.js.map +1 -1
  52. package/dist/services/transactions-service.js +0 -1
  53. package/dist/services/transactions-service.js.map +1 -1
  54. package/dist/services/watchlists-service.js +0 -1
  55. package/dist/services/watchlists-service.js.map +1 -1
  56. package/dist/tastytrade-api.d.ts +12 -2
  57. package/dist/tastytrade-api.d.ts.map +1 -1
  58. package/dist/tastytrade-api.js +22 -6
  59. package/dist/tastytrade-api.js.map +1 -1
  60. package/dist/utils/json-util.d.ts.map +1 -1
  61. package/dist/utils/json-util.js +0 -2
  62. package/dist/utils/json-util.js.map +1 -1
  63. package/dist/utils/response-util.d.ts.map +1 -1
  64. package/dist/utils/response-util.js +0 -1
  65. package/dist/utils/response-util.js.map +1 -1
  66. package/eslint.config.cjs +46 -0
  67. package/lib/account-streamer.ts +31 -23
  68. package/lib/logger.ts +6 -1
  69. package/lib/models/access-token.ts +40 -0
  70. package/lib/models/tastytrade-session.ts +1 -1
  71. package/lib/quote-streamer.ts +151 -0
  72. package/lib/services/instruments-service.ts +1 -1
  73. package/lib/services/session-service.ts +13 -1
  74. package/lib/services/tastytrade-http-client.ts +93 -15
  75. package/lib/tastytrade-api.ts +31 -4
  76. package/lib/utils/json-util.ts +0 -2
  77. package/lib/utils/response-util.ts +3 -4
  78. package/package.json +26 -18
  79. package/tsconfig.json +2 -2
  80. package/.eslintrc.cjs +0 -18
  81. package/lib/market-data-streamer.ts +0 -376
@@ -0,0 +1,151 @@
1
+ import { DXLinkWebSocketClient, DXLinkFeed, FeedContract, FeedDataFormat, type DXLinkFeedEventListener } from '@dxfeed/dxlink-api'
2
+ import type AccountsAndCustomersService from './services/accounts-and-customers-service.js'
3
+ import type Logger from './logger.js'
4
+ import _ from 'lodash'
5
+
6
+ // TODO: Make sure this works in node and we don't have to override the global Websocket class
7
+
8
+ export enum MarketDataSubscriptionType {
9
+ Candle = 'Candle',
10
+ Quote = 'Quote',
11
+ Trade = 'Trade',
12
+ Summary = 'Summary',
13
+ Profile = 'Profile',
14
+ Greeks = 'Greeks',
15
+ Underlying = 'Underlying'
16
+ }
17
+
18
+ const ALL_EVENT_TYPES = [
19
+ MarketDataSubscriptionType.Quote,
20
+ MarketDataSubscriptionType.Trade,
21
+ MarketDataSubscriptionType.Summary,
22
+ MarketDataSubscriptionType.Profile,
23
+ MarketDataSubscriptionType.Greeks,
24
+ MarketDataSubscriptionType.Underlying
25
+ ]
26
+
27
+ export enum CandleType {
28
+ Tick = 't',
29
+ Second = 's',
30
+ Minute = 'm',
31
+ Hour = 'h',
32
+ Day = 'd',
33
+ Week = 'w',
34
+ Month = 'mo',
35
+ ThirdFriday = 'o',
36
+ Year = 'y',
37
+ Volume = 'v',
38
+ Price = 'p'
39
+ }
40
+
41
+ export default class QuoteStreamer {
42
+ public dxLinkFeed: DXLinkFeed<any> | null = null
43
+ public dxLinkUrl: string | null = null
44
+ public dxLinkAuthToken: string | null = null
45
+ public eventListeners: DXLinkFeedEventListener[] = []
46
+
47
+ constructor(private readonly accountsAndCustomersService: AccountsAndCustomersService, private readonly logger: Logger) {
48
+ }
49
+
50
+ /**
51
+ * Connects to the DxLink WebSocket and sets up the feed
52
+ * Make sure to call disconnect() when done
53
+ * Calls `getApiQuoteToken` to get the connection URL and auth token
54
+ * Make sure you have a valid session or access token before calling this
55
+ */
56
+ async connect() {
57
+ const tokenResponse = await this.accountsAndCustomersService.getApiQuoteToken()
58
+ this.dxLinkUrl = _.get(tokenResponse, 'dxlink-url')
59
+ this.dxLinkAuthToken = _.get(tokenResponse, 'token')
60
+
61
+ const client = new DXLinkWebSocketClient()
62
+ client.connect(this.dxLinkUrl!)
63
+ client.setAuthToken(this.dxLinkAuthToken!)
64
+
65
+ this.dxLinkFeed = new DXLinkFeed(client, FeedContract.AUTO)
66
+
67
+ this.dxLinkFeed.configure({
68
+ acceptAggregationPeriod: 10,
69
+ acceptDataFormat: FeedDataFormat.COMPACT
70
+ })
71
+
72
+ this.eventListeners.forEach(listener => this.dxLinkFeed!.addEventListener(listener))
73
+ }
74
+
75
+ disconnect() {
76
+ if (_.isNil(this.dxLinkFeed)) {
77
+ return
78
+ }
79
+
80
+ this.eventListeners.forEach(listener => this.removeEventListener(listener))
81
+ this.dxLinkFeed = null
82
+ }
83
+
84
+ // Returns a function that can be called to remove the listener
85
+ addEventListener(listener: DXLinkFeedEventListener): () => void {
86
+ this.eventListeners.push(listener)
87
+ if (this.dxLinkFeed) {
88
+ this.dxLinkFeed.addEventListener(listener)
89
+ }
90
+
91
+ return () => {
92
+ this.removeEventListener(listener)
93
+ }
94
+ }
95
+
96
+ removeEventListener(listenerToRemove: DXLinkFeedEventListener) {
97
+ _.remove(this.eventListeners, listener => listener === listenerToRemove)
98
+ if (this.dxLinkFeed) {
99
+ this.dxLinkFeed.removeEventListener(listenerToRemove)
100
+ }
101
+ }
102
+
103
+ subscribe(streamerSymbols: string[], types: MarketDataSubscriptionType[] | null = null) {
104
+ if (_.isNil(this.dxLinkFeed)) {
105
+ throw new Error('DxLink feed is not connected')
106
+ }
107
+
108
+ types = types ?? ALL_EVENT_TYPES
109
+ streamerSymbols.forEach(symbol => {
110
+ types.forEach(type => {
111
+ this.dxLinkFeed!.addSubscriptions({ type, symbol })
112
+ })
113
+ })
114
+ }
115
+
116
+ unsubscribe(streamerSymbols: string[]) {
117
+ if (_.isNil(this.dxLinkFeed)) {
118
+ throw new Error('DxLink feed is not connected')
119
+ }
120
+
121
+ streamerSymbols.forEach(symbol => {
122
+ ALL_EVENT_TYPES.forEach(type => {
123
+ this.dxLinkFeed!.removeSubscriptions({ type, symbol })
124
+ })
125
+ })
126
+ }
127
+
128
+ /**
129
+ * Adds a candle subscription
130
+ * @param streamerSymbol Get this from an instrument's streamer-symbol json response field
131
+ * @param fromTime Epoch timestamp from where you want to start
132
+ * @param period The duration of each candle
133
+ * @param type The duration type of the period
134
+ * For example, a period/type of 5/m means you want each candle to represent 5 minutes of data
135
+ * From there, setting fromTime to 24 hours ago would give you 24 hours of data grouped in 5 minute intervals
136
+ * @returns
137
+ */
138
+ subscribeCandles(streamerSymbol: string, fromTime: number, period: number, type: CandleType) {
139
+ // Example: AAPL{=5m} where each candle represents 5 minutes of data
140
+ const candleSymbol = `${streamerSymbol}{=${period}${type}}`
141
+ if (_.isNil(this.dxLinkFeed)) {
142
+ throw new Error('DxLink feed is not connected')
143
+ }
144
+
145
+ this.dxLinkFeed!.addSubscriptions({
146
+ type: MarketDataSubscriptionType.Candle,
147
+ symbol: candleSymbol,
148
+ fromTime
149
+ })
150
+ }
151
+ }
@@ -118,7 +118,7 @@ export default class InstrumentsService {
118
118
  return extractResponseData(futureOptionChain)
119
119
  }
120
120
 
121
- //Option-chains: Allows an API client to fetch futures option chains.
121
+ //Option-chains: Allows an API client to fetch equity option chains.
122
122
  async getNestedOptionChain(symbol: string){
123
123
  //Returns an option chain given an underlying symbol,
124
124
  //i.e. AAPL in a nested form to minimize redundant processing
@@ -5,6 +5,18 @@ export default class SessionService {
5
5
  constructor(public httpClient: TastytradeHttpClient) {
6
6
  }
7
7
 
8
+ private get clientId(): string {
9
+ return '9953f07a-5de4-408c-a8ab-688a6320f00f'
10
+ }
11
+
12
+ private get clientSecret(): string {
13
+ return 'baa245033420a05d013541c0c6ef4f98bb16a1ec'
14
+ }
15
+
16
+ private get refreshToken(): string {
17
+ return 'eyJhbGciOiJFZERTQSIsInR5cCI6InJ0K2p3dCIsImtpZCI6IkZqVTdUT25qVEQ2WnVySlg2cVlwWmVPbzBDQzQ5TnIzR1pUN1E4MTc0cUkiLCJqa3UiOiJodHRwczovL2ludGVyaW9yLWFwaS5hcjIudGFzdHl0cmFkZS5zeXN0ZW1zL29hdXRoL2p3a3MifQ.eyJpc3MiOiJodHRwczovL2FwaS50YXN0eXRyYWRlLmNvbSIsInN1YiI6IlUwMDAwMDM3MTg0IiwiaWF0IjoxNzU4MjQzNjY0LCJhdWQiOiI5OTUzZjA3YS01ZGU0LTQwOGMtYThhYi02ODhhNjMyMGYwMGYiLCJncmFudF9pZCI6Ikc0ZTc4MjFkYy03NTQyLTQ0NTQtODBkMy1iYjU3NGEwMGRkYWMiLCJzY29wZSI6InJlYWQgdHJhZGUifQ.ZsP51rUGQIXsP-cU0OCa-45AwwMp18YxOrT_mrrocClhRL7bfWctX8GOJ35Nn_E48WOuQPxUF3KMhSn1tkqLBQ'
18
+ }
19
+
8
20
  // Sessions: Allows an API client to interact with their session, or create a new one.
9
21
  async login(usernameOrEmail: string, password: string, rememberMe = false) {
10
22
  // Create a new user session.
@@ -30,6 +42,6 @@ export default class SessionService {
30
42
  async logout(){
31
43
  const response = await this.httpClient.deleteData('/sessions', {});// added this for the integration tests?
32
44
  this.httpClient.session.clear()
33
- return extractResponseData(response);
45
+ return { status: response.status };
34
46
  }
35
47
  }
@@ -1,9 +1,11 @@
1
1
  import TastytradeSession from "../models/tastytrade-session.js"
2
+ import AccessToken from "../models/access-token.js"
2
3
  import axios from "axios"
3
4
  import qs from 'qs'
4
5
  import { recursiveDasherizeKeys } from "../utils/json-util.js"
5
6
  import _ from 'lodash'
6
7
  import type Logger from "../logger.js"
8
+ import type { ClientConfig } from "../tastytrade-api.js"
7
9
 
8
10
  const ParamsSerializer = {
9
11
  serialize: function (queryParams: object) {
@@ -11,22 +13,65 @@ const ParamsSerializer = {
11
13
  }
12
14
  }
13
15
 
16
+ const ApiVersionRegex = /^\d{8}$/
17
+
14
18
  export default class TastytradeHttpClient {
15
19
  private readonly logger?: Logger
20
+ public baseUrl: string
21
+ public clientSecret?: string
22
+ public refreshToken?: string
23
+ public oauthScopes?: string[]
24
+ public readonly accessToken: AccessToken
16
25
  public readonly session: TastytradeSession
26
+ private _targetApiVersion?: string
17
27
 
18
- constructor(private readonly baseUrl: string, logger?: Logger) {
28
+ constructor(clientConfig: Partial<ClientConfig>, logger?: Logger) {
19
29
  this.logger = logger
30
+ this.baseUrl = clientConfig.baseUrl!
31
+ this.accessToken = new AccessToken()
20
32
  this.session = new TastytradeSession()
33
+ this.updateConfig(clientConfig)
34
+ }
35
+
36
+ public updateConfig(config: Partial<ClientConfig>) {
37
+ const httpClientConfig = _.pick(config, ['clientSecret', 'refreshToken', 'oauthScopes', 'targetApiVersion'])
38
+ if (!_.isEmpty(httpClientConfig)) {
39
+ Object.assign(this, httpClientConfig)
40
+ this.accessToken.clear()
41
+ }
42
+ }
43
+
44
+ get needsTokenRefresh(): boolean {
45
+ if (this.session.isValid) {
46
+ return false
47
+ }
48
+ if (_.isNil(this.refreshToken) || _.isNil(this.clientSecret)) {
49
+ return false
50
+ }
51
+ return this.accessToken.isExpired
52
+ }
53
+
54
+ get authHeader(): string | null {
55
+ if (this.session.isValid) {
56
+ return this.session.authToken
57
+ }
58
+ if (this.accessToken.isValid) {
59
+ return this.accessToken.authorizationHeader
60
+ }
61
+ return null
21
62
  }
22
63
 
23
64
  private getDefaultHeaders(): any {
24
65
  const headers: { [key: string]: any } = {
25
66
  "Content-Type": "application/json",
26
67
  "Accept": "application/json",
27
- "Authorization": this.session.authToken
68
+ "Authorization": this.authHeader
28
69
  };
29
70
 
71
+ if (!_.isNil(this.targetApiVersion)) {
72
+ headers["Accept-Version"] = this.targetApiVersion
73
+ }
74
+
30
75
  // Only set user agent if running in node
31
76
  if (typeof window === 'undefined') {
32
77
  headers["User-Agent"] = 'tastytrade-sdk-js'
@@ -35,21 +80,43 @@ export default class TastytradeHttpClient {
35
80
  return headers
36
81
  }
37
82
 
38
- private async executeRequest(method: string, url: string, data: object = {}, headers: object = {}, params: object = {}) {
39
- const dasherizedParams = recursiveDasherizeKeys(params)
40
- const dasherizedData = recursiveDasherizeKeys(data)
41
- const mergedHeaders = { ...headers, ...this.getDefaultHeaders() }
83
+ private axiosConfig(method: string, url: string, data: object = {}, headers: object = {}, params: object = {}): any {
84
+ return _.omitBy(
85
+ { method, url, baseURL: this.baseUrl, data, headers, params, paramsSerializer: ParamsSerializer},
86
+ _.isEmpty
87
+ )
88
+ }
42
89
 
43
- const config = _.omitBy({
44
- method,
45
- url,
46
- baseURL: this.baseUrl,
47
- data: dasherizedData,
48
- headers: mergedHeaders,
49
- params: dasherizedParams,
50
- paramsSerializer: ParamsSerializer
51
- }, _.isEmpty)
90
+ public async generateAccessToken(): Promise<any> {
91
+ if (_.isNil(this.refreshToken) || _.isNil(this.clientSecret) || _.isNil(this.oauthScopes)) {
92
+ throw new Error('Missing required parameters to generate access token (refreshToken, clientSecret, oauthScopes)')
93
+ }
94
+ const params = {
95
+ refresh_token: this.refreshToken,
96
+ client_secret: this.clientSecret,
97
+ scope: this.oauthScopes!.join(' '),
98
+ grant_type: 'refresh_token'
99
+ }
52
100
 
101
+ const config = this.axiosConfig('post', '/oauth/token', params)
102
+ this.logger?.info('Making request', config)
103
+ const tokenResponse = await axios.request(config)
104
+ this.accessToken.updateFromTokenResponse(tokenResponse)
105
+ return this.accessToken
106
+ }
107
+
108
+ private async executeRequest(method: string, url: string, data: object = {}, headers: object = {}, params: object = {}): Promise<any> {
109
+ if (this.needsTokenRefresh) {
110
+ await this.generateAccessToken()
111
+ }
112
+ let dasherizedParams = params
113
+ let dasherizedData = data
114
+ dasherizedParams = recursiveDasherizeKeys(params)
115
+ dasherizedData = recursiveDasherizeKeys(data)
116
+
117
+ const mergedHeaders = { ...headers, ...this.getDefaultHeaders() }
118
+
119
+ const config = this.axiosConfig(method, url, dasherizedData, mergedHeaders, dasherizedParams)
53
120
  this.logger?.info('Making request', config)
54
121
  return axios.request(config)
55
122
  }
@@ -73,4 +140,15 @@ export default class TastytradeHttpClient {
73
140
  async deleteData(url: string, headers: object): Promise<any> {
74
141
  return this.executeRequest('delete', url, headers);
75
142
  }
143
+
144
+ public get targetApiVersion(): string | undefined {
145
+ return this._targetApiVersion
146
+ }
147
+
148
+ public set targetApiVersion(version: string | undefined) {
149
+ if (!_.isNil(version) && !ApiVersionRegex.test(version)) {
150
+ throw new Error('Invalid API version format. Expected YYYYMMDD.')
151
+ }
152
+ this._targetApiVersion = version
153
+ }
76
154
  }
@@ -1,6 +1,6 @@
1
1
  import TastytradeHttpClient from "./services/tastytrade-http-client.js"
2
2
  import { AccountStreamer, STREAMER_STATE, type Disposer, type StreamerStateObserver } from './account-streamer.js'
3
- import MarketDataStreamer, { type CandleSubscriptionOptions, CandleType, MarketDataSubscriptionType, type MarketDataListener } from "./market-data-streamer.js"
3
+ import _ from 'lodash'
4
4
 
5
5
  //Services:
6
6
  import SessionService from "./services/session-service.js"
@@ -19,19 +19,34 @@ import WatchlistsService from "./services/watchlists-service.js"
19
19
  import TastytradeSession from "./models/tastytrade-session.js"
20
20
  import type Logger from "./logger.js"
21
21
  import { TastytradeLogger, LogLevel } from "./logger.js"
22
+ import QuoteStreamer, { MarketDataSubscriptionType, CandleType } from "./quote-streamer.js"
23
+ import type AccessToken from "./models/access-token.js"
22
24
 
23
25
  export type ClientConfig = {
24
26
  baseUrl: string,
25
27
  accountStreamerUrl: string,
28
+ clientSecret?: string,
29
+ refreshToken?: string,
30
+ oauthScopes?: string[],
26
31
  logger?: Logger,
27
32
  logLevel?: LogLevel
33
+ targetApiVersion?: string
28
34
  }
29
35
 
30
36
  export default class TastytradeClient {
37
+ public static readonly ProdConfig: ClientConfig = {
38
+ baseUrl: 'https://api.tastyworks.com',
39
+ accountStreamerUrl: 'wss://streamer.tastyworks.com',
40
+ }
41
+ public static readonly SandboxConfig: ClientConfig = {
42
+ baseUrl: 'https://api.cert.tastyworks.com',
43
+ accountStreamerUrl: 'wss://streamer.cert.tastyworks.com',
44
+ }
31
45
  public readonly logger: TastytradeLogger
32
46
  public readonly httpClient: TastytradeHttpClient
33
47
 
34
48
  public readonly accountStreamer: AccountStreamer
49
+ public readonly quoteStreamer: QuoteStreamer
35
50
 
36
51
  public readonly sessionService: SessionService
37
52
  public readonly accountStatusService: AccountStatusService
@@ -49,8 +64,7 @@ export default class TastytradeClient {
49
64
 
50
65
  constructor(config: ClientConfig) {
51
66
  this.logger = new TastytradeLogger(config.logger, config.logLevel)
52
- this.httpClient = new TastytradeHttpClient(config.baseUrl, this.logger)
53
- this.accountStreamer = new AccountStreamer(config.accountStreamerUrl, this.session, this.logger)
67
+ this.httpClient = new TastytradeHttpClient(config, this.logger)
54
68
 
55
69
  this.sessionService = new SessionService(this.httpClient)
56
70
  this.accountStatusService = new AccountStatusService(this.httpClient)
@@ -65,14 +79,27 @@ export default class TastytradeClient {
65
79
  this.symbolSearchService = new SymbolSearchService(this.httpClient)
66
80
  this.transactionsService = new TransactionsService(this.httpClient)
67
81
  this.watchlistsService = new WatchlistsService(this.httpClient)
82
+
83
+
84
+ this.accountStreamer = new AccountStreamer(config.accountStreamerUrl, this.session, this.accessToken, this.logger)
85
+ this.quoteStreamer = new QuoteStreamer(this.accountsAndCustomersService, this.logger)
86
+ }
87
+
88
+ public updateConfig(config: Partial<ClientConfig>) {
89
+ this.httpClient.updateConfig(config)
90
+ this.logger.updateConfig(config)
68
91
  }
69
92
 
70
93
  get session(): TastytradeSession {
71
94
  return this.httpClient.session
72
95
  }
96
+
97
+ get accessToken(): AccessToken {
98
+ return this.httpClient.accessToken
99
+ }
73
100
  }
74
101
 
75
- export { MarketDataStreamer, MarketDataSubscriptionType, type MarketDataListener, type CandleSubscriptionOptions, CandleType }
102
+ export { MarketDataSubscriptionType, CandleType }
76
103
  export { AccountStreamer, STREAMER_STATE, type Disposer, type StreamerStateObserver }
77
104
  export { TastytradeLogger, LogLevel }
78
105
  export type { Logger }
@@ -20,11 +20,9 @@ export class JsonBuilder {
20
20
  }
21
21
  }
22
22
 
23
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
23
  export function recursiveDasherizeKeys(body: any) {
25
24
  let dasherized = _.mapKeys(body, (_value, key) => dasherize(key))
26
25
 
27
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
26
  dasherized = _.mapValues(dasherized, (value: any) => {
29
27
  if (_.isPlainObject(value)) {
30
28
  return recursiveDasherizeKeys(value)
@@ -1,12 +1,11 @@
1
1
  import _ from 'lodash'
2
2
 
3
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
- export default function extractResponseData(httpResponse: any){
3
+ export default function extractResponseData(httpResponse: any) {
5
4
  if (_.has(httpResponse, 'data.data.items')) {
6
5
  return _.get(httpResponse, 'data.data.items')
7
- } else if (_.has(httpResponse, 'data.data')){
6
+ } else if (_.has(httpResponse, 'data.data')) {
8
7
  return _.get(httpResponse, 'data.data')
9
- }else{
8
+ } else {
10
9
  return httpResponse
11
10
  }
12
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tastytrade/api",
3
- "version": "5.0.0",
3
+ "version": "6.0.0",
4
4
  "type": "module",
5
5
  "module": "dist/tastytrade-api.js",
6
6
  "types": "dist/tastytrade-api.d.ts",
@@ -15,37 +15,45 @@
15
15
  "npm": ">=9.0.0",
16
16
  "node": ">=20.0.0"
17
17
  },
18
+ "prettier": {
19
+ "semi": false,
20
+ "singleQuote": true,
21
+ "trailingComma": "none"
22
+ },
18
23
  "scripts": {
19
24
  "build": "tsc -p tsconfig.json",
20
25
  "test": "jest -i --restoreMocks",
21
26
  "unit-test": "node --experimental-vm-modules ./node_modules/.bin/jest tests/unit",
22
27
  "integration-test": "node --experimental-vm-modules ./node_modules/.bin/jest tests/integration",
23
28
  "lint": "eslint lib/** tests/**",
29
+ "format": "prettier --write .",
24
30
  "prepublishOnly": "npm run unit-test && npm run build",
25
31
  "postpack": "git tag -a $npm_package_version -m $npm_package_version && git push origin $npm_package_version"
26
32
  },
27
33
  "dependencies": {
28
- "@types/lodash": "^4.14.182",
29
- "@types/qs": "^6.9.7",
30
- "axios": "^1.3.4",
34
+ "@dxfeed/dxlink-api": "^0.3.0",
35
+ "@types/lodash": "^4.17.16",
36
+ "@types/qs": "^6.9.18",
37
+ "axios": "^1.9.0",
31
38
  "isomorphic-ws": "^5.0.0",
32
39
  "lodash": "^4.17.21",
33
- "qs": "^6.11.1",
34
- "uuid": "^9.0.0",
35
- "ws": "^8.13.0"
40
+ "qs": "^6.14.0",
41
+ "uuid": "^11.1.0",
42
+ "ws": "^8.18.2"
36
43
  },
37
44
  "devDependencies": {
38
- "@types/jest": "^29.5.0",
39
- "@types/node": "20.9.0",
40
- "@types/uuid": "^9.0.2",
41
- "@types/ws": "^8.5.9",
42
- "@typescript-eslint/eslint-plugin": "^5.57.1",
43
- "@typescript-eslint/parser": "^5.57.1",
44
- "dotenv": "^16.0.3",
45
- "eslint": "^8.14.0",
45
+ "@types/jest": "^29.5.14",
46
+ "@types/node": "22.15.14",
47
+ "@types/uuid": "^10.0.0",
48
+ "@types/ws": "^8.18.1",
49
+ "@typescript-eslint/eslint-plugin": "^8.32.0",
50
+ "@typescript-eslint/parser": "^8.32.0",
51
+ "dotenv": "^16.5.0",
52
+ "eslint": "^9.26.0",
46
53
  "jest": "^29.7.0",
47
- "nock": "^13.5.4",
48
- "ts-jest": "^29.1.2",
49
- "typescript": "^5.4.2"
54
+ "nock": "^14.0.4",
55
+ "prettier": "^3.5.3",
56
+ "ts-jest": "^29.3.2",
57
+ "typescript": "^5.8.3"
50
58
  }
51
59
  }
package/tsconfig.json CHANGED
@@ -48,7 +48,7 @@
48
48
  // "typeRoots": [], /* List of folders to include type definitions from. */
49
49
  // "types": [], /* Type declaration files to be included in compilation. */
50
50
  // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
51
- // "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
51
+ "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
52
52
  // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
53
53
  // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
54
54
 
@@ -65,7 +65,7 @@
65
65
  /* Advanced Options */
66
66
  "skipLibCheck": true, /* Skip type checking of declaration files. */
67
67
  "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
68
- "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
68
+ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
69
69
  },
70
70
  "include": ["lib/**/*"],
71
71
  "exclude": ["node_modules", "dist", "tests/**/*", "examples", ".env", ".env.sample"]
package/.eslintrc.cjs DELETED
@@ -1,18 +0,0 @@
1
- module.exports = {
2
- extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
3
- parser: '@typescript-eslint/parser',
4
- plugins: ['@typescript-eslint'],
5
- root: true,
6
- rules: {
7
- "@typescript-eslint/no-inferrable-types": "warn",
8
- "@typescript-eslint/no-unused-vars": [
9
- "error",
10
- {
11
- "argsIgnorePattern": "^_",
12
- "varsIgnorePattern": "^_",
13
- "caughtErrorsIgnorePattern": "^_"
14
- }
15
- ],
16
- "@typescript-eslint/no-non-null-assertion": "off"
17
- }
18
- };