@icgio/icg-exchanges-wrapper 1.14.269 → 1.15.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.
package/README.md CHANGED
@@ -66,16 +66,6 @@ await orders.cancel_order(exchange, pair, order_id)
66
66
  const status = await orders.get_order_status(exchange, pair, order_id)
67
67
  ```
68
68
 
69
- ### WebSocket Data Client
70
-
71
- ```javascript
72
- const { ws_data_client, send_log } = require('@icgio/icg-exchanges-wrapper')
73
-
74
- // Send log messages via WebSocket
75
- send_log('info', 'Operation completed successfully')
76
- send_log('error', 'An error occurred')
77
- ```
78
-
79
69
  ## Project Structure
80
70
 
81
71
  ```
@@ -91,8 +81,6 @@ icg-exchanges-wrapper/
91
81
  ├── middleware/
92
82
  │ ├── account.js # Account middleware
93
83
  │ └── orders.js # Order management middleware
94
- ├── lib/
95
- │ └── ws-data-client.js # WebSocket data client
96
84
  └── package.json
97
85
  ```
98
86
 
package/index.js CHANGED
@@ -4,7 +4,6 @@ const get_exchanges_trades = require('./functions/get-exchanges-trades.js')
4
4
  const get_exchanges_open_orders = require('./functions/get-exchanges-open-orders.js')
5
5
  const get_exchanges_deposits = require('./functions/get-exchanges-deposits.js')
6
6
  const get_exchanges_withdrawals = require('./functions/get-exchanges-withdrawals.js')
7
- const { ws_data_client } = require('./lib/ws-data-client.js')
8
7
 
9
8
  module.exports.functions = {
10
9
  get_exchanges_rates,
@@ -18,7 +17,3 @@ module.exports.functions = {
18
17
  }
19
18
 
20
19
  module.exports.orders = require('./middleware/orders.js')
21
-
22
- // WebSocket data client for log streaming
23
- module.exports.ws_data_client = ws_data_client
24
- module.exports.send_log = (level, message) => ws_data_client.send_log(level, message)
@@ -7,10 +7,9 @@ const utils = require('@icgio/icg-utils').utils
7
7
  const { crypto_rates: get_crypto_rates, observable: Observable } = require('@icgio/icg-utils')
8
8
 
9
9
  const Account = require('./account.js')
10
- const { ws_data_client: default_ws_data_client } = require('../lib/ws-data-client.js')
11
10
 
12
- // Module-level ws_data_client reference (can be overridden)
13
- let ws_data_client = default_ws_data_client
11
+ // Module-level ws_client reference (must be provided via settings.ws_client)
12
+ let ws_client = null
14
13
 
15
14
  // Default precision functions - will be overridden if provided in settings
16
15
  let round_price = (exchange, pair, price) => price
@@ -246,14 +245,11 @@ module.exports = class Orders {
246
245
  this.process_query_order(this.query_order)
247
246
  }, PROCESS_OPEN_ORDERS_TRADERS_INTERVAL_MS)
248
247
  }
249
- // Initialize WS data client for streaming to dashboard backend
250
- // If external ws_client is provided, use it instead of the default
248
+ // Initialize WS client for streaming to dashboard backend
249
+ // ws_client must be provided via settings (no built-in fallback)
251
250
  if (settings.ws_client) {
252
- ws_data_client = settings.ws_client
253
- ws_data_client.set_order_info_ref(this.order_info)
254
- } else if (settings.ws_backend_secret && settings.client_id) {
255
- ws_data_client.init(settings.ws_backend_secret, settings.client_id)
256
- ws_data_client.set_order_info_ref(this.order_info)
251
+ ws_client = settings.ws_client
252
+ ws_client.set_order_info_ref(this.order_info)
257
253
  }
258
254
  }
259
255
  init_account(balances, benchmarks) {
@@ -811,8 +807,8 @@ module.exports = class Orders {
811
807
  // Send order update via WebSocket to dashboard backend
812
808
  const order_data = _.get(this.order_info, [exchange, pair, type, order_id], {})
813
809
  // Only send N-FILLED and later statuses (skip INIT which has temporary order_id)
814
- if (dict.status !== 'INIT' && order_data.status !== 'INIT') {
815
- ws_data_client.send_order({
810
+ if (dict.status !== 'INIT' && order_data.status !== 'INIT' && ws_client) {
811
+ ws_client.send_order({
816
812
  action: 'update',
817
813
  exchange,
818
814
  pair,
@@ -829,8 +825,8 @@ module.exports = class Orders {
829
825
  console.log('DELETE %s: %s %s %s %s', note, exchange, pair, type, order_id)
830
826
 
831
827
  // Send delete event via WebSocket (only for non-INIT orders)
832
- if (order_data.status !== 'INIT') {
833
- ws_data_client.send_order({
828
+ if (order_data.status !== 'INIT' && ws_client) {
829
+ ws_client.send_order({
834
830
  action: 'delete',
835
831
  exchange,
836
832
  pair,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icgio/icg-exchanges-wrapper",
3
- "version": "1.14.269",
3
+ "version": "1.15.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,248 +0,0 @@
1
- const WebSocket = require('ws')
2
- const { randomUUID: uuid } = require('crypto')
3
-
4
- // Hardcoded backend URL for production
5
- const WS_BACKEND_URL = 'wss://api-prod.icg.io/ws/data/ingest'
6
-
7
- /**
8
- * WebSocket client for streaming logs and order status to dashboard backend.
9
- * Singleton instance shared across the application.
10
- */
11
- class WSDataClient {
12
- constructor() {
13
- this.backend_url = WS_BACKEND_URL
14
- this.secret = null
15
- this.client_id = null
16
- this.ws = null
17
- this.queue = []
18
- this.max_queue = 100
19
- this.reconnect_interval = 5000
20
- this.enabled = false
21
- this.connected = false
22
- this.reconnect_timer = null
23
- this.sync_interval = null
24
- this.sync_interval_ms = 30 * 1000 // 30 seconds
25
- this.order_info_ref = null // Reference to Orders.order_info for periodic sync
26
- }
27
-
28
- /**
29
- * Initialize the WS client with connection details.
30
- * @param {string} secret - Shared secret for authentication
31
- * @param {string} client_id - Client identifier (e.g., "FREEZONE")
32
- */
33
- init(secret, client_id) {
34
- if (!secret || !client_id) {
35
- console.log('[WSDataClient] Missing config (secret or client_id), disabled')
36
- return
37
- }
38
-
39
- this.secret = secret
40
- this.client_id = client_id
41
- this.enabled = true
42
-
43
- console.log('[WSDataClient] Initializing for client:', client_id, 'URL:', this.backend_url)
44
- this.connect()
45
- }
46
-
47
- /**
48
- * Set reference to order_info for periodic sync.
49
- * @param {object} order_info - Reference to Orders.order_info object
50
- */
51
- set_order_info_ref(order_info) {
52
- this.order_info_ref = order_info
53
-
54
- // Start periodic sync if not already started
55
- if (!this.sync_interval && this.enabled) {
56
- this.start_periodic_sync()
57
- }
58
- }
59
-
60
- /**
61
- * Start periodic full sync of order_info (every 30 seconds).
62
- */
63
- start_periodic_sync() {
64
- if (this.sync_interval) return
65
-
66
- // Send initial reset on startup
67
- this.send_reset('orders')
68
-
69
- this.sync_interval = setInterval(() => {
70
- if (this.order_info_ref && this.connected) {
71
- this.send_order_sync(this.order_info_ref)
72
- }
73
- }, this.sync_interval_ms)
74
-
75
- console.log('[WSDataClient] Periodic sync started (every %ds)', this.sync_interval_ms / 1000)
76
- }
77
-
78
- /**
79
- * Connect to the dashboard backend WebSocket.
80
- */
81
- connect() {
82
- if (!this.enabled) return
83
-
84
- try {
85
- this.ws = new WebSocket(this.backend_url, {
86
- headers: {
87
- 'x-client-secret': this.secret,
88
- 'x-client-id': this.client_id,
89
- },
90
- })
91
-
92
- this.ws.on('open', () => {
93
- console.log('[WSDataClient] Connected to backend')
94
- this.connected = true
95
- this.flush_queue()
96
-
97
- // Send reset on reconnect to clear stale state
98
- this.send_reset('orders')
99
-
100
- // Send full sync immediately after connect
101
- if (this.order_info_ref) {
102
- this.send_order_sync(this.order_info_ref)
103
- }
104
- })
105
-
106
- this.ws.on('close', (code, reason) => {
107
- console.log('[WSDataClient] Disconnected:', code, reason?.toString())
108
- this.connected = false
109
- this.schedule_reconnect()
110
- })
111
-
112
- this.ws.on('error', (err) => {
113
- console.error('[WSDataClient] Error:', err.message)
114
- this.connected = false
115
- })
116
-
117
- this.ws.on('message', (data) => {
118
- // Handle any messages from backend (e.g., acknowledgments)
119
- try {
120
- const msg = JSON.parse(data.toString())
121
- if (msg.type === 'error') {
122
- console.error('[WSDataClient] Backend error:', msg.message)
123
- }
124
- } catch (e) {
125
- // Ignore parse errors
126
- }
127
- })
128
- } catch (err) {
129
- console.error('[WSDataClient] Connection error:', err.message)
130
- this.schedule_reconnect()
131
- }
132
- }
133
-
134
- /**
135
- * Schedule reconnection attempt.
136
- */
137
- schedule_reconnect() {
138
- if (this.reconnect_timer) return
139
-
140
- this.reconnect_timer = setTimeout(() => {
141
- this.reconnect_timer = null
142
- console.log('[WSDataClient] Attempting reconnect...')
143
- this.connect()
144
- }, this.reconnect_interval)
145
- }
146
-
147
- /**
148
- * Send a message to the backend.
149
- * @param {string} type - Message type
150
- * @param {object} data - Message data
151
- */
152
- send(type, data) {
153
- if (!this.enabled) return
154
-
155
- const msg = JSON.stringify({
156
- type,
157
- client: this.client_id,
158
- ts: Date.now(),
159
- ...data,
160
- })
161
-
162
- if (this.connected && this.ws?.readyState === WebSocket.OPEN) {
163
- this.ws.send(msg)
164
- } else {
165
- // Queue message for later
166
- this.queue.push(msg)
167
- if (this.queue.length > this.max_queue) {
168
- this.queue.shift() // Drop oldest
169
- }
170
- }
171
- }
172
-
173
- /**
174
- * Flush queued messages after reconnection.
175
- */
176
- flush_queue() {
177
- while (this.queue.length > 0 && this.connected && this.ws?.readyState === WebSocket.OPEN) {
178
- const msg = this.queue.shift()
179
- this.ws.send(msg)
180
- }
181
- }
182
-
183
- /**
184
- * Send reset event to clear state on backend.
185
- * @param {string} scope - What to reset ('orders', 'logs', 'all')
186
- */
187
- send_reset(scope = 'orders') {
188
- this.send('reset', { scope })
189
- }
190
-
191
- /**
192
- * Send full order_info sync to backend.
193
- * @param {object} order_info - Complete order_info object
194
- */
195
- send_order_sync(order_info) {
196
- this.send('order_sync', { data: order_info })
197
- }
198
-
199
- /**
200
- * Send order update event to backend.
201
- * @param {object} event - Order event data
202
- */
203
- send_order(event) {
204
- this.send('order', event)
205
- }
206
-
207
- /**
208
- * Send log entry to backend.
209
- * @param {string} level - Log level ('info', 'warn', 'error')
210
- * @param {string} message - Log message
211
- */
212
- send_log(level, message) {
213
- this.send('log', {
214
- id: uuid(),
215
- level,
216
- msg: message,
217
- })
218
- }
219
-
220
- /**
221
- * Close the WebSocket connection.
222
- */
223
- close() {
224
- this.enabled = false
225
-
226
- if (this.sync_interval) {
227
- clearInterval(this.sync_interval)
228
- this.sync_interval = null
229
- }
230
-
231
- if (this.reconnect_timer) {
232
- clearTimeout(this.reconnect_timer)
233
- this.reconnect_timer = null
234
- }
235
-
236
- if (this.ws) {
237
- this.ws.close()
238
- this.ws = null
239
- }
240
-
241
- this.connected = false
242
- }
243
- }
244
-
245
- // Singleton instance
246
- const ws_data_client = new WSDataClient()
247
-
248
- module.exports = { WSDataClient, ws_data_client }