mdas-jsview-sdk 1.0.17-uat.0 → 1.0.21-uat.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/dist/mdas-sdk.js CHANGED
@@ -1283,6 +1283,24 @@
1283
1283
  <span class="l2-market-name"></span>
1284
1284
  </div>
1285
1285
  <span class="symbol editable-symbol">--</span>
1286
+ <div class="level1-info">
1287
+ <div class="l1-item">
1288
+ <span class="l1-label">Last:</span>
1289
+ <span class="l1-value l1-last-px">--</span>
1290
+ </div>
1291
+ <div class="l1-item">
1292
+ <span class="l1-label">Low:</span>
1293
+ <span class="l1-value l1-low-px">--</span>
1294
+ </div>
1295
+ <div class="l1-item">
1296
+ <span class="l1-label">High:</span>
1297
+ <span class="l1-value l1-high-px">--</span>
1298
+ </div>
1299
+ <div class="l1-item">
1300
+ <span class="l1-label">Volume:</span>
1301
+ <span class="l1-value l1-volume">--</span>
1302
+ </div>
1303
+ </div>
1286
1304
  </div>
1287
1305
  </div>
1288
1306
 
@@ -1957,6 +1975,38 @@
1957
1975
  color: #111827;
1958
1976
  }
1959
1977
 
1978
+ /* ========================================
1979
+ LEVEL 1 INFO (INSIDE HEADER)
1980
+ ======================================== */
1981
+
1982
+ .onbbo-level2-widget .level1-info {
1983
+ display: grid;
1984
+ grid-template-columns: repeat(4, 1fr);
1985
+ gap: 12px;
1986
+ margin-top: 10px;
1987
+ }
1988
+
1989
+ .onbbo-level2-widget .l1-item {
1990
+ display: flex;
1991
+ flex-direction: column;
1992
+ gap: 3px;
1993
+ }
1994
+
1995
+ .onbbo-level2-widget .l1-label {
1996
+ font-size: 11px;
1997
+ font-weight: 600;
1998
+ color: #6b7280;
1999
+ text-transform: uppercase;
2000
+ letter-spacing: 0.5px;
2001
+ }
2002
+
2003
+ .onbbo-level2-widget .l1-value {
2004
+ font-size: 15px;
2005
+ font-weight: 700;
2006
+ color: #111827;
2007
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2008
+ }
2009
+
1960
2010
  /* ========================================
1961
2011
  ORDER BOOK CONTAINER
1962
2012
  ======================================== */
@@ -2341,6 +2391,11 @@
2341
2391
  .onbbo-level2-widget .orderbook-panel:last-child {
2342
2392
  border-bottom: none;
2343
2393
  }
2394
+
2395
+ .onbbo-level2-widget .level1-info {
2396
+ grid-template-columns: repeat(2, 1fr);
2397
+ gap: 6px;
2398
+ }
2344
2399
  }
2345
2400
 
2346
2401
  @media (max-width: 480px) {
@@ -2359,6 +2414,19 @@
2359
2414
  text-align: left;
2360
2415
  }
2361
2416
 
2417
+ .onbbo-level2-widget .level1-info {
2418
+ grid-template-columns: repeat(2, 1fr);
2419
+ gap: 4px;
2420
+ }
2421
+
2422
+ .onbbo-level2-widget .l1-label {
2423
+ font-size: 9px;
2424
+ }
2425
+
2426
+ .onbbo-level2-widget .l1-value {
2427
+ font-size: 11px;
2428
+ }
2429
+
2362
2430
  .onbbo-level2-widget .panel-header {
2363
2431
  font-size: 10px;
2364
2432
  padding: 6px 2px;
@@ -4507,13 +4575,14 @@
4507
4575
  } */
4508
4576
 
4509
4577
  handleData(message) {
4578
+ console.log('DEBUG', message);
4510
4579
  if (this.loadingTimeout) {
4511
4580
  clearTimeout(this.loadingTimeout);
4512
4581
  this.loadingTimeout = null;
4513
4582
  }
4514
4583
 
4515
4584
  // Handle error messages from server (plain text converted to structured format)
4516
- if (message.type === 'error' && message.noData) {
4585
+ if (message.type === 'error') {
4517
4586
  if (this.debug) {
4518
4587
  console.log('[MarketDataWidget] Received no data message:', message.message);
4519
4588
  }
@@ -5283,13 +5352,16 @@
5283
5352
  // to avoid duplicate timeouts
5284
5353
  }
5285
5354
  handleData(message) {
5355
+ console.log('DEBUG NIGHT', message);
5356
+ //message = message.data || message.Data
5357
+
5286
5358
  if (this.loadingTimeout) {
5287
5359
  clearTimeout(this.loadingTimeout);
5288
5360
  this.loadingTimeout = null;
5289
5361
  }
5290
5362
 
5291
5363
  // Handle error messages from server (plain text converted to structured format)
5292
- if (message.type === 'error' && message.noData) {
5364
+ if (message.type === 'error' || message.error == true) {
5293
5365
  if (this.debug) {
5294
5366
  console.log('[NightSessionWidget] Received no data message:', message.message);
5295
5367
  }
@@ -5345,9 +5417,10 @@
5345
5417
  }
5346
5418
 
5347
5419
  // Filter for night session data
5348
- if (Array.isArray(message)) {
5420
+
5421
+ if (Array.isArray(message.data)) {
5349
5422
  // First, try to find data matching our symbol regardless of MarketName
5350
- const symbolData = message.find(item => item.Symbol === this.symbol);
5423
+ const symbolData = message.data.find(item => item.Symbol === this.symbol);
5351
5424
  if (symbolData) {
5352
5425
  if (this.debug) {
5353
5426
  console.log('[NightSessionWidget] Found data for symbol:', symbolData);
@@ -5387,34 +5460,35 @@
5387
5460
  }
5388
5461
  }
5389
5462
  // Handle wrapped format
5390
- else if (message.type === 'queryblueoceanl1' || message.type === 'querybrucel1' || message.type === 'queryonbbol1') {
5391
- if (message['0']?.Symbol === this.symbol) {
5392
- // For onbbo source, skip MarketName check
5393
- const isOnbbo = message.type === 'queryonbbol1';
5394
- const shouldShowNoData = message['0'].NotFound === true || !isOnbbo && (!message['0'].MarketName || message['0'].MarketName !== 'BLUE');
5395
- if (shouldShowNoData) {
5396
- // Only show no data state if we don't have cached data
5397
- if (!this.data) {
5398
- this.showNoDataState(message['0']);
5399
- } else {
5400
- this.hideLoading();
5401
- if (this.debug) {
5402
- console.log('[NightSessionWidget] No new data, keeping cached data visible');
5463
+ /* else if (message.type === 'queryblueoceanl1' || message.type === 'querybrucel1' || message.type === 'queryonbbol1') {
5464
+ if (message.data['0']?.Symbol === this.symbol) {
5465
+ // For onbbo source, skip MarketName check
5466
+ const isOnbbo = message.type === 'queryonbbol1';
5467
+ const shouldShowNoData = message['0'].NotFound === true ||
5468
+ (!isOnbbo && (!message['0'].MarketName || message['0'].MarketName !== 'BLUE'));
5469
+ if (shouldShowNoData) {
5470
+ // Only show no data state if we don't have cached data
5471
+ if (!this.data) {
5472
+ this.showNoDataState(message['0']);
5473
+ } else {
5474
+ this.hideLoading();
5475
+ if (this.debug) {
5476
+ console.log('[NightSessionWidget] No new data, keeping cached data visible');
5477
+ }
5478
+ }
5479
+ } else {
5480
+ const model = new NightSessionModel(message['0']);
5481
+ this.data = model; // Store for caching
5482
+ this.updateWidget(model);
5403
5483
  }
5404
- }
5405
5484
  } else {
5406
- const model = new NightSessionModel(message['0']);
5407
- this.data = model; // Store for caching
5408
- this.updateWidget(model);
5409
- }
5410
- } else {
5411
- // No matching symbol - keep cached data if available
5412
- if (this.debug) {
5413
- console.log('[NightSessionWidget] No matching symbol in response, keeping cached data');
5485
+ // No matching symbol - keep cached data if available
5486
+ if (this.debug) {
5487
+ console.log('[NightSessionWidget] No matching symbol in response, keeping cached data');
5488
+ }
5489
+ this.hideLoading();
5414
5490
  }
5415
- this.hideLoading();
5416
- }
5417
- }
5491
+ } */
5418
5492
  if (message._cached) ;
5419
5493
  }
5420
5494
  updateWidget(data) {
@@ -39091,7 +39165,7 @@ ${SharedStyles}
39091
39165
  let priceData = null;
39092
39166
 
39093
39167
  // Handle array format (standard night session format)
39094
- if (Array.isArray(message)) {
39168
+ if (Array.isArray(message.data)) {
39095
39169
  console.log('[IntradayChartWidget] Processing array format, length:', message.length);
39096
39170
  const symbolData = message.find(item => item.Symbol === this.symbol);
39097
39171
  console.log('[IntradayChartWidget] Found symbol data:', symbolData);
@@ -40062,8 +40136,10 @@ ${SharedStyles}
40062
40136
  this.styled = options.styled !== undefined ? options.styled : true;
40063
40137
  this.maxLevels = options.maxLevels || 10; // Number of levels to display
40064
40138
  this.data = null;
40139
+ this.level1Data = null; // Store Level 1 data separately
40065
40140
  this.isDestroyed = false;
40066
- this.unsubscribe = null;
40141
+ this.unsubscribeL2 = null;
40142
+ this.unsubscribeL1 = null;
40067
40143
  this.symbolEditor = null;
40068
40144
  this.loadingTimeout = null;
40069
40145
 
@@ -40164,13 +40240,22 @@ ${SharedStyles}
40164
40240
  this.showError(`No data received for ${upperSymbol}. Please try again.`);
40165
40241
  }, 10000);
40166
40242
 
40167
- // Unsubscribe from old symbol
40168
- if (this.unsubscribe) {
40243
+ // Unsubscribe from old symbol's L2 and L1 data
40244
+ if (this.unsubscribeL2) {
40169
40245
  if (this.debug) {
40170
- console.log(`[ONBBOLevel2Widget] Unsubscribing from ${this.symbol}`);
40246
+ console.log(`[ONBBOLevel2Widget] Unsubscribing from ${this.symbol} L2`);
40171
40247
  }
40172
- this.unsubscribe();
40173
- this.unsubscribe = null;
40248
+ this.wsManager.sendUnsubscribe('queryonbbol2', this.symbol);
40249
+ this.unsubscribeL2();
40250
+ this.unsubscribeL2 = null;
40251
+ }
40252
+ if (this.unsubscribeL1) {
40253
+ if (this.debug) {
40254
+ console.log(`[ONBBOLevel2Widget] Unsubscribing from ${this.symbol} L1`);
40255
+ }
40256
+ this.wsManager.sendUnsubscribe('queryonbbol1', this.symbol);
40257
+ this.unsubscribeL1();
40258
+ this.unsubscribeL1 = null;
40174
40259
  }
40175
40260
 
40176
40261
  // Update internal symbol
@@ -40240,10 +40325,51 @@ ${SharedStyles}
40240
40325
  }
40241
40326
  }
40242
40327
  subscribeToData() {
40243
- // Subscribe to ONBBO Level 2 data
40244
- this.unsubscribe = this.wsManager.subscribe(this.widgetId, ['queryonbbol2'],
40245
- // ONBBO Level 2 subscription type
40246
- this.handleMessage.bind(this), this.symbol);
40328
+ // Subscribe to ONBBO Level 2 data (order book)
40329
+ // Use separate widget IDs to avoid "already active" duplicate detection
40330
+ this.unsubscribeL2 = this.wsManager.subscribe(`${this.widgetId}-l2`, ['queryonbbol2'], messageWrapper => {
40331
+ const {
40332
+ event,
40333
+ data
40334
+ } = messageWrapper;
40335
+
40336
+ // Handle connection events
40337
+ if (event === 'connection') {
40338
+ this.handleConnectionStatus(data);
40339
+ return;
40340
+ }
40341
+
40342
+ // For data events, add type context
40343
+ if (event === 'data') {
40344
+ data._dataType = 'level2';
40345
+ this.handleMessage({
40346
+ event,
40347
+ data
40348
+ });
40349
+ }
40350
+ }, this.symbol);
40351
+
40352
+ // Subscribe to ONBBO Level 1 data (statistics: LastPx, LowPx, HighPx, Volume)
40353
+ this.unsubscribeL1 = this.wsManager.subscribe(`${this.widgetId}-l1`, ['queryonbbol1'], messageWrapper => {
40354
+ const {
40355
+ event,
40356
+ data
40357
+ } = messageWrapper;
40358
+
40359
+ // Connection already handled by L2 subscription
40360
+ if (event === 'connection') {
40361
+ return;
40362
+ }
40363
+
40364
+ // For data events, add type context
40365
+ if (event === 'data') {
40366
+ data._dataType = 'level1';
40367
+ this.handleMessage({
40368
+ event,
40369
+ data
40370
+ });
40371
+ }
40372
+ }, this.symbol);
40247
40373
  }
40248
40374
  handleData(message) {
40249
40375
  if (this.loadingTimeout) {
@@ -40251,8 +40377,14 @@ ${SharedStyles}
40251
40377
  this.loadingTimeout = null;
40252
40378
  }
40253
40379
 
40380
+ // Extract data type from metadata (added by subscription callback)
40381
+ const dataType = message._dataType;
40382
+ if (this.debug) {
40383
+ console.log(`[ONBBOLevel2Widget] handleData called with type: ${dataType}`, message);
40384
+ }
40385
+
40254
40386
  // Handle error messages
40255
- if (message.type === 'error') {
40387
+ if (message.type === 'error' || message.error == true) {
40256
40388
  const errorMsg = message.message || 'Server error';
40257
40389
  if (this.debug) {
40258
40390
  console.log('[ONBBOLevel2Widget] Error:', errorMsg);
@@ -40261,16 +40393,54 @@ ${SharedStyles}
40261
40393
  return;
40262
40394
  }
40263
40395
 
40396
+ // Handle Level 1 data (statistics: LastPx, LowPx, HighPx, Volume)
40397
+ if (message.type === 'queryonbbol1') {
40398
+ if (this.debug) {
40399
+ console.log('[ONBBOLevel2Widget] Processing Level 1 data:', message);
40400
+ }
40401
+
40402
+ // Handle array format (new format with Data wrapper)
40403
+ if (message.data && Array.isArray(message.data)) {
40404
+ const l1Data = message.data.find(d => d.Symbol === this.symbol);
40405
+ if (l1Data) {
40406
+ this.level1Data = {
40407
+ lastPx: l1Data.LastPx || 0,
40408
+ lowPx: l1Data.LowPx || 0,
40409
+ highPx: l1Data.HighPx || 0,
40410
+ volume: l1Data.Volume || 0
40411
+ };
40412
+ this.updateLevel1Display();
40413
+ }
40414
+ }
40415
+ // Handle direct array format (standard format)
40416
+ else if (Array.isArray(message)) {
40417
+ const l1Data = message.find(d => d.Symbol === this.symbol);
40418
+ if (l1Data) {
40419
+ this.level1Data = {
40420
+ lastPx: l1Data.LastPx || 0,
40421
+ lowPx: l1Data.LowPx || 0,
40422
+ highPx: l1Data.HighPx || 0,
40423
+ volume: l1Data.Volume || 0
40424
+ };
40425
+ this.updateLevel1Display();
40426
+ }
40427
+ }
40428
+
40429
+ // Clean up metadata
40430
+ delete message._dataType;
40431
+ return;
40432
+ }
40433
+
40264
40434
  // Handle Level 2 data - Array of MMID quotes
40265
- if (Array.isArray(message)) {
40435
+ if (Array.isArray(message.data)) {
40266
40436
  // Check if it's an array of MMID objects (ONBBO format)
40267
- if (message.length > 0 && message[0].MMID) {
40437
+ if (message.data.length > 0 && message.data[0].MMID) {
40268
40438
  if (this.debug) {
40269
40439
  console.log('[ONBBOLevel2Widget] Received MMID array:', message);
40270
40440
  }
40271
40441
 
40272
40442
  // Create model from MMID array
40273
- const model = new ONBBOLevel2Model(message);
40443
+ const model = new ONBBOLevel2Model(message.data);
40274
40444
  model.symbol = this.symbol; // Set symbol from widget
40275
40445
  this.data = model;
40276
40446
  this.updateWidget(model);
@@ -40278,7 +40448,7 @@ ${SharedStyles}
40278
40448
  }
40279
40449
 
40280
40450
  // Try to find symbol in array (alternative format)
40281
- const symbolData = message.find(item => item.Symbol === this.symbol);
40451
+ const symbolData = message.data.find(item => item.Symbol === this.symbol);
40282
40452
  if (symbolData) {
40283
40453
  if (symbolData.NotFound === true) {
40284
40454
  this.showError(`No Level 2 data available for ${this.symbol}`);
@@ -40292,32 +40462,31 @@ ${SharedStyles}
40292
40462
  this.showError(`No Level 2 data available for ${this.symbol}`);
40293
40463
  }
40294
40464
  // Handle wrapped format
40295
- else if (message.type === 'queryonbbol2') {
40296
- // Check if wrapped data contains MMID array
40297
- if (message['0'] && Array.isArray(message['0'])) {
40298
- if (this.debug) {
40299
- console.log('[ONBBOLevel2Widget] Received wrapped MMID array:', message['0']);
40465
+ /* else if (message.type === 'queryonbbol2') {
40466
+ // Check if wrapped data contains MMID array
40467
+ if (message['0'] && Array.isArray(message['0'])) {
40468
+ if (this.debug) {
40469
+ console.log('[ONBBOLevel2Widget] Received wrapped MMID array:', message['0']);
40470
+ }
40471
+ const model = new ONBBOLevel2Model(message['0']);
40472
+ model.symbol = this.symbol;
40473
+ this.data = model;
40474
+ this.updateWidget(model);
40475
+ return;
40300
40476
  }
40301
- const model = new ONBBOLevel2Model(message['0']);
40302
- model.symbol = this.symbol;
40303
- this.data = model;
40304
- this.updateWidget(model);
40305
- return;
40306
- }
40307
-
40308
- // Check for symbol match in wrapped format
40309
- if (message['0']?.Symbol === this.symbol) {
40310
- if (message['0'].NotFound === true) {
40311
- this.showError(`No Level 2 data available for ${this.symbol}`);
40477
+ // Check for symbol match in wrapped format
40478
+ if (message['0']?.Symbol === this.symbol) {
40479
+ if (message['0'].NotFound === true) {
40480
+ this.showError(`No Level 2 data available for ${this.symbol}`);
40481
+ } else {
40482
+ const model = new ONBBOLevel2Model(message['0']);
40483
+ this.data = model;
40484
+ this.updateWidget(model);
40485
+ }
40312
40486
  } else {
40313
- const model = new ONBBOLevel2Model(message['0']);
40314
- this.data = model;
40315
- this.updateWidget(model);
40487
+ this.showError(`No Level 2 data available for ${this.symbol}`);
40316
40488
  }
40317
- } else {
40318
- this.showError(`No Level 2 data available for ${this.symbol}`);
40319
- }
40320
- }
40489
+ } */
40321
40490
  }
40322
40491
  updateWidget(data) {
40323
40492
  if (this.isDestroyed) return;
@@ -40415,6 +40584,43 @@ ${SharedStyles}
40415
40584
  askBody.appendChild(row);
40416
40585
  });
40417
40586
  }
40587
+ updateLevel1Display() {
40588
+ if (!this.level1Data) return;
40589
+ const formatPrice = price => {
40590
+ return price ? price.toFixed(2) : '--';
40591
+ };
40592
+ const formatVolume = volume => {
40593
+ if (!volume) return '--';
40594
+ return volume.toLocaleString();
40595
+ };
40596
+
40597
+ // Update Last Price
40598
+ const lastPxElement = this.container.querySelector('.l1-last-px');
40599
+ if (lastPxElement) {
40600
+ lastPxElement.textContent = formatPrice(this.level1Data.lastPx);
40601
+ }
40602
+
40603
+ // Update Low Price
40604
+ const lowPxElement = this.container.querySelector('.l1-low-px');
40605
+ if (lowPxElement) {
40606
+ lowPxElement.textContent = formatPrice(this.level1Data.lowPx);
40607
+ }
40608
+
40609
+ // Update High Price
40610
+ const highPxElement = this.container.querySelector('.l1-high-px');
40611
+ if (highPxElement) {
40612
+ highPxElement.textContent = formatPrice(this.level1Data.highPx);
40613
+ }
40614
+
40615
+ // Update Volume
40616
+ const volumeElement = this.container.querySelector('.l1-volume');
40617
+ if (volumeElement) {
40618
+ volumeElement.textContent = formatVolume(this.level1Data.volume);
40619
+ }
40620
+ if (this.debug) {
40621
+ console.log('[ONBBOLevel2Widget] Updated Level 1 display:', this.level1Data);
40622
+ }
40623
+ }
40418
40624
  showLoading() {
40419
40625
  const loadingOverlay = this.container.querySelector('.widget-loading-overlay');
40420
40626
  if (loadingOverlay) {
@@ -40461,9 +40667,17 @@ ${SharedStyles}
40461
40667
  this.clearTimeout(this.loadingTimeout);
40462
40668
  this.loadingTimeout = null;
40463
40669
  }
40464
- if (this.unsubscribe) {
40465
- this.unsubscribe();
40466
- this.unsubscribe = null;
40670
+
40671
+ // Unsubscribe from L2 data
40672
+ if (this.unsubscribeL2) {
40673
+ this.unsubscribeL2();
40674
+ this.unsubscribeL2 = null;
40675
+ }
40676
+
40677
+ // Unsubscribe from L1 data
40678
+ if (this.unsubscribeL1) {
40679
+ this.unsubscribeL1();
40680
+ this.unsubscribeL1 = null;
40467
40681
  }
40468
40682
 
40469
40683
  // Destroy the symbol editor
@@ -41589,6 +41803,7 @@ ${SharedStyles}
41589
41803
  }
41590
41804
  }
41591
41805
  handleMessage(event) {
41806
+ //console.log('EVENT', event)
41592
41807
  try {
41593
41808
  // Update activity tracking - connection is alive!
41594
41809
  this.lastMessageReceived = Date.now();
@@ -41613,22 +41828,20 @@ ${SharedStyles}
41613
41828
  return;
41614
41829
  }
41615
41830
 
41616
- // Determine target type based on error content
41617
- const targetType = this._getTargetTypeFromErrorMessage(textMessage);
41831
+ // Determine type based on error content, default to 'error'
41832
+ const errorType = this._getTargetTypeFromErrorMessage(textMessage) || 'error';
41618
41833
  if (textMessage.toLowerCase().includes('no night session') || textMessage.toLowerCase().includes('no data')) {
41619
41834
  message = {
41620
- type: 'error',
41835
+ type: errorType,
41621
41836
  message: textMessage,
41622
- error: textMessage,
41623
- noData: true,
41624
- targetType: targetType // Will be 'querynightsession' for night session errors
41837
+ error: true,
41838
+ noData: true
41625
41839
  };
41626
41840
  } else {
41627
41841
  message = {
41628
- type: 'error',
41842
+ type: errorType,
41629
41843
  message: textMessage,
41630
- error: textMessage,
41631
- targetType: targetType // Could be null for general errors
41844
+ error: true
41632
41845
  };
41633
41846
  }
41634
41847
  }
@@ -41636,9 +41849,16 @@ ${SharedStyles}
41636
41849
  console.log('[WebSocketManager] Processed message:', message);
41637
41850
  }
41638
41851
 
41639
- // Handle new message format with type and data fields (case-insensitive)
41640
- const normalizedMessage = this._normalizeMessage(message);
41641
- this._routeMessage(normalizedMessage);
41852
+ // Extract data field for night session messages to avoid nested structure
41853
+ // Night session format: { type: 'queryonbbol1', error: false, data: [...] }
41854
+ // We want to send just the data array to widgets
41855
+ let dataToRoute = message;
41856
+ /* if (message.data !== undefined && message.type !== undefined) {
41857
+ // This is night session format - extract the data
41858
+ dataToRoute = message.data;
41859
+ } */
41860
+
41861
+ this._routeMessage(dataToRoute);
41642
41862
  } catch (error) {
41643
41863
  console.error('[WebSocketManager] Error handling message:', error);
41644
41864
  this._notifyWidgets('error', {
@@ -41777,7 +41997,31 @@ ${SharedStyles}
41777
41997
  }
41778
41998
  } else {
41779
41999
  if (this.config.debug) {
41780
- console.log(`[WebSocketManager] Subscription ${subscriptionKey} already active, skipping`);
42000
+ console.log(`[WebSocketManager] Subscription ${subscriptionKey} already active, skipping server subscription`);
42001
+ }
42002
+
42003
+ // Subscription already active, but send cached data to newly subscribing widgets
42004
+ const cachedMessage = this.lastMessageCache.get(subscriptionKey);
42005
+ console.log('LAST', this.lastMessageCache);
42006
+ if (cachedMessage) {
42007
+ if (this.config.debug) {
42008
+ console.log(`[WebSocketManager] Sending cached data to newly subscribing widgets for ${subscriptionKey}`);
42009
+ }
42010
+
42011
+ // Find all widgets subscribed to this type:symbol combination
42012
+ this.subscriptions.forEach((subscription, widgetId) => {
42013
+ if (subscription.types.has(type) && subscription.symbol === symbol) {
42014
+ try {
42015
+ subscription.callback({
42016
+ event: 'data',
42017
+ data: cachedMessage,
42018
+ widgetId
42019
+ });
42020
+ } catch (error) {
42021
+ console.error(`[WebSocketManager] Error sending cached data to widget ${widgetId}:`, error);
42022
+ }
42023
+ }
42024
+ });
41781
42025
  }
41782
42026
  }
41783
42027
  });
@@ -41829,11 +42073,6 @@ ${SharedStyles}
41829
42073
  * The data field structure varies based on the type
41830
42074
  */
41831
42075
  _normalizeMessage(message) {
41832
- // If message already has targetType (error messages), return as is
41833
- if (message.targetType) {
41834
- return message;
41835
- }
41836
-
41837
42076
  // Check for error field (case-insensitive)
41838
42077
  const errorField = message.error || message.Error;
41839
42078
  const typeField = message.type || message.Type;
@@ -41844,13 +42083,12 @@ ${SharedStyles}
41844
42083
  if (this.config.debug) {
41845
42084
  console.log(`[WebSocketManager] Detected error message with type: ${typeField}`);
41846
42085
  }
41847
-
41848
- // Set targetType for routing to specific widget types
41849
42086
  return {
41850
42087
  ...message,
41851
- targetType: typeField,
41852
- type: typeField,
41853
- isError: true
42088
+ // Only add type if message doesn't already have one
42089
+ ...(!message.type && !message.Type && typeField ? {
42090
+ type: typeField
42091
+ } : {})
41854
42092
  };
41855
42093
  }
41856
42094
 
@@ -41868,10 +42106,11 @@ ${SharedStyles}
41868
42106
  // Treat as error and route based on type
41869
42107
  return {
41870
42108
  ...message,
41871
- targetType: typeField,
41872
- type: typeField,
41873
- isError: true,
41874
- error: true // Mark as error even if it was false
42109
+ // Only add type if message doesn't already have one
42110
+ ...(!message.type && !message.Type && typeField ? {
42111
+ type: typeField
42112
+ } : {}),
42113
+ error: true // Mark as error since it was detected as implicit error
41875
42114
  };
41876
42115
  }
41877
42116
  }
@@ -41905,8 +42144,8 @@ ${SharedStyles}
41905
42144
  };
41906
42145
  }
41907
42146
 
41908
- // Also preserve the original type at the root level for backward compatibility
41909
- if (typeof normalizedData === 'object' && !normalizedData.type) {
42147
+ // Only add type field if the normalized data doesn't already have one
42148
+ if (typeof normalizedData === 'object' && !normalizedData.type && !normalizedData.Type) {
41910
42149
  normalizedData.type = typeField;
41911
42150
  }
41912
42151
  return normalizedData;
@@ -41928,32 +42167,7 @@ ${SharedStyles}
41928
42167
  // Cache the message for later use by new subscribers
41929
42168
  this._cacheMessage(message);
41930
42169
 
41931
- // Check if message has a specific target type (like night session errors)
41932
- if (message.targetType) {
41933
- const targetWidgets = this.typeSubscriptions.get(message.targetType);
41934
- if (targetWidgets && targetWidgets.size > 0) {
41935
- if (this.config.debug) {
41936
- console.log(`[WebSocketManager] Routing ${message.targetType} error to specific widgets:`, [...targetWidgets]);
41937
- }
41938
- targetWidgets.forEach(widgetId => {
41939
- const subscription = this.subscriptions.get(widgetId);
41940
- if (subscription) {
41941
- try {
41942
- subscription.callback({
41943
- event: 'data',
41944
- data: message,
41945
- widgetId
41946
- });
41947
- } catch (error) {
41948
- console.error(`[WebSocketManager] Error in widget ${widgetId} callback:`, error);
41949
- }
41950
- }
41951
- });
41952
- return; // Don't fall through to broadcast
41953
- }
41954
- }
41955
-
41956
- //console.log('here');
42170
+ // Get relevant widgets based on message type and symbol
41957
42171
  const relevantWidgets = this._getRelevantWidgets(message);
41958
42172
  if (this.config.debug && relevantWidgets.size > 0) {
41959
42173
  console.log(`[WebSocketManager] Routing message to ${relevantWidgets.size} relevant widgets`);
@@ -41996,42 +42210,49 @@ ${SharedStyles}
41996
42210
  _cacheMessage(message) {
41997
42211
  try {
41998
42212
  // Extract symbol and message type to create cache key
41999
- const symbol = this._extractSymbol(message);
42000
- const messageType = this._extractMessageType(message);
42213
+ // This allows new widgets to get instant data when subscribing
42214
+ // check in message.data or message.Data
42215
+ let data;
42216
+ if (message.data) {
42217
+ data = message.data;
42218
+ } else if (message.Data) {
42219
+ data = message.Data;
42220
+ } else {
42221
+ data = message;
42222
+ }
42223
+ const symbol = this._extractSymbol(data);
42224
+ const messageType = message.type;
42225
+ if (this.config.debug) {
42226
+ console.log(`[WebSocketManager] _cacheMessage - extracted symbol: "${symbol}", messageType: "${messageType}"`);
42227
+ }
42228
+
42229
+ // Cache by type:symbol combination
42001
42230
  if (messageType && symbol) {
42002
42231
  const cacheKey = `${messageType}:${symbol}`;
42003
42232
  this.lastMessageCache.set(cacheKey, message);
42004
42233
  if (this.config.debug) {
42005
42234
  console.log(`[WebSocketManager] Cached message for ${cacheKey}`);
42006
42235
  }
42007
- }
42008
-
42009
- // Also handle array messages (for night session / blueocean data)
42010
- if (Array.isArray(message) && message.length > 0 && message[0].Symbol) {
42011
- const firstItem = message[0];
42012
- const symbol = firstItem.Symbol;
42013
- const messageType = this._extractMessageType(message);
42014
- if (messageType && symbol) {
42015
- const cacheKey = `${messageType}:${symbol}`;
42016
- this.lastMessageCache.set(cacheKey, message);
42236
+ } else {
42237
+ // If we can't extract symbol from message, try to cache by type only
42238
+ // This helps with data like ONBBO L2 which doesn't have symbol in the message
42239
+ if (messageType) {
42017
42240
  if (this.config.debug) {
42018
- console.log(`[WebSocketManager] Cached array message for ${cacheKey}`);
42241
+ console.log(`[WebSocketManager] No symbol found in message, attempting to infer from subscriptions for type: ${messageType}`);
42019
42242
  }
42020
- }
42021
- }
42022
42243
 
42023
- // Handle Data array format (for queryl1 data)
42024
- if (message.Data && Array.isArray(message.Data)) {
42025
- message.Data.forEach(dataItem => {
42026
- if (dataItem.Symbol) {
42027
- const cacheKey = `queryl1:${dataItem.Symbol}`;
42028
- // Cache the whole message, not just the item
42029
- this.lastMessageCache.set(cacheKey, message);
42030
- if (this.config.debug) {
42031
- console.log(`[WebSocketManager] Cached data item for ${cacheKey}`);
42244
+ // Find active subscriptions for this message type and cache for each
42245
+ this.activeSubscriptions.forEach(activeKey => {
42246
+ const [type, sym] = activeKey.split(':');
42247
+ if (type === messageType && sym) {
42248
+ const cacheKey = activeKey;
42249
+ this.lastMessageCache.set(cacheKey, message);
42250
+ if (this.config.debug) {
42251
+ console.log(`[WebSocketManager] Cached message for ${cacheKey} (inferred from active subscription)`);
42252
+ }
42032
42253
  }
42033
- }
42034
- });
42254
+ });
42255
+ }
42035
42256
  }
42036
42257
  } catch (error) {
42037
42258
  console.error('[WebSocketManager] Error caching message:', error);
@@ -42198,7 +42419,7 @@ ${SharedStyles}
42198
42419
  _extractSymbol(item) {
42199
42420
  // Handle new format where symbol might be in nested data structure
42200
42421
  // Check if this is an array with data that has _messageType (normalized new format)
42201
- if (Array.isArray(item) && item._messageType) {
42422
+ if (Array.isArray(item)) {
42202
42423
  // The data is an array, check first item for symbol
42203
42424
  if (item.length > 0 && item[0]) {
42204
42425
  return this._extractSymbolFromItem(item[0]);