@stryke-xyz/premarket-sdk 1.2.1 → 1.2.2
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/dist/cjs/config/index.js +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/sync/clients/activity-client.js +325 -454
- package/dist/cjs/sync/clients/order-client.js +609 -429
- package/dist/esm/config/index.js +3 -3
- package/dist/esm/package.json +1 -1
- package/dist/esm/sync/clients/activity-client.js +325 -454
- package/dist/esm/sync/clients/order-client.js +609 -429
- package/dist/types/sync/clients/activity-client.d.ts +39 -32
- package/dist/types/sync/clients/order-client.d.ts +79 -58
- package/dist/types/sync/index.d.ts +2 -2
- package/package.json +58 -58
- package/dist/cjs/api/README.md +0 -296
- package/dist/cjs/config/README.md +0 -112
- package/dist/cjs/exchange/README.md +0 -280
- package/dist/cjs/registry/README.md +0 -150
- package/dist/cjs/shared/README.md +0 -235
- package/dist/cjs/shared/utils.js +0 -1
- package/dist/cjs/sync/README.md +0 -215
- package/dist/cjs/sync/clients/base-client.js +0 -518
- package/dist/cjs/sync/redis-ws-client.js +0 -235
- package/dist/cjs/utils/README.md +0 -89
- package/dist/cjs/vault/README.md +0 -268
- package/dist/esm/api/README.md +0 -296
- package/dist/esm/config/README.md +0 -112
- package/dist/esm/exchange/README.md +0 -280
- package/dist/esm/registry/README.md +0 -150
- package/dist/esm/shared/README.md +0 -235
- package/dist/esm/shared/utils.js +0 -0
- package/dist/esm/sync/README.md +0 -215
- package/dist/esm/sync/clients/base-client.js +0 -508
- package/dist/esm/sync/redis-ws-client.js +0 -225
- package/dist/esm/utils/README.md +0 -89
- package/dist/esm/vault/README.md +0 -268
- package/dist/types/shared/utils.d.ts +0 -0
- package/dist/types/sync/clients/base-client.d.ts +0 -56
- package/dist/types/sync/redis-ws-client.d.ts +0 -21
|
@@ -134,6 +134,10 @@ function _sliced_to_array(arr, i) {
|
|
|
134
134
|
function _to_consumable_array(arr) {
|
|
135
135
|
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
136
136
|
}
|
|
137
|
+
function _type_of(obj) {
|
|
138
|
+
"@swc/helpers - typeof";
|
|
139
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
140
|
+
}
|
|
137
141
|
function _unsupported_iterable_to_array(o, minLen) {
|
|
138
142
|
if (!o) return;
|
|
139
143
|
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
@@ -248,21 +252,40 @@ function scheduleDeferred(callback) {
|
|
|
248
252
|
}
|
|
249
253
|
setTimeout(callback, 0);
|
|
250
254
|
}
|
|
251
|
-
|
|
252
|
-
* Normalize a price string to a canonical form for consistent map key lookups.
|
|
253
|
-
* Converts "1.000000" and "1" both to "1" (removes trailing zeros after decimal).
|
|
254
|
-
*/ function normalizePrice(price) {
|
|
255
|
+
function normalizePrice(price) {
|
|
255
256
|
var num = parseFloat(price);
|
|
256
257
|
if (isNaN(num)) return price;
|
|
257
258
|
return num.toString();
|
|
258
259
|
}
|
|
259
|
-
/** Current depth state for a token within a market subscription. */ /** One bid or ask level change in the normalized SDK format. */ /** Raw level shape inside the wire `depth_update.levels[]` array. */ /** Raw `depth_update` frame as published by the depth projector. */ /** Consolidated depth update emitted to SDK listeners after normalization. */ /** Last applied executor sequenceId for this token (== endSeq of the frame). */ /** Range covered by this frame; clients can detect gaps via startSeq != prevSeq + 1. */ /**
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
/** A market + its token IDs to subscribe to. */ /** Current depth state for a token within a market subscription. */ /** One bid or ask level change in the normalized SDK format. */ /** Raw level shape inside the wire `depth_update.levels[]` array. */ /** Raw `depth_update` frame as published by the depth projector. */ /** Consolidated depth update emitted to SDK listeners after normalization. */ /** Last applied executor sequenceId for this token (== endSeq of the frame). */ /** Range covered by this frame; clients can detect gaps via startSeq != prevSeq + 1. */ /**
|
|
261
|
+
* Configuration for the market depth websocket client.
|
|
262
|
+
*
|
|
263
|
+
* Supports multiple markets over a single WebSocket connection.
|
|
264
|
+
* Markets can be added/removed dynamically via subscribeMarket / unsubscribeMarket
|
|
265
|
+
* without closing the connection.
|
|
266
|
+
*/ /** Initial market subscriptions. More can be added later via subscribeMarket(). */ /** Heartbeat interval in ms (default: 30000) */ /** Heartbeat timeout - if no pong received within this time, reconnect (default: 10000) */ /** Max reconnection attempts before giving up (default: Infinity) */ /** Initial reconnect delay in ms (default: 1000) */ /** Max reconnect delay in ms (default: 30000) */ /** @deprecated Use MarketDepthClientConfig with markets[] instead of marketId+tokenIds. */ // Internal state per token
|
|
267
|
+
// price → depth
|
|
268
|
+
// price → depth
|
|
263
269
|
/**
|
|
264
|
-
* Client for syncing orderbook depth data for
|
|
265
|
-
*
|
|
270
|
+
* Client for syncing orderbook depth data for one or more markets over a
|
|
271
|
+
* single persistent WebSocket connection.
|
|
272
|
+
*
|
|
273
|
+
* Usage:
|
|
274
|
+
* const client = new MarketDepthSyncClient({ wsUrl: "wss://..." });
|
|
275
|
+
* await client.connect();
|
|
276
|
+
*
|
|
277
|
+
* // Subscribe to markets dynamically (e.g. when they scroll into view)
|
|
278
|
+
* client.subscribeMarket("5", ["0xabc..."]);
|
|
279
|
+
*
|
|
280
|
+
* // Unsubscribe without closing the connection (e.g. scrolled out of view)
|
|
281
|
+
* client.unsubscribeMarket("5");
|
|
282
|
+
*
|
|
283
|
+
* // Listen for updates
|
|
284
|
+
* const offDelta = client.onDelta((marketId, update) => { ... });
|
|
285
|
+
* const offSnap = client.onSnapshot((marketId, snapshots) => { ... });
|
|
286
|
+
*
|
|
287
|
+
* // Full disconnect (app teardown)
|
|
288
|
+
* await client.disconnect();
|
|
266
289
|
*/ var MarketDepthSyncClient = /*#__PURE__*/ function() {
|
|
267
290
|
"use strict";
|
|
268
291
|
function MarketDepthSyncClient(config) {
|
|
@@ -270,11 +293,18 @@ function scheduleDeferred(callback) {
|
|
|
270
293
|
_define_property(this, "ws", null);
|
|
271
294
|
_define_property(this, "config", void 0);
|
|
272
295
|
_define_property(this, "status", "disconnected");
|
|
273
|
-
// Per-token depth state (
|
|
296
|
+
// Per-token depth state (tokenIds are globally unique across all subscribed markets)
|
|
274
297
|
_define_property(this, "tokenStates", new Map());
|
|
275
|
-
//
|
|
276
|
-
_define_property(this, "
|
|
277
|
-
|
|
298
|
+
// Active subscriptions: marketId → tokenIds[]
|
|
299
|
+
_define_property(this, "activeSubscriptions", new Map());
|
|
300
|
+
// Reverse map: tokenId → marketId (for gap resync targeting)
|
|
301
|
+
_define_property(this, "tokenToMarket", new Map());
|
|
302
|
+
// Markets that have received their initial snapshot on this connection
|
|
303
|
+
_define_property(this, "syncedMarkets", new Set());
|
|
304
|
+
// Per-market frame queues: buffer depth_updates received before snapshot
|
|
305
|
+
_define_property(this, "frameQueues", new Map());
|
|
306
|
+
// Per-market processing mutex
|
|
307
|
+
_define_property(this, "processingMarkets", new Set());
|
|
278
308
|
// Listeners
|
|
279
309
|
_define_property(this, "statusListeners", new Set());
|
|
280
310
|
_define_property(this, "snapshotListeners", new Set());
|
|
@@ -283,121 +313,186 @@ function scheduleDeferred(callback) {
|
|
|
283
313
|
_define_property(this, "shouldBeConnected", false);
|
|
284
314
|
_define_property(this, "reconnectAttempts", 0);
|
|
285
315
|
_define_property(this, "reconnectTimeoutId", null);
|
|
286
|
-
// Heartbeat
|
|
316
|
+
// Heartbeat
|
|
287
317
|
_define_property(this, "heartbeatIntervalId", null);
|
|
288
318
|
_define_property(this, "heartbeatTimeoutId", null);
|
|
289
319
|
_define_property(this, "lastPongTime", 0);
|
|
290
|
-
// Visibility
|
|
320
|
+
// Visibility handler
|
|
291
321
|
_define_property(this, "visibilityChangeHandler", null);
|
|
322
|
+
// Support legacy single-market config (marketId + tokenIds)
|
|
323
|
+
var normalizedConfig = _object_spread({}, config);
|
|
324
|
+
if (config.marketId && config.tokenIds) {
|
|
325
|
+
var _config_markets;
|
|
326
|
+
normalizedConfig.markets = [
|
|
327
|
+
{
|
|
328
|
+
marketId: config.marketId,
|
|
329
|
+
tokenIds: config.tokenIds
|
|
330
|
+
}
|
|
331
|
+
].concat(_to_consumable_array((_config_markets = config.markets) !== null && _config_markets !== void 0 ? _config_markets : []));
|
|
332
|
+
}
|
|
292
333
|
this.config = _object_spread({
|
|
334
|
+
markets: [],
|
|
293
335
|
heartbeatIntervalMs: 30000,
|
|
294
336
|
heartbeatTimeoutMs: 10000,
|
|
295
337
|
maxReconnectAttempts: Infinity,
|
|
296
338
|
initialReconnectDelayMs: 1000,
|
|
297
339
|
maxReconnectDelayMs: 30000
|
|
298
|
-
},
|
|
340
|
+
}, normalizedConfig);
|
|
341
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
342
|
+
try {
|
|
343
|
+
// Seed initial subscriptions from config
|
|
344
|
+
for(var _iterator = this.config.markets[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
345
|
+
var spec = _step.value;
|
|
346
|
+
this.activeSubscriptions.set(spec.marketId, _to_consumable_array(spec.tokenIds));
|
|
347
|
+
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
348
|
+
try {
|
|
349
|
+
for(var _iterator1 = spec.tokenIds[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
350
|
+
var tokenId = _step1.value;
|
|
351
|
+
this.tokenToMarket.set(tokenId, spec.marketId);
|
|
352
|
+
}
|
|
353
|
+
} catch (err) {
|
|
354
|
+
_didIteratorError1 = true;
|
|
355
|
+
_iteratorError1 = err;
|
|
356
|
+
} finally{
|
|
357
|
+
try {
|
|
358
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
359
|
+
_iterator1.return();
|
|
360
|
+
}
|
|
361
|
+
} finally{
|
|
362
|
+
if (_didIteratorError1) {
|
|
363
|
+
throw _iteratorError1;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
} catch (err) {
|
|
369
|
+
_didIteratorError = true;
|
|
370
|
+
_iteratorError = err;
|
|
371
|
+
} finally{
|
|
372
|
+
try {
|
|
373
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
374
|
+
_iterator.return();
|
|
375
|
+
}
|
|
376
|
+
} finally{
|
|
377
|
+
if (_didIteratorError) {
|
|
378
|
+
throw _iteratorError;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
299
382
|
}
|
|
300
383
|
_create_class(MarketDepthSyncClient, [
|
|
301
384
|
{
|
|
302
385
|
key: "connect",
|
|
303
|
-
value:
|
|
386
|
+
value: // ---------------------------------------------------------------------------
|
|
387
|
+
// Public API
|
|
388
|
+
// ---------------------------------------------------------------------------
|
|
389
|
+
/** Open the WebSocket connection and subscribe to all configured markets. */ function connect() {
|
|
304
390
|
return _async_to_generator(function() {
|
|
305
391
|
var _this, wsUrl;
|
|
306
392
|
return _ts_generator(this, function(_state) {
|
|
307
393
|
_this = this;
|
|
308
|
-
// Clean up any existing connection first
|
|
309
394
|
this.stopHeartbeat();
|
|
310
395
|
this.clearReconnectTimeout();
|
|
311
396
|
if (this.ws) {
|
|
312
397
|
this.ws.onclose = null;
|
|
313
|
-
// Prevent triggering reconnect
|
|
314
398
|
this.ws.close();
|
|
315
399
|
this.ws = null;
|
|
316
400
|
}
|
|
317
401
|
this.shouldBeConnected = true;
|
|
402
|
+
this.syncedMarkets.clear();
|
|
403
|
+
this.frameQueues.clear();
|
|
404
|
+
this.processingMarkets.clear();
|
|
318
405
|
this.setStatus("connecting");
|
|
319
406
|
wsUrl = this.config.wsUrl;
|
|
320
407
|
if (!wsUrl.startsWith("ws://") && !wsUrl.startsWith("wss://")) {
|
|
321
408
|
throw new Error("Invalid WebSocket URL: ".concat(wsUrl));
|
|
322
409
|
}
|
|
323
|
-
// Prevent queue processing until snapshots are received
|
|
324
|
-
this.isProcessing = true;
|
|
325
|
-
// Set up visibility change handler
|
|
326
410
|
this.setupVisibilityChangeHandler();
|
|
327
411
|
return [
|
|
328
412
|
2,
|
|
329
413
|
new Promise(function(resolve, reject) {
|
|
330
414
|
var settled = false;
|
|
331
415
|
var resolveOnce = function() {
|
|
332
|
-
if (settled)
|
|
333
|
-
|
|
334
|
-
|
|
416
|
+
if (!settled) {
|
|
417
|
+
settled = true;
|
|
418
|
+
resolve();
|
|
419
|
+
}
|
|
335
420
|
};
|
|
336
|
-
var rejectOnce = function(
|
|
337
|
-
if (settled)
|
|
338
|
-
|
|
339
|
-
|
|
421
|
+
var rejectOnce = function(e) {
|
|
422
|
+
if (!settled) {
|
|
423
|
+
settled = true;
|
|
424
|
+
reject(e);
|
|
425
|
+
}
|
|
340
426
|
};
|
|
341
427
|
_this.ws = new WebSocket(wsUrl);
|
|
342
428
|
_this.ws.onopen = function() {
|
|
343
|
-
// Reset reconnect attempts on successful connection
|
|
344
429
|
_this.reconnectAttempts = 0;
|
|
345
430
|
_this.lastPongTime = Date.now();
|
|
346
|
-
// Send subscribe_market message
|
|
347
|
-
_this.ws.send(JSON.stringify({
|
|
348
|
-
type: "subscribe_market",
|
|
349
|
-
marketId: _this.config.marketId,
|
|
350
|
-
tokenIds: _this.config.tokenIds
|
|
351
|
-
}));
|
|
352
|
-
// Start heartbeat
|
|
353
431
|
_this.startHeartbeat();
|
|
432
|
+
if (_this.activeSubscriptions.size === 0) {
|
|
433
|
+
_this.setStatus("synced");
|
|
434
|
+
resolveOnce();
|
|
435
|
+
} else {
|
|
436
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
437
|
+
try {
|
|
438
|
+
for(var _iterator = _this.activeSubscriptions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
439
|
+
var _step_value = _sliced_to_array(_step.value, 2), marketId = _step_value[0], tokenIds = _step_value[1];
|
|
440
|
+
_this._sendSubscribeMarket(marketId, tokenIds);
|
|
441
|
+
}
|
|
442
|
+
} catch (err) {
|
|
443
|
+
_didIteratorError = true;
|
|
444
|
+
_iteratorError = err;
|
|
445
|
+
} finally{
|
|
446
|
+
try {
|
|
447
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
448
|
+
_iterator.return();
|
|
449
|
+
}
|
|
450
|
+
} finally{
|
|
451
|
+
if (_didIteratorError) {
|
|
452
|
+
throw _iteratorError;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// Resolve immediately once WS is open — snapshots arrive async
|
|
457
|
+
resolveOnce();
|
|
458
|
+
}
|
|
354
459
|
};
|
|
355
460
|
_this.ws.onmessage = function(event) {
|
|
356
461
|
try {
|
|
357
462
|
var msg = JSON.parse(event.data);
|
|
358
|
-
// Handle pong response
|
|
359
463
|
if (msg.type === "pong") {
|
|
360
464
|
_this.lastPongTime = Date.now();
|
|
361
465
|
_this.clearHeartbeatTimeout();
|
|
362
466
|
return;
|
|
363
467
|
}
|
|
364
468
|
if (msg.type === "subscribed_market") {
|
|
365
|
-
// Received initial snapshots
|
|
366
469
|
_this.handleSubscribedMarket(msg);
|
|
367
|
-
_this.isProcessing = false;
|
|
368
|
-
_this.setStatus("synced");
|
|
369
|
-
void _this.processFrameQueue();
|
|
370
|
-
resolveOnce();
|
|
371
470
|
} else if (msg.type === "depth_update") {
|
|
372
471
|
_this.handleDepthUpdate(msg);
|
|
373
472
|
} else if (msg.type === "market_state") {
|
|
374
|
-
// Received market state (bestBid, bestAsk, lastPrice) - preferred over last_price
|
|
375
473
|
_this.handleMarketStateUpdate(msg);
|
|
376
474
|
} else if (msg.type === "last_price") {
|
|
377
|
-
// Legacy: last price only (still supported)
|
|
378
475
|
_this.handleLastPriceUpdate(msg);
|
|
379
476
|
} else if (msg.type === "error") {
|
|
380
|
-
console.error("
|
|
477
|
+
console.error("[MarketDepthSyncClient] server error:", msg.message);
|
|
381
478
|
rejectOnce(new Error(msg.message));
|
|
382
479
|
}
|
|
383
480
|
} catch (error) {
|
|
384
|
-
console.error("
|
|
481
|
+
console.error("[MarketDepthSyncClient] parse error:", error);
|
|
385
482
|
}
|
|
386
483
|
};
|
|
387
484
|
_this.ws.onerror = function(error) {
|
|
388
|
-
console.error("WebSocket error:", error);
|
|
389
|
-
// Only reject if this is the initial connection
|
|
390
485
|
if (_this.status === "connecting") {
|
|
391
486
|
rejectOnce(error);
|
|
392
487
|
}
|
|
393
488
|
};
|
|
394
489
|
_this.ws.onclose = function() {
|
|
395
490
|
_this.stopHeartbeat();
|
|
491
|
+
_this.syncedMarkets.clear();
|
|
396
492
|
_this.setStatus("disconnected");
|
|
397
493
|
if (!settled) {
|
|
398
|
-
rejectOnce(new Error("WebSocket closed before
|
|
494
|
+
rejectOnce(new Error("[MarketDepthSyncClient] WebSocket closed before connecting"));
|
|
399
495
|
}
|
|
400
|
-
// Attempt to reconnect if we should still be connected
|
|
401
496
|
if (_this.shouldBeConnected) {
|
|
402
497
|
_this.scheduleReconnect();
|
|
403
498
|
}
|
|
@@ -409,210 +504,257 @@ function scheduleDeferred(callback) {
|
|
|
409
504
|
}
|
|
410
505
|
},
|
|
411
506
|
{
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
this.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
507
|
+
/**
|
|
508
|
+
* Subscribe to a market's depth. If the WebSocket is already open, sends the
|
|
509
|
+
* `subscribe_market` message immediately. Otherwise, the subscription is queued
|
|
510
|
+
* and sent when the connection opens. Calling this for an already-subscribed
|
|
511
|
+
* market re-requests a fresh snapshot.
|
|
512
|
+
*/ key: "subscribeMarket",
|
|
513
|
+
value: function subscribeMarket(marketId, tokenIds) {
|
|
514
|
+
this.activeSubscriptions.set(marketId, _to_consumable_array(tokenIds));
|
|
515
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
516
|
+
try {
|
|
517
|
+
for(var _iterator = tokenIds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
518
|
+
var tokenId = _step.value;
|
|
519
|
+
this.tokenToMarket.set(tokenId, marketId);
|
|
423
520
|
}
|
|
424
|
-
}
|
|
425
|
-
|
|
521
|
+
} catch (err) {
|
|
522
|
+
_didIteratorError = true;
|
|
523
|
+
_iteratorError = err;
|
|
524
|
+
} finally{
|
|
525
|
+
try {
|
|
526
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
527
|
+
_iterator.return();
|
|
528
|
+
}
|
|
529
|
+
} finally{
|
|
530
|
+
if (_didIteratorError) {
|
|
531
|
+
throw _iteratorError;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
536
|
+
this._sendSubscribeMarket(marketId, tokenIds);
|
|
537
|
+
}
|
|
426
538
|
}
|
|
427
539
|
},
|
|
428
540
|
{
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
541
|
+
/**
|
|
542
|
+
* Unsubscribe from a market. Sends `unsubscribe_market` to the server and
|
|
543
|
+
* clears local state for that market's tokens. The WebSocket connection is
|
|
544
|
+
* kept open for other subscriptions.
|
|
545
|
+
*/ key: "unsubscribeMarket",
|
|
546
|
+
value: function unsubscribeMarket(marketId) {
|
|
547
|
+
var _this_activeSubscriptions_get;
|
|
548
|
+
var tokenIds = (_this_activeSubscriptions_get = this.activeSubscriptions.get(marketId)) !== null && _this_activeSubscriptions_get !== void 0 ? _this_activeSubscriptions_get : [];
|
|
549
|
+
this.activeSubscriptions.delete(marketId);
|
|
550
|
+
this.syncedMarkets.delete(marketId);
|
|
551
|
+
this.frameQueues.delete(marketId);
|
|
552
|
+
this.processingMarkets.delete(marketId);
|
|
553
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
554
|
+
try {
|
|
555
|
+
for(var _iterator = tokenIds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
556
|
+
var tokenId = _step.value;
|
|
557
|
+
this.tokenStates.delete(tokenId);
|
|
558
|
+
this.tokenToMarket.delete(tokenId);
|
|
559
|
+
}
|
|
560
|
+
} catch (err) {
|
|
561
|
+
_didIteratorError = true;
|
|
562
|
+
_iteratorError = err;
|
|
563
|
+
} finally{
|
|
564
|
+
try {
|
|
565
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
566
|
+
_iterator.return();
|
|
567
|
+
}
|
|
568
|
+
} finally{
|
|
569
|
+
if (_didIteratorError) {
|
|
570
|
+
throw _iteratorError;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
575
|
+
try {
|
|
576
|
+
this.ws.send(JSON.stringify({
|
|
577
|
+
type: "unsubscribe_market",
|
|
578
|
+
marketId: marketId
|
|
579
|
+
}));
|
|
580
|
+
} catch (unused) {}
|
|
435
581
|
}
|
|
436
582
|
}
|
|
437
583
|
},
|
|
438
584
|
{
|
|
439
|
-
key: "
|
|
440
|
-
value:
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
// Send failed, connection is dead
|
|
461
|
-
this.handleConnectionLost();
|
|
462
|
-
}
|
|
585
|
+
key: "disconnect",
|
|
586
|
+
value: // Ignore — connection closing
|
|
587
|
+
/** Fully disconnect and stop reconnecting. */ function disconnect() {
|
|
588
|
+
return _async_to_generator(function() {
|
|
589
|
+
return _ts_generator(this, function(_state) {
|
|
590
|
+
this.shouldBeConnected = false;
|
|
591
|
+
this.stopHeartbeat();
|
|
592
|
+
this.clearReconnectTimeout();
|
|
593
|
+
this.removeVisibilityChangeHandler();
|
|
594
|
+
if (this.ws) {
|
|
595
|
+
this.ws.onclose = null;
|
|
596
|
+
this.ws.close();
|
|
597
|
+
this.ws = null;
|
|
598
|
+
}
|
|
599
|
+
this.syncedMarkets.clear();
|
|
600
|
+
this.setStatus("disconnected");
|
|
601
|
+
return [
|
|
602
|
+
2
|
|
603
|
+
];
|
|
604
|
+
});
|
|
605
|
+
}).call(this);
|
|
463
606
|
}
|
|
464
607
|
},
|
|
465
608
|
{
|
|
466
|
-
key: "
|
|
467
|
-
value: function
|
|
468
|
-
this.
|
|
469
|
-
if (this.ws) {
|
|
470
|
-
this.ws.onclose = null;
|
|
471
|
-
// Prevent double-triggering
|
|
472
|
-
this.ws.close();
|
|
473
|
-
this.ws = null;
|
|
474
|
-
}
|
|
475
|
-
this.setStatus("disconnected");
|
|
476
|
-
if (this.shouldBeConnected) {
|
|
477
|
-
this.scheduleReconnect();
|
|
478
|
-
}
|
|
609
|
+
key: "getStatus",
|
|
610
|
+
value: function getStatus() {
|
|
611
|
+
return this.status;
|
|
479
612
|
}
|
|
480
613
|
},
|
|
481
614
|
{
|
|
482
|
-
key: "
|
|
483
|
-
value: function
|
|
484
|
-
|
|
485
|
-
this.
|
|
486
|
-
this.heartbeatIntervalId = setInterval(function() {
|
|
487
|
-
if (!_this.ws || _this.ws.readyState !== WebSocket.OPEN) {
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
try {
|
|
491
|
-
_this.ws.send(JSON.stringify({
|
|
492
|
-
type: "ping"
|
|
493
|
-
}));
|
|
494
|
-
// Set timeout for pong response
|
|
495
|
-
_this.clearHeartbeatTimeout();
|
|
496
|
-
_this.heartbeatTimeoutId = setTimeout(function() {
|
|
497
|
-
console.log("Heartbeat timeout, reconnecting...");
|
|
498
|
-
_this.handleConnectionLost();
|
|
499
|
-
}, _this.config.heartbeatTimeoutMs);
|
|
500
|
-
} catch (unused) {
|
|
501
|
-
// Send failed, connection is dead
|
|
502
|
-
_this.handleConnectionLost();
|
|
503
|
-
}
|
|
504
|
-
}, this.config.heartbeatIntervalMs);
|
|
615
|
+
key: "isSynced",
|
|
616
|
+
value: function isSynced() {
|
|
617
|
+
if (this.activeSubscriptions.size === 0) return this.status === "synced";
|
|
618
|
+
return this.activeSubscriptions.size === this.syncedMarkets.size;
|
|
505
619
|
}
|
|
506
620
|
},
|
|
507
621
|
{
|
|
508
|
-
key: "
|
|
509
|
-
value: function
|
|
510
|
-
|
|
511
|
-
clearInterval(this.heartbeatIntervalId);
|
|
512
|
-
this.heartbeatIntervalId = null;
|
|
513
|
-
}
|
|
514
|
-
this.clearHeartbeatTimeout();
|
|
622
|
+
key: "isMarketSynced",
|
|
623
|
+
value: function isMarketSynced(marketId) {
|
|
624
|
+
return this.syncedMarkets.has(marketId);
|
|
515
625
|
}
|
|
516
626
|
},
|
|
517
627
|
{
|
|
518
|
-
key: "
|
|
519
|
-
value: function
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
628
|
+
key: "getBids",
|
|
629
|
+
value: function getBids(tokenId) {
|
|
630
|
+
var state = this.tokenStates.get(tokenId);
|
|
631
|
+
if (!state) return [];
|
|
632
|
+
return Array.from(state.bids.entries()).map(function(param) {
|
|
633
|
+
var _param = _sliced_to_array(param, 2), price = _param[0], depth = _param[1];
|
|
634
|
+
return {
|
|
635
|
+
price: price,
|
|
636
|
+
depth: depth
|
|
637
|
+
};
|
|
638
|
+
});
|
|
524
639
|
}
|
|
525
640
|
},
|
|
526
641
|
{
|
|
527
|
-
key: "
|
|
528
|
-
value: function
|
|
529
|
-
var
|
|
530
|
-
if (!
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
var maxDelay = this.config.maxReconnectDelayMs;
|
|
539
|
-
var delay = Math.min(baseDelay * Math.pow(2, this.reconnectAttempts) + Math.random() * 1000, maxDelay);
|
|
540
|
-
this.reconnectAttempts++;
|
|
541
|
-
this.setStatus("recovering");
|
|
542
|
-
console.log("Scheduling reconnect attempt ".concat(this.reconnectAttempts, " in ").concat(Math.round(delay), "ms"));
|
|
543
|
-
this.clearReconnectTimeout();
|
|
544
|
-
this.reconnectTimeoutId = setTimeout(function() {
|
|
545
|
-
_this.performReconnect();
|
|
546
|
-
}, delay);
|
|
642
|
+
key: "getAsks",
|
|
643
|
+
value: function getAsks(tokenId) {
|
|
644
|
+
var state = this.tokenStates.get(tokenId);
|
|
645
|
+
if (!state) return [];
|
|
646
|
+
return Array.from(state.asks.entries()).map(function(param) {
|
|
647
|
+
var _param = _sliced_to_array(param, 2), price = _param[0], depth = _param[1];
|
|
648
|
+
return {
|
|
649
|
+
price: price,
|
|
650
|
+
depth: depth
|
|
651
|
+
};
|
|
652
|
+
});
|
|
547
653
|
}
|
|
548
654
|
},
|
|
549
655
|
{
|
|
550
|
-
key: "
|
|
551
|
-
value: function
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
656
|
+
key: "getBestBid",
|
|
657
|
+
value: function getBestBid(tokenId) {
|
|
658
|
+
var _ref;
|
|
659
|
+
var _this_tokenStates_get;
|
|
660
|
+
return (_ref = (_this_tokenStates_get = this.tokenStates.get(tokenId)) === null || _this_tokenStates_get === void 0 ? void 0 : _this_tokenStates_get.bestBid) !== null && _ref !== void 0 ? _ref : null;
|
|
556
661
|
}
|
|
557
662
|
},
|
|
558
663
|
{
|
|
559
|
-
key: "
|
|
560
|
-
value: function
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
switch(_state.label){
|
|
565
|
-
case 0:
|
|
566
|
-
if (!this.shouldBeConnected) return [
|
|
567
|
-
2
|
|
568
|
-
];
|
|
569
|
-
_state.label = 1;
|
|
570
|
-
case 1:
|
|
571
|
-
_state.trys.push([
|
|
572
|
-
1,
|
|
573
|
-
3,
|
|
574
|
-
,
|
|
575
|
-
4
|
|
576
|
-
]);
|
|
577
|
-
// Clear state before reconnecting
|
|
578
|
-
this.tokenStates.clear();
|
|
579
|
-
this.frameQueue = [];
|
|
580
|
-
return [
|
|
581
|
-
4,
|
|
582
|
-
this.connect()
|
|
583
|
-
];
|
|
584
|
-
case 2:
|
|
585
|
-
_state.sent();
|
|
586
|
-
console.log("Reconnected successfully");
|
|
587
|
-
return [
|
|
588
|
-
3,
|
|
589
|
-
4
|
|
590
|
-
];
|
|
591
|
-
case 3:
|
|
592
|
-
error = _state.sent();
|
|
593
|
-
console.error("Reconnection failed:", error);
|
|
594
|
-
return [
|
|
595
|
-
3,
|
|
596
|
-
4
|
|
597
|
-
];
|
|
598
|
-
case 4:
|
|
599
|
-
return [
|
|
600
|
-
2
|
|
601
|
-
];
|
|
602
|
-
}
|
|
603
|
-
});
|
|
604
|
-
}).call(this);
|
|
664
|
+
key: "getBestAsk",
|
|
665
|
+
value: function getBestAsk(tokenId) {
|
|
666
|
+
var _ref;
|
|
667
|
+
var _this_tokenStates_get;
|
|
668
|
+
return (_ref = (_this_tokenStates_get = this.tokenStates.get(tokenId)) === null || _this_tokenStates_get === void 0 ? void 0 : _this_tokenStates_get.bestAsk) !== null && _ref !== void 0 ? _ref : null;
|
|
605
669
|
}
|
|
606
670
|
},
|
|
607
671
|
{
|
|
608
|
-
key: "
|
|
609
|
-
value:
|
|
610
|
-
|
|
611
|
-
var
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
672
|
+
key: "getLastPrice",
|
|
673
|
+
value: function getLastPrice(tokenId) {
|
|
674
|
+
var _ref;
|
|
675
|
+
var _this_tokenStates_get;
|
|
676
|
+
return (_ref = (_this_tokenStates_get = this.tokenStates.get(tokenId)) === null || _this_tokenStates_get === void 0 ? void 0 : _this_tokenStates_get.lastPrice) !== null && _ref !== void 0 ? _ref : null;
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
key: "onSnapshot",
|
|
681
|
+
value: function onSnapshot(listener) {
|
|
682
|
+
var _this = this;
|
|
683
|
+
this.snapshotListeners.add(listener);
|
|
684
|
+
return function() {
|
|
685
|
+
return _this.snapshotListeners.delete(listener);
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
key: "onDelta",
|
|
691
|
+
value: function onDelta(listener) {
|
|
692
|
+
var _this = this;
|
|
693
|
+
this.deltaListeners.add(listener);
|
|
694
|
+
return function() {
|
|
695
|
+
return _this.deltaListeners.delete(listener);
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
key: "onStatus",
|
|
701
|
+
value: function onStatus(listener) {
|
|
702
|
+
var _this = this;
|
|
703
|
+
this.statusListeners.add(listener);
|
|
704
|
+
return function() {
|
|
705
|
+
return _this.statusListeners.delete(listener);
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
key: "_sendSubscribeMarket",
|
|
711
|
+
value: // ---------------------------------------------------------------------------
|
|
712
|
+
// Private — WS message handlers
|
|
713
|
+
// ---------------------------------------------------------------------------
|
|
714
|
+
function _sendSubscribeMarket(marketId, tokenIds) {
|
|
715
|
+
var _this_activeSubscriptions_get;
|
|
716
|
+
// Clear stale state so we get a clean snapshot
|
|
717
|
+
var existingTokenIds = (_this_activeSubscriptions_get = this.activeSubscriptions.get(marketId)) !== null && _this_activeSubscriptions_get !== void 0 ? _this_activeSubscriptions_get : tokenIds;
|
|
718
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
719
|
+
try {
|
|
720
|
+
for(var _iterator = existingTokenIds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
721
|
+
var tokenId = _step.value;
|
|
722
|
+
this.tokenStates.delete(tokenId);
|
|
723
|
+
}
|
|
724
|
+
} catch (err) {
|
|
725
|
+
_didIteratorError = true;
|
|
726
|
+
_iteratorError = err;
|
|
727
|
+
} finally{
|
|
728
|
+
try {
|
|
729
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
730
|
+
_iterator.return();
|
|
731
|
+
}
|
|
732
|
+
} finally{
|
|
733
|
+
if (_didIteratorError) {
|
|
734
|
+
throw _iteratorError;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
this.syncedMarkets.delete(marketId);
|
|
739
|
+
this.frameQueues.delete(marketId);
|
|
740
|
+
try {
|
|
741
|
+
this.ws.send(JSON.stringify({
|
|
742
|
+
type: "subscribe_market",
|
|
743
|
+
marketId: marketId,
|
|
744
|
+
tokenIds: tokenIds
|
|
745
|
+
}));
|
|
746
|
+
} catch (e) {
|
|
747
|
+
console.warn("[MarketDepthSyncClient] failed to send subscribe_market:", e);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
key: "handleSubscribedMarket",
|
|
753
|
+
value: function handleSubscribedMarket(msg) {
|
|
754
|
+
var marketId = msg.marketId, snapshots = msg.snapshots;
|
|
755
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
756
|
+
try {
|
|
757
|
+
for(var _iterator = snapshots[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
616
758
|
var snapshot = _step.value;
|
|
617
759
|
var state = {
|
|
618
760
|
bids: new Map(),
|
|
@@ -678,20 +820,38 @@ function scheduleDeferred(callback) {
|
|
|
678
820
|
}
|
|
679
821
|
}
|
|
680
822
|
}
|
|
823
|
+
this.syncedMarkets.add(marketId);
|
|
824
|
+
if (this.activeSubscriptions.size > 0 && this.syncedMarkets.size >= this.activeSubscriptions.size) {
|
|
825
|
+
this.setStatus("synced");
|
|
826
|
+
}
|
|
681
827
|
this.snapshotListeners.forEach(function(listener) {
|
|
682
828
|
try {
|
|
683
|
-
listener(
|
|
684
|
-
} catch (
|
|
685
|
-
console.error(
|
|
829
|
+
listener(marketId, snapshots);
|
|
830
|
+
} catch (e) {
|
|
831
|
+
console.error(e);
|
|
686
832
|
}
|
|
687
833
|
});
|
|
834
|
+
// Process any buffered depth_updates for this market
|
|
835
|
+
void this.processFrameQueue(marketId);
|
|
688
836
|
}
|
|
689
837
|
},
|
|
690
838
|
{
|
|
691
839
|
key: "handleDepthUpdate",
|
|
692
840
|
value: function handleDepthUpdate(msg) {
|
|
693
|
-
|
|
694
|
-
|
|
841
|
+
var marketId = msg.marketId;
|
|
842
|
+
if (!this.syncedMarkets.has(marketId)) {
|
|
843
|
+
// Buffer until snapshot arrives
|
|
844
|
+
if (!this.frameQueues.has(marketId)) {
|
|
845
|
+
this.frameQueues.set(marketId, []);
|
|
846
|
+
}
|
|
847
|
+
this.frameQueues.get(marketId).push(msg);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
if (!this.frameQueues.has(marketId)) {
|
|
851
|
+
this.frameQueues.set(marketId, []);
|
|
852
|
+
}
|
|
853
|
+
this.frameQueues.get(marketId).push(msg);
|
|
854
|
+
void this.processFrameQueue(marketId);
|
|
695
855
|
}
|
|
696
856
|
},
|
|
697
857
|
{
|
|
@@ -699,83 +859,88 @@ function scheduleDeferred(callback) {
|
|
|
699
859
|
value: function handleMarketStateUpdate(msg) {
|
|
700
860
|
var _msg_bestBid, _msg_bestAsk, _msg_lastPrice;
|
|
701
861
|
var state = this.tokenStates.get(String(msg.tokenId));
|
|
702
|
-
if (!state)
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
862
|
+
if (!state) return;
|
|
705
863
|
state.bestBid = (_msg_bestBid = msg.bestBid) !== null && _msg_bestBid !== void 0 ? _msg_bestBid : state.bestBid;
|
|
706
864
|
state.bestAsk = (_msg_bestAsk = msg.bestAsk) !== null && _msg_bestAsk !== void 0 ? _msg_bestAsk : state.bestAsk;
|
|
707
865
|
state.lastPrice = (_msg_lastPrice = msg.lastPrice) !== null && _msg_lastPrice !== void 0 ? _msg_lastPrice : state.lastPrice;
|
|
708
|
-
this.emitOutOfBandUpdate(msg.tokenId, state);
|
|
866
|
+
this.emitOutOfBandUpdate(msg.marketId, msg.tokenId, state);
|
|
709
867
|
}
|
|
710
868
|
},
|
|
711
869
|
{
|
|
712
870
|
key: "handleLastPriceUpdate",
|
|
713
871
|
value: function handleLastPriceUpdate(msg) {
|
|
872
|
+
var _ref, _msg_marketId;
|
|
714
873
|
var state = this.tokenStates.get(String(msg.tokenId));
|
|
715
|
-
if (!state)
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
874
|
+
if (!state) return;
|
|
718
875
|
state.lastPrice = msg.lastPrice;
|
|
719
|
-
this.
|
|
876
|
+
var marketId = (_ref = (_msg_marketId = msg.marketId) !== null && _msg_marketId !== void 0 ? _msg_marketId : this.tokenToMarket.get(msg.tokenId)) !== null && _ref !== void 0 ? _ref : "";
|
|
877
|
+
this.emitOutOfBandUpdate(marketId, msg.tokenId, state);
|
|
720
878
|
}
|
|
721
879
|
},
|
|
722
880
|
{
|
|
723
881
|
key: "emitOutOfBandUpdate",
|
|
724
|
-
value:
|
|
725
|
-
var
|
|
882
|
+
value: function emitOutOfBandUpdate(marketId, tokenId, state) {
|
|
883
|
+
var update = {
|
|
884
|
+
tokenId: tokenId,
|
|
885
|
+
levels: [],
|
|
886
|
+
bestBid: state.bestBid,
|
|
887
|
+
bestAsk: state.bestAsk,
|
|
888
|
+
lastPrice: state.lastPrice,
|
|
889
|
+
seq: state.seq,
|
|
890
|
+
startSeq: state.seq,
|
|
891
|
+
endSeq: state.seq
|
|
892
|
+
};
|
|
726
893
|
this.deltaListeners.forEach(function(listener) {
|
|
727
894
|
try {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
bestBid: state.bestBid,
|
|
732
|
-
bestAsk: state.bestAsk,
|
|
733
|
-
lastPrice: state.lastPrice,
|
|
734
|
-
seq: state.seq,
|
|
735
|
-
startSeq: state.seq,
|
|
736
|
-
endSeq: state.seq
|
|
737
|
-
};
|
|
738
|
-
listener(_this.config.marketId, update);
|
|
739
|
-
} catch (error) {
|
|
740
|
-
console.error("Error in delta listener:", error);
|
|
895
|
+
listener(marketId, update);
|
|
896
|
+
} catch (e) {
|
|
897
|
+
console.error(e);
|
|
741
898
|
}
|
|
742
899
|
});
|
|
743
900
|
}
|
|
744
901
|
},
|
|
745
902
|
{
|
|
746
903
|
key: "processFrameQueue",
|
|
747
|
-
value: function processFrameQueue() {
|
|
904
|
+
value: function processFrameQueue(marketId) {
|
|
748
905
|
return _async_to_generator(function() {
|
|
749
|
-
var _this, _this1, _loop;
|
|
906
|
+
var _this, _this1, _loop, queue, _ret, queue1;
|
|
750
907
|
return _ts_generator(this, function(_state) {
|
|
751
908
|
_this = this;
|
|
752
|
-
if (this.
|
|
909
|
+
if (this.processingMarkets.has(marketId)) return [
|
|
753
910
|
2
|
|
754
911
|
];
|
|
755
|
-
this.
|
|
912
|
+
this.processingMarkets.add(marketId);
|
|
756
913
|
try {
|
|
757
914
|
_loop = function() {
|
|
758
|
-
var frame =
|
|
915
|
+
var frame = queue.shift();
|
|
759
916
|
var state = _this1.tokenStates.get(String(frame.tokenId));
|
|
760
917
|
if (!state) return "continue";
|
|
761
918
|
var startSeq = parseInt(frame.startSeq, 10);
|
|
762
919
|
var endSeq = parseInt(frame.endSeq, 10);
|
|
763
|
-
//
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
//
|
|
767
|
-
if (state.seq > 0 && startSeq > state.seq + 1) {
|
|
768
|
-
console.warn("[
|
|
920
|
+
// endSeq=0 is a sentinel meaning "no sequence tracking" — always apply.
|
|
921
|
+
// Non-zero endSeq <= state.seq means this frame was already applied.
|
|
922
|
+
if (endSeq !== 0 && endSeq <= state.seq) return "continue";
|
|
923
|
+
// Gap detection: warn and trigger resync for that specific market.
|
|
924
|
+
if (state.seq > 0 && startSeq > 0 && startSeq > state.seq + 1) {
|
|
925
|
+
console.warn("[MarketDepthSyncClient] gap detected tokenId=".concat(frame.tokenId, " ") + "expected=".concat(state.seq + 1, " got=").concat(startSeq, " — resyncing market ").concat(marketId));
|
|
926
|
+
// Re-subscribe to get a fresh snapshot (clears state, re-sends subscribe)
|
|
927
|
+
var tokenIds = _this1.activeSubscriptions.get(marketId);
|
|
928
|
+
if (tokenIds && _this1.ws && _this1.ws.readyState === WebSocket.OPEN) {
|
|
929
|
+
_this1._sendSubscribeMarket(marketId, tokenIds);
|
|
930
|
+
}
|
|
931
|
+
return {
|
|
932
|
+
v: void void 0
|
|
933
|
+
};
|
|
769
934
|
}
|
|
935
|
+
// Stop processing stale queue; new snapshot will restart it
|
|
770
936
|
var emittedLevels = [];
|
|
771
937
|
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
772
938
|
try {
|
|
773
939
|
for(var _iterator = frame.levels[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
774
940
|
var lv = _step.value;
|
|
775
|
-
// Per-level dedup against state.seq guards against re-applying
|
|
776
|
-
// levels from a partially-applied previous frame.
|
|
777
941
|
var lvSeq = parseInt(lv.seq, 10);
|
|
778
|
-
|
|
942
|
+
// lvSeq=0 means no sequence → always apply (same sentinel logic as endSeq)
|
|
943
|
+
if (lvSeq !== 0 && lvSeq <= state.seq) continue;
|
|
779
944
|
_this1.applyLevel(lv, state);
|
|
780
945
|
emittedLevels.push({
|
|
781
946
|
side: lv.side,
|
|
@@ -797,32 +962,44 @@ function scheduleDeferred(callback) {
|
|
|
797
962
|
}
|
|
798
963
|
}
|
|
799
964
|
}
|
|
800
|
-
|
|
965
|
+
// Advance seq only when endSeq is meaningful
|
|
966
|
+
if (endSeq > state.seq) state.seq = endSeq;
|
|
801
967
|
if (emittedLevels.length === 0) return "continue";
|
|
968
|
+
var update = {
|
|
969
|
+
tokenId: frame.tokenId,
|
|
970
|
+
levels: emittedLevels,
|
|
971
|
+
bestBid: state.bestBid,
|
|
972
|
+
bestAsk: state.bestAsk,
|
|
973
|
+
lastPrice: state.lastPrice,
|
|
974
|
+
seq: state.seq,
|
|
975
|
+
startSeq: startSeq,
|
|
976
|
+
endSeq: endSeq
|
|
977
|
+
};
|
|
802
978
|
_this1.deltaListeners.forEach(function(listener) {
|
|
803
979
|
try {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
bestBid: state.bestBid,
|
|
808
|
-
bestAsk: state.bestAsk,
|
|
809
|
-
lastPrice: state.lastPrice,
|
|
810
|
-
seq: endSeq,
|
|
811
|
-
startSeq: startSeq,
|
|
812
|
-
endSeq: endSeq
|
|
813
|
-
};
|
|
814
|
-
listener(_this.config.marketId, update);
|
|
815
|
-
} catch (error) {
|
|
816
|
-
console.error("Error in delta listener:", error);
|
|
980
|
+
listener(marketId, update);
|
|
981
|
+
} catch (e) {
|
|
982
|
+
console.error(e);
|
|
817
983
|
}
|
|
818
984
|
});
|
|
819
985
|
};
|
|
820
|
-
|
|
986
|
+
queue = this.frameQueues.get(marketId);
|
|
987
|
+
if (!queue) return [
|
|
988
|
+
2
|
|
989
|
+
];
|
|
990
|
+
while(queue.length > 0){
|
|
991
|
+
_ret = (_this1 = this, _loop());
|
|
992
|
+
if (_type_of(_ret) === "object") return [
|
|
993
|
+
2,
|
|
994
|
+
_ret.v
|
|
995
|
+
];
|
|
996
|
+
}
|
|
821
997
|
} finally{
|
|
822
|
-
this.
|
|
823
|
-
|
|
998
|
+
this.processingMarkets.delete(marketId);
|
|
999
|
+
queue1 = this.frameQueues.get(marketId);
|
|
1000
|
+
if (queue1 && queue1.length > 0) {
|
|
824
1001
|
scheduleDeferred(function() {
|
|
825
|
-
void _this.processFrameQueue();
|
|
1002
|
+
void _this.processFrameQueue(marketId);
|
|
826
1003
|
});
|
|
827
1004
|
}
|
|
828
1005
|
}
|
|
@@ -844,7 +1021,6 @@ function scheduleDeferred(callback) {
|
|
|
844
1021
|
} else {
|
|
845
1022
|
map.set(key, level.depth);
|
|
846
1023
|
}
|
|
847
|
-
// Recalculate best prices from current state
|
|
848
1024
|
if (level.side === "bid") {
|
|
849
1025
|
var _Math;
|
|
850
1026
|
var bidPrices = Array.from(state.bids.keys()).map(function(p) {
|
|
@@ -868,186 +1044,190 @@ function scheduleDeferred(callback) {
|
|
|
868
1044
|
this.statusListeners.forEach(function(listener) {
|
|
869
1045
|
try {
|
|
870
1046
|
listener(status);
|
|
871
|
-
} catch (
|
|
872
|
-
console.error(
|
|
1047
|
+
} catch (e) {
|
|
1048
|
+
console.error(e);
|
|
873
1049
|
}
|
|
874
1050
|
});
|
|
875
1051
|
}
|
|
876
1052
|
},
|
|
877
1053
|
{
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
value: function getTokenIds() {
|
|
893
|
-
return Array.from(this.tokenStates.keys());
|
|
894
|
-
}
|
|
895
|
-
},
|
|
896
|
-
{
|
|
897
|
-
/** Returns raw internal token state for advanced integrations. */ key: "getTokenState",
|
|
898
|
-
value: function getTokenState(tokenId) {
|
|
899
|
-
return this.tokenStates.get(tokenId);
|
|
900
|
-
}
|
|
901
|
-
},
|
|
902
|
-
{
|
|
903
|
-
/** Returns the current bid ladder for a token, sorted from highest to lowest price. */ key: "getBids",
|
|
904
|
-
value: function getBids(tokenId) {
|
|
905
|
-
var state = this.tokenStates.get(tokenId);
|
|
906
|
-
if (!state) return [];
|
|
907
|
-
return Array.from(state.bids.entries()).map(function(param) {
|
|
908
|
-
var _param = _sliced_to_array(param, 2), price = _param[0], depth = _param[1];
|
|
909
|
-
return {
|
|
910
|
-
price: price,
|
|
911
|
-
depth: depth
|
|
912
|
-
};
|
|
913
|
-
}).sort(function(a, b) {
|
|
914
|
-
return parseFloat(b.price) - parseFloat(a.price);
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
|
-
},
|
|
918
|
-
{
|
|
919
|
-
// Highest first
|
|
920
|
-
/** Returns the current ask ladder for a token, sorted from lowest to highest price. */ key: "getAsks",
|
|
921
|
-
value: function getAsks(tokenId) {
|
|
922
|
-
var state = this.tokenStates.get(tokenId);
|
|
923
|
-
if (!state) return [];
|
|
924
|
-
return Array.from(state.asks.entries()).map(function(param) {
|
|
925
|
-
var _param = _sliced_to_array(param, 2), price = _param[0], depth = _param[1];
|
|
926
|
-
return {
|
|
927
|
-
price: price,
|
|
928
|
-
depth: depth
|
|
929
|
-
};
|
|
930
|
-
}).sort(function(a, b) {
|
|
931
|
-
return parseFloat(a.price) - parseFloat(b.price);
|
|
932
|
-
});
|
|
933
|
-
}
|
|
934
|
-
},
|
|
935
|
-
{
|
|
936
|
-
// Lowest first
|
|
937
|
-
/** Returns the best bid price for a token, if known. */ key: "getBestBid",
|
|
938
|
-
value: function getBestBid(tokenId) {
|
|
939
|
-
var _ref;
|
|
940
|
-
var _this_tokenStates_get;
|
|
941
|
-
return (_ref = (_this_tokenStates_get = this.tokenStates.get(tokenId)) === null || _this_tokenStates_get === void 0 ? void 0 : _this_tokenStates_get.bestBid) !== null && _ref !== void 0 ? _ref : null;
|
|
1054
|
+
key: "setupVisibilityChangeHandler",
|
|
1055
|
+
value: // ---------------------------------------------------------------------------
|
|
1056
|
+
// Private — reconnection & heartbeat
|
|
1057
|
+
// ---------------------------------------------------------------------------
|
|
1058
|
+
function setupVisibilityChangeHandler() {
|
|
1059
|
+
var _this = this;
|
|
1060
|
+
if (typeof document === "undefined") return;
|
|
1061
|
+
this.removeVisibilityChangeHandler();
|
|
1062
|
+
this.visibilityChangeHandler = function() {
|
|
1063
|
+
if (document.visibilityState === "visible" && _this.shouldBeConnected) {
|
|
1064
|
+
_this.checkConnectionHealth();
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
document.addEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
942
1068
|
}
|
|
943
1069
|
},
|
|
944
1070
|
{
|
|
945
|
-
|
|
946
|
-
value: function
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1071
|
+
key: "removeVisibilityChangeHandler",
|
|
1072
|
+
value: function removeVisibilityChangeHandler() {
|
|
1073
|
+
if (typeof document === "undefined") return;
|
|
1074
|
+
if (this.visibilityChangeHandler) {
|
|
1075
|
+
document.removeEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
1076
|
+
this.visibilityChangeHandler = null;
|
|
1077
|
+
}
|
|
950
1078
|
}
|
|
951
1079
|
},
|
|
952
1080
|
{
|
|
953
|
-
|
|
954
|
-
value: function
|
|
955
|
-
var
|
|
956
|
-
|
|
957
|
-
|
|
1081
|
+
key: "checkConnectionHealth",
|
|
1082
|
+
value: function checkConnectionHealth() {
|
|
1083
|
+
var _this = this;
|
|
1084
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
1085
|
+
this.handleConnectionLost();
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
try {
|
|
1089
|
+
this.ws.send(JSON.stringify({
|
|
1090
|
+
type: "ping"
|
|
1091
|
+
}));
|
|
1092
|
+
this.clearHeartbeatTimeout();
|
|
1093
|
+
this.heartbeatTimeoutId = setTimeout(function() {
|
|
1094
|
+
_this.handleConnectionLost();
|
|
1095
|
+
}, this.config.heartbeatTimeoutMs);
|
|
1096
|
+
} catch (unused) {
|
|
1097
|
+
this.handleConnectionLost();
|
|
1098
|
+
}
|
|
958
1099
|
}
|
|
959
1100
|
},
|
|
960
1101
|
{
|
|
961
|
-
|
|
962
|
-
value: function
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1102
|
+
key: "handleConnectionLost",
|
|
1103
|
+
value: function handleConnectionLost() {
|
|
1104
|
+
this.stopHeartbeat();
|
|
1105
|
+
if (this.ws) {
|
|
1106
|
+
this.ws.onclose = null;
|
|
1107
|
+
this.ws.close();
|
|
1108
|
+
this.ws = null;
|
|
1109
|
+
}
|
|
1110
|
+
this.setStatus("disconnected");
|
|
1111
|
+
if (this.shouldBeConnected) {
|
|
1112
|
+
this.scheduleReconnect();
|
|
1113
|
+
}
|
|
966
1114
|
}
|
|
967
1115
|
},
|
|
968
1116
|
{
|
|
969
|
-
|
|
970
|
-
value: function
|
|
971
|
-
var
|
|
972
|
-
|
|
973
|
-
|
|
1117
|
+
key: "startHeartbeat",
|
|
1118
|
+
value: function startHeartbeat() {
|
|
1119
|
+
var _this = this;
|
|
1120
|
+
this.stopHeartbeat();
|
|
1121
|
+
this.heartbeatIntervalId = setInterval(function() {
|
|
1122
|
+
if (!_this.ws || _this.ws.readyState !== WebSocket.OPEN) return;
|
|
1123
|
+
try {
|
|
1124
|
+
_this.ws.send(JSON.stringify({
|
|
1125
|
+
type: "ping"
|
|
1126
|
+
}));
|
|
1127
|
+
_this.clearHeartbeatTimeout();
|
|
1128
|
+
_this.heartbeatTimeoutId = setTimeout(function() {
|
|
1129
|
+
_this.handleConnectionLost();
|
|
1130
|
+
}, _this.config.heartbeatTimeoutMs);
|
|
1131
|
+
} catch (unused) {
|
|
1132
|
+
_this.handleConnectionLost();
|
|
1133
|
+
}
|
|
1134
|
+
}, this.config.heartbeatIntervalMs);
|
|
974
1135
|
}
|
|
975
1136
|
},
|
|
976
1137
|
{
|
|
977
|
-
|
|
978
|
-
value: function
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1138
|
+
key: "stopHeartbeat",
|
|
1139
|
+
value: function stopHeartbeat() {
|
|
1140
|
+
if (this.heartbeatIntervalId) {
|
|
1141
|
+
clearInterval(this.heartbeatIntervalId);
|
|
1142
|
+
this.heartbeatIntervalId = null;
|
|
1143
|
+
}
|
|
1144
|
+
this.clearHeartbeatTimeout();
|
|
983
1145
|
}
|
|
984
1146
|
},
|
|
985
1147
|
{
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
return _this.statusListeners.delete(callback);
|
|
993
|
-
};
|
|
1148
|
+
key: "clearHeartbeatTimeout",
|
|
1149
|
+
value: function clearHeartbeatTimeout() {
|
|
1150
|
+
if (this.heartbeatTimeoutId) {
|
|
1151
|
+
clearTimeout(this.heartbeatTimeoutId);
|
|
1152
|
+
this.heartbeatTimeoutId = null;
|
|
1153
|
+
}
|
|
994
1154
|
}
|
|
995
1155
|
},
|
|
996
1156
|
{
|
|
997
|
-
|
|
998
|
-
value: function
|
|
1157
|
+
key: "scheduleReconnect",
|
|
1158
|
+
value: function scheduleReconnect() {
|
|
999
1159
|
var _this = this;
|
|
1000
|
-
this.
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1160
|
+
if (!this.shouldBeConnected) return;
|
|
1161
|
+
var maxAttempts = this.config.maxReconnectAttempts;
|
|
1162
|
+
if (this.reconnectAttempts >= maxAttempts) {
|
|
1163
|
+
console.error("[MarketDepthSyncClient] max reconnect attempts (".concat(maxAttempts, ") reached"));
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
var baseDelay = this.config.initialReconnectDelayMs;
|
|
1167
|
+
var maxDelay = this.config.maxReconnectDelayMs;
|
|
1168
|
+
var delay = Math.min(baseDelay * Math.pow(2, this.reconnectAttempts) + Math.random() * 1000, maxDelay);
|
|
1169
|
+
this.reconnectAttempts++;
|
|
1170
|
+
this.setStatus("recovering");
|
|
1171
|
+
this.clearReconnectTimeout();
|
|
1172
|
+
this.reconnectTimeoutId = setTimeout(function() {
|
|
1173
|
+
void _this.performReconnect();
|
|
1174
|
+
}, delay);
|
|
1004
1175
|
}
|
|
1005
1176
|
},
|
|
1006
1177
|
{
|
|
1007
|
-
|
|
1008
|
-
value: function
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
};
|
|
1178
|
+
key: "clearReconnectTimeout",
|
|
1179
|
+
value: function clearReconnectTimeout() {
|
|
1180
|
+
if (this.reconnectTimeoutId) {
|
|
1181
|
+
clearTimeout(this.reconnectTimeoutId);
|
|
1182
|
+
this.reconnectTimeoutId = null;
|
|
1183
|
+
}
|
|
1014
1184
|
}
|
|
1015
1185
|
},
|
|
1016
1186
|
{
|
|
1017
|
-
key: "
|
|
1018
|
-
value:
|
|
1187
|
+
key: "performReconnect",
|
|
1188
|
+
value: function performReconnect() {
|
|
1019
1189
|
return _async_to_generator(function() {
|
|
1190
|
+
var error;
|
|
1020
1191
|
return _ts_generator(this, function(_state) {
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1192
|
+
switch(_state.label){
|
|
1193
|
+
case 0:
|
|
1194
|
+
if (!this.shouldBeConnected) return [
|
|
1195
|
+
2
|
|
1196
|
+
];
|
|
1197
|
+
_state.label = 1;
|
|
1198
|
+
case 1:
|
|
1199
|
+
_state.trys.push([
|
|
1200
|
+
1,
|
|
1201
|
+
3,
|
|
1202
|
+
,
|
|
1203
|
+
4
|
|
1204
|
+
]);
|
|
1205
|
+
// Clear snapshot state — will be re-populated from fresh snapshots
|
|
1206
|
+
this.tokenStates.clear();
|
|
1207
|
+
this.frameQueues.clear();
|
|
1208
|
+
this.processingMarkets.clear();
|
|
1209
|
+
return [
|
|
1210
|
+
4,
|
|
1211
|
+
this.connect()
|
|
1212
|
+
];
|
|
1213
|
+
case 2:
|
|
1214
|
+
_state.sent();
|
|
1215
|
+
return [
|
|
1216
|
+
3,
|
|
1217
|
+
4
|
|
1218
|
+
];
|
|
1219
|
+
case 3:
|
|
1220
|
+
error = _state.sent();
|
|
1221
|
+
console.error("[MarketDepthSyncClient] reconnection failed:", error);
|
|
1222
|
+
return [
|
|
1223
|
+
3,
|
|
1224
|
+
4
|
|
1225
|
+
];
|
|
1226
|
+
case 4:
|
|
1227
|
+
return [
|
|
1228
|
+
2
|
|
1229
|
+
];
|
|
1044
1230
|
}
|
|
1045
|
-
this.frameQueue = [];
|
|
1046
|
-
this.tokenStates.clear();
|
|
1047
|
-
this.setStatus("disconnected");
|
|
1048
|
-
return [
|
|
1049
|
-
2
|
|
1050
|
-
];
|
|
1051
1231
|
});
|
|
1052
1232
|
}).call(this);
|
|
1053
1233
|
}
|