@serve.zone/dcrouter 17.2.2 → 17.2.4

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.
@@ -1,3 +1,8 @@
1
+ import {
2
+ NETWORK_ACTIVITY_PROTOCOL_ORDER,
3
+ orderNetworkActivityProtocolNames,
4
+ } from './network-activity-visuals.js';
5
+
1
6
  export interface IChartPoint {
2
7
  x: string | number;
3
8
  y: number;
@@ -14,9 +19,19 @@ export type TProtocolChartSeries = Array<{
14
19
  data: IChartPoint[];
15
20
  }>;
16
21
 
17
- export const PROTOCOL_ORDER = ['HTTP/1.1', 'HTTP/2', 'HTTP/3', 'WebSocket', 'Other'] as const;
22
+ export const PROTOCOL_ORDER = NETWORK_ACTIVITY_PROTOCOL_ORDER;
18
23
  export const PROTOCOL_SERIES_ORDER = ['Total Active', ...PROTOCOL_ORDER] as const;
19
24
 
25
+ const getProtocolSeriesOrder = (
26
+ protocolNamesArg: string[],
27
+ ): string[] => [
28
+ 'Total Active',
29
+ ...orderNetworkActivityProtocolNames([
30
+ ...PROTOCOL_ORDER,
31
+ ...protocolNamesArg,
32
+ ]),
33
+ ];
34
+
20
35
  const toEpochSecond = (timestampArg: string | number): number => {
21
36
  const timestampMs = typeof timestampArg === 'number'
22
37
  ? timestampArg
@@ -59,7 +74,9 @@ export const mergeChartPoints = (
59
74
 
60
75
  export const createProtocolConnectionSeries = (
61
76
  historyArg: IProtocolConnectionHistoryPointLike[] = [],
62
- ): TProtocolChartSeries => PROTOCOL_SERIES_ORDER.map((seriesName) => ({
77
+ ): TProtocolChartSeries => getProtocolSeriesOrder(
78
+ historyArg.flatMap((point) => Object.keys(point.protocols)),
79
+ ).map((seriesName) => ({
63
80
  name: seriesName,
64
81
  data: historyArg.map((point) => ({
65
82
  x: new Date(point.timestamp).toISOString(),
@@ -79,8 +96,12 @@ export const mergeProtocolConnectionHistory = (
79
96
  createProtocolConnectionSeries(canonicalHistoryArg)
80
97
  .map((series) => [series.name, series]),
81
98
  );
99
+ const seriesOrder = getProtocolSeriesOrder([
100
+ ...localSeriesArg.map((series) => series.name),
101
+ ...canonicalHistoryArg.flatMap((point) => Object.keys(point.protocols)),
102
+ ]);
82
103
 
83
- return PROTOCOL_SERIES_ORDER.map((seriesName) => ({
104
+ return seriesOrder.map((seriesName) => ({
84
105
  name: seriesName,
85
106
  data: mergeChartPoints(
86
107
  localSeriesByName.get(seriesName)?.data ?? [],
@@ -92,10 +113,9 @@ export const mergeProtocolConnectionHistory = (
92
113
 
93
114
  export const getProtocolConnectionTotal = (
94
115
  protocolsArg: Record<string, number>,
95
- ): number => (protocolsArg['HTTP/1.1'] ?? 0)
96
- + (protocolsArg['HTTP/2'] ?? 0)
97
- + (protocolsArg['HTTP/3'] ?? 0)
98
- + (protocolsArg.Other ?? 0);
116
+ ): number => Object.entries(protocolsArg)
117
+ .filter(([name]) => name !== 'WebSocket')
118
+ .reduce((total, [, count]) => total + count, 0);
99
119
 
100
120
  export const appendProtocolConnectionSample = (
101
121
  localSeriesArg: TProtocolChartSeries,
@@ -104,7 +124,11 @@ export const appendProtocolConnectionSample = (
104
124
  maximumPointsArg: number,
105
125
  ): TProtocolChartSeries => {
106
126
  const localSeriesByName = new Map(localSeriesArg.map((series) => [series.name, series]));
107
- return PROTOCOL_SERIES_ORDER.map((seriesName) => ({
127
+ const seriesOrder = getProtocolSeriesOrder([
128
+ ...localSeriesArg.map((series) => series.name),
129
+ ...Object.keys(protocolsArg),
130
+ ]);
131
+ return seriesOrder.map((seriesName) => ({
108
132
  name: seriesName,
109
133
  data: mergeChartPoints(
110
134
  localSeriesByName.get(seriesName)?.data ?? [],
@@ -123,4 +147,3 @@ export const shouldRestartNetworkActivityTimer = (
123
147
  isConnectedArg: boolean,
124
148
  documentHiddenArg: boolean,
125
149
  ): boolean => isConnectedArg && !documentHiddenArg;
126
-
@@ -0,0 +1,93 @@
1
+ export const NETWORK_ACTIVITY_COLORS = {
2
+ 'Total Active': '#64748b',
3
+ 'HTTP/1.1': '#1976d2',
4
+ 'HTTP/2': '#388e3c',
5
+ 'HTTP/3': '#7b1fa2',
6
+ 'WebSocket': '#f57c00',
7
+ 'Other': '#757575',
8
+ 'Inbound': '#22c55e',
9
+ 'Outbound': '#8b5cf6',
10
+ 'Requests/sec': '#3b82f6',
11
+ } as const;
12
+
13
+ export const NETWORK_ACTIVITY_PROTOCOL_ORDER = [
14
+ 'HTTP/1.1',
15
+ 'HTTP/2',
16
+ 'HTTP/3',
17
+ 'WebSocket',
18
+ 'Other',
19
+ ] as const;
20
+
21
+ export const PROTOCOL_CONNECTION_LEGEND_STATS: Array<'latest'> = ['latest'];
22
+ export const PROTOCOL_CONNECTION_CHART_LINES: Array<'avg' | 'max'> = [];
23
+
24
+ export const NETWORK_ACTIVITY_FALLBACK_COLORS = [
25
+ '#007aff',
26
+ '#32ade6',
27
+ '#af52de',
28
+ '#ff9500',
29
+ '#34c759',
30
+ '#ff2d55',
31
+ ] as const;
32
+
33
+ const knownProtocolNames = new Set<string>(NETWORK_ACTIVITY_PROTOCOL_ORDER);
34
+
35
+ export const getNetworkActivityColor = (
36
+ nameArg: string,
37
+ fallbackIndexArg = 0,
38
+ ): string => {
39
+ const canonicalColor = NETWORK_ACTIVITY_COLORS[
40
+ nameArg as keyof typeof NETWORK_ACTIVITY_COLORS
41
+ ];
42
+ if (canonicalColor) return canonicalColor;
43
+ const fallbackIndex = Math.max(0, Math.trunc(fallbackIndexArg));
44
+ return NETWORK_ACTIVITY_FALLBACK_COLORS[
45
+ fallbackIndex % NETWORK_ACTIVITY_FALLBACK_COLORS.length
46
+ ];
47
+ };
48
+
49
+ export const orderNetworkActivityProtocolNames = (
50
+ namesArg: string[],
51
+ ): string[] => {
52
+ const names = new Set(namesArg.filter((name) => name && name !== 'Total Active'));
53
+ const orderedKnown = NETWORK_ACTIVITY_PROTOCOL_ORDER
54
+ .filter((name) => name !== 'Other' && names.has(name));
55
+ const orderedUnknown = [...names]
56
+ .filter((name) => !knownProtocolNames.has(name))
57
+ .sort((nameA, nameB) => nameA.localeCompare(nameB));
58
+ return [
59
+ ...orderedKnown,
60
+ ...orderedUnknown,
61
+ ...(names.has('Other') ? ['Other'] : []),
62
+ ];
63
+ };
64
+
65
+ export const createNetworkProtocolDonutData = (
66
+ countsArg: Record<string, number>,
67
+ paletteProtocolNamesArg: string[] = [],
68
+ ): Array<{ name: string; value: number; color: string }> => {
69
+ const orderedNames = orderNetworkActivityProtocolNames(Object.keys(countsArg));
70
+ const paletteNames = orderNetworkActivityProtocolNames([
71
+ ...orderedNames,
72
+ ...paletteProtocolNamesArg,
73
+ ]);
74
+ const unknownNames = paletteNames.filter((name) => !knownProtocolNames.has(name));
75
+ return orderedNames
76
+ .filter((name) => (countsArg[name] ?? 0) > 0)
77
+ .map((name) => ({
78
+ name,
79
+ value: countsArg[name] ?? 0,
80
+ color: getNetworkActivityColor(name, unknownNames.indexOf(name)),
81
+ }));
82
+ };
83
+
84
+ export const colorNetworkProtocolSeries = <T extends { name: string }>(
85
+ seriesArg: T[],
86
+ ): Array<T & { color: string }> => {
87
+ const orderedNames = orderNetworkActivityProtocolNames(seriesArg.map((series) => series.name));
88
+ const unknownNames = orderedNames.filter((name) => !knownProtocolNames.has(name));
89
+ return seriesArg.map((series) => ({
90
+ ...series,
91
+ color: getNetworkActivityColor(series.name, unknownNames.indexOf(series.name)),
92
+ }));
93
+ };
@@ -14,14 +14,14 @@ import {
14
14
  type IChartPoint,
15
15
  type TProtocolChartSeries,
16
16
  } from './network-activity-history.js';
17
-
18
- const PROTOCOL_COLORS: Record<string, string> = {
19
- 'HTTP/1.1': '#1976d2',
20
- 'HTTP/2': '#388e3c',
21
- 'HTTP/3': '#7b1fa2',
22
- 'WebSocket': '#f57c00',
23
- 'Other': '#757575',
24
- };
17
+ import {
18
+ NETWORK_ACTIVITY_COLORS,
19
+ PROTOCOL_CONNECTION_CHART_LINES,
20
+ PROTOCOL_CONNECTION_LEGEND_STATS,
21
+ colorNetworkProtocolSeries,
22
+ createNetworkProtocolDonutData,
23
+ getNetworkActivityColor,
24
+ } from './network-activity-visuals.js';
25
25
 
26
26
  declare global {
27
27
  interface HTMLElementTagNameMap {
@@ -181,13 +181,6 @@ export class OpsViewNetworkActivity extends DeesElement {
181
181
  };
182
182
  }
183
183
 
184
- private getProtocolSeriesWithColors(seriesArg: TProtocolChartSeries) {
185
- return seriesArg.map((series) => ({
186
- ...series,
187
- color: series.name === 'Total Active' ? '#ef4444' : (PROTOCOL_COLORS[series.name] ?? '#757575'),
188
- }));
189
- }
190
-
191
184
  private mergeProtocolConnectionHistories(): void {
192
185
  const frontendHistory = this.networkState.frontendConnectionHistory || [];
193
186
  const latestFrontendEpochSecond = getLatestHistoryEpochSecond(frontendHistory);
@@ -389,12 +382,12 @@ export class OpsViewNetworkActivity extends DeesElement {
389
382
  {
390
383
  name: 'Inbound',
391
384
  data: this.trafficDataIn,
392
- color: '#22c55e',
385
+ color: NETWORK_ACTIVITY_COLORS.Inbound,
393
386
  },
394
387
  {
395
388
  name: 'Outbound',
396
389
  data: this.trafficDataOut,
397
- color: '#8b5cf6',
390
+ color: NETWORK_ACTIVITY_COLORS.Outbound,
398
391
  }
399
392
  ]}
400
393
  .realtimeMode=${true}
@@ -602,7 +595,7 @@ export class OpsViewNetworkActivity extends DeesElement {
602
595
  value: activeConnections,
603
596
  type: 'number',
604
597
  icon: 'lucide:Plug',
605
- color: activeConnections > 100 ? '#f59e0b' : '#22c55e',
598
+ color: NETWORK_ACTIVITY_COLORS['Total Active'],
606
599
  description: `Total: ${this.formatNumber(this.statsState.serverStats?.totalConnections || 0)} connections`,
607
600
  actions: [
608
601
  {
@@ -619,7 +612,7 @@ export class OpsViewNetworkActivity extends DeesElement {
619
612
  value: reqPerSec,
620
613
  type: 'trend',
621
614
  icon: 'lucide:ChartLine',
622
- color: '#3b82f6',
615
+ color: NETWORK_ACTIVITY_COLORS['Requests/sec'],
623
616
  trendData: trendData,
624
617
  description: `Total: ${this.formatNumber(this.networkState.requestsTotal || 0)} requests`,
625
618
  },
@@ -630,7 +623,7 @@ export class OpsViewNetworkActivity extends DeesElement {
630
623
  unit: '',
631
624
  type: 'number',
632
625
  icon: 'lucide:Download',
633
- color: '#22c55e',
626
+ color: NETWORK_ACTIVITY_COLORS.Inbound,
634
627
  description: `Total: ${this.formatBytes(this.networkState.totalBytes?.in || 0)}`,
635
628
  },
636
629
  {
@@ -640,7 +633,7 @@ export class OpsViewNetworkActivity extends DeesElement {
640
633
  unit: '',
641
634
  type: 'number',
642
635
  icon: 'lucide:Upload',
643
- color: '#8b5cf6',
636
+ color: NETWORK_ACTIVITY_COLORS.Outbound,
644
637
  description: `Total: ${this.formatBytes(this.networkState.totalBytes?.out || 0)}`,
645
638
  },
646
639
  ];
@@ -657,28 +650,25 @@ export class OpsViewNetworkActivity extends DeesElement {
657
650
  const fp = this.networkState.frontendProtocols;
658
651
  const bp = this.networkState.backendProtocols;
659
652
 
660
- const protoColors = PROTOCOL_COLORS;
661
-
662
- const buildDonutData = (dist: interfaces.data.IProtocolDistribution | null) => {
663
- if (!dist) return [];
664
- const items: Array<{ name: string; value: number; color: string }> = [];
665
- if (dist.h1Active > 0) items.push({ name: 'HTTP/1.1', value: dist.h1Active, color: protoColors['HTTP/1.1'] });
666
- if (dist.h2Active > 0) items.push({ name: 'HTTP/2', value: dist.h2Active, color: protoColors['HTTP/2'] });
667
- if (dist.h3Active > 0) items.push({ name: 'HTTP/3', value: dist.h3Active, color: protoColors['HTTP/3'] });
668
- if (dist.wsActive > 0) items.push({ name: 'WebSocket', value: dist.wsActive, color: protoColors['WebSocket'] });
669
- if (dist.otherActive > 0) items.push({ name: 'Other', value: dist.otherActive, color: protoColors['Other'] });
670
- return items;
671
- };
653
+ const buildDonutData = (
654
+ dist: interfaces.data.IProtocolDistribution | null,
655
+ historySeries: TProtocolChartSeries,
656
+ ) => dist
657
+ ? createNetworkProtocolDonutData(
658
+ this.getProtocolCounts(dist),
659
+ historySeries.map((series) => series.name),
660
+ )
661
+ : [];
672
662
 
673
- const frontendData = buildDonutData(fp);
674
- const backendData = buildDonutData(bp);
663
+ const frontendData = buildDonutData(fp, this.frontendConnectionSeries);
664
+ const backendData = buildDonutData(bp, this.backendConnectionSeries);
675
665
 
676
666
  return html`
677
667
  <div class="protocolChartGrid">
678
668
  <div class="protocolChartColumn">
679
669
  <dees-chart-donut
680
670
  .label=${'Frontend Protocols'}
681
- .data=${frontendData.length > 0 ? frontendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
671
+ .data=${frontendData.length > 0 ? frontendData : [{ name: 'No Traffic', value: 1, color: getNetworkActivityColor('Other') }]}
682
672
  .showLegend=${true}
683
673
  .showLabels=${true}
684
674
  .innerRadiusPercent=${'55%'}
@@ -686,7 +676,9 @@ export class OpsViewNetworkActivity extends DeesElement {
686
676
  ></dees-chart-donut>
687
677
  <dees-chart-area
688
678
  .label=${'Frontend Active Connections'}
689
- .series=${this.getProtocolSeriesWithColors(this.frontendConnectionSeries)}
679
+ .series=${colorNetworkProtocolSeries(this.frontendConnectionSeries)}
680
+ .legendStats=${PROTOCOL_CONNECTION_LEGEND_STATS}
681
+ .chartLines=${PROTOCOL_CONNECTION_CHART_LINES}
690
682
  .realtimeMode=${true}
691
683
  .rollingWindow=${OpsViewNetworkActivity.CHART_WINDOW_MS}
692
684
  .yAxisFormatter=${(val: number) => `${Math.round(val)} active`}
@@ -695,7 +687,7 @@ export class OpsViewNetworkActivity extends DeesElement {
695
687
  <div class="protocolChartColumn">
696
688
  <dees-chart-donut
697
689
  .label=${'Backend Protocols'}
698
- .data=${backendData.length > 0 ? backendData : [{ name: 'No Traffic', value: 1, color: '#757575' }]}
690
+ .data=${backendData.length > 0 ? backendData : [{ name: 'No Traffic', value: 1, color: getNetworkActivityColor('Other') }]}
699
691
  .showLegend=${true}
700
692
  .showLabels=${true}
701
693
  .innerRadiusPercent=${'55%'}
@@ -703,7 +695,9 @@ export class OpsViewNetworkActivity extends DeesElement {
703
695
  ></dees-chart-donut>
704
696
  <dees-chart-area
705
697
  .label=${'Backend Active Connections'}
706
- .series=${this.getProtocolSeriesWithColors(this.backendConnectionSeries)}
698
+ .series=${colorNetworkProtocolSeries(this.backendConnectionSeries)}
699
+ .legendStats=${PROTOCOL_CONNECTION_LEGEND_STATS}
700
+ .chartLines=${PROTOCOL_CONNECTION_CHART_LINES}
707
701
  .realtimeMode=${true}
708
702
  .rollingWindow=${OpsViewNetworkActivity.CHART_WINDOW_MS}
709
703
  .yAxisFormatter=${(val: number) => `${Math.round(val)} active`}