@benchkit/adapters 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -0
- package/dist/chartjs.d.ts +77 -0
- package/dist/chartjs.d.ts.map +1 -0
- package/dist/chartjs.js +88 -0
- package/dist/chartjs.js.map +1 -0
- package/dist/coordinate-transforms.d.ts +12 -0
- package/dist/coordinate-transforms.d.ts.map +1 -0
- package/dist/coordinate-transforms.js +80 -0
- package/dist/coordinate-transforms.js.map +1 -0
- package/dist/coordinate-transforms.test.d.ts +2 -0
- package/dist/coordinate-transforms.test.d.ts.map +1 -0
- package/dist/coordinate-transforms.test.js +59 -0
- package/dist/coordinate-transforms.test.js.map +1 -0
- package/dist/echarts.d.ts +20 -0
- package/dist/echarts.d.ts.map +1 -0
- package/dist/echarts.js +82 -0
- package/dist/echarts.js.map +1 -0
- package/dist/echarts.test.d.ts +2 -0
- package/dist/echarts.test.d.ts.map +1 -0
- package/dist/echarts.test.js +44 -0
- package/dist/echarts.test.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/recharts.d.ts +30 -0
- package/dist/recharts.d.ts.map +1 -0
- package/dist/recharts.js +33 -0
- package/dist/recharts.js.map +1 -0
- package/dist/recharts.test.d.ts +2 -0
- package/dist/recharts.test.d.ts.map +1 -0
- package/dist/recharts.test.js +46 -0
- package/dist/recharts.test.js.map +1 -0
- package/dist/regression.d.ts +45 -0
- package/dist/regression.d.ts.map +1 -0
- package/dist/regression.js +64 -0
- package/dist/regression.js.map +1 -0
- package/dist/shared-contract.d.ts +31 -0
- package/dist/shared-contract.d.ts.map +1 -0
- package/dist/shared-contract.js +29 -0
- package/dist/shared-contract.js.map +1 -0
- package/dist/shared-contract.test.d.ts +2 -0
- package/dist/shared-contract.test.d.ts.map +1 -0
- package/dist/shared-contract.test.js +23 -0
- package/dist/shared-contract.test.js.map +1 -0
- package/dist/transforms.d.ts +30 -0
- package/dist/transforms.d.ts.map +1 -0
- package/dist/transforms.js +64 -0
- package/dist/transforms.js.map +1 -0
- package/dist/visx.d.ts +35 -0
- package/dist/visx.d.ts.map +1 -0
- package/dist/visx.js +45 -0
- package/dist/visx.js.map +1 -0
- package/dist/visx.test.d.ts +2 -0
- package/dist/visx.test.d.ts.map +1 -0
- package/dist/visx.test.js +39 -0
- package/dist/visx.test.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# @benchkit/adapters
|
|
2
|
+
|
|
3
|
+
Data transforms for converting benchkit metric data into chart-library-friendly shapes.
|
|
4
|
+
|
|
5
|
+
`@benchkit/adapters` keeps metric shaping logic separate from rendering. You can use:
|
|
6
|
+
|
|
7
|
+
- Shared contract and coordinate helpers
|
|
8
|
+
- Chart.js transforms
|
|
9
|
+
- Recharts transforms
|
|
10
|
+
- ECharts option builders
|
|
11
|
+
- Visx helpers
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @benchkit/adapters
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Install your charting library separately as needed (`chart.js`, `recharts`, `echarts`, `@visx/*`).
|
|
20
|
+
|
|
21
|
+
## Shared foundation
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
normalizeMaxPoints,
|
|
26
|
+
validateTagFilters,
|
|
27
|
+
seriesEntryToCoordinates,
|
|
28
|
+
alignComparisonCoordinates,
|
|
29
|
+
getLatestValueRows,
|
|
30
|
+
} from '@benchkit/adapters';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Core exports:
|
|
34
|
+
|
|
35
|
+
- `@benchkit/adapters/shared-contract`
|
|
36
|
+
- `@benchkit/adapters/coordinate-transforms`
|
|
37
|
+
|
|
38
|
+
## Chart.js
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { trendChartDataset } from '@benchkit/adapters/chartjs';
|
|
42
|
+
|
|
43
|
+
const result = trendChartDataset('latency_ms', entry, {
|
|
44
|
+
threshold: 10,
|
|
45
|
+
window: 5,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
console.log(result.labels, result.dataset);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Recharts
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import {
|
|
55
|
+
trendLineData,
|
|
56
|
+
comparisonLineData,
|
|
57
|
+
comparisonBarData,
|
|
58
|
+
} from '@benchkit/adapters/recharts';
|
|
59
|
+
|
|
60
|
+
const trend = trendLineData(entry, { maxPoints: 100 });
|
|
61
|
+
const comparison = comparisonLineData(baselinePoints, currentPoints);
|
|
62
|
+
const bars = comparisonBarData(seriesFile);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## ECharts
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import {
|
|
69
|
+
trendLineOption,
|
|
70
|
+
comparisonLineOption,
|
|
71
|
+
comparisonBarOption,
|
|
72
|
+
} from '@benchkit/adapters/echarts';
|
|
73
|
+
|
|
74
|
+
const trendOption = trendLineOption(entry, {
|
|
75
|
+
metricName: 'latency_ms',
|
|
76
|
+
title: 'Latency trend',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const compareOption = comparisonLineOption(baselinePoints, currentPoints);
|
|
80
|
+
const barOption = comparisonBarOption(seriesFile);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Visx
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import {
|
|
87
|
+
trendLineSeries,
|
|
88
|
+
comparisonLineSeries,
|
|
89
|
+
comparisonBarSeries,
|
|
90
|
+
} from '@benchkit/adapters/visx';
|
|
91
|
+
|
|
92
|
+
const trend = trendLineSeries(entry);
|
|
93
|
+
const compare = comparisonLineSeries(baselinePoints, currentPoints);
|
|
94
|
+
const bars = comparisonBarSeries(seriesFile);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Supported modules
|
|
98
|
+
|
|
99
|
+
| Library | Module | Status |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| Shared contract | `@benchkit/adapters/shared-contract` | ✅ Stable |
|
|
102
|
+
| Coordinate transforms | `@benchkit/adapters/coordinate-transforms` | ✅ Stable |
|
|
103
|
+
| Chart.js | `@benchkit/adapters/chartjs` | ✅ Stable |
|
|
104
|
+
| Recharts | `@benchkit/adapters/recharts` | ✅ Stable |
|
|
105
|
+
| ECharts | `@benchkit/adapters/echarts` | ✅ Stable |
|
|
106
|
+
| Visx | `@benchkit/adapters/visx` | ✅ Stable |
|
|
107
|
+
|
|
108
|
+
## Adapter intents
|
|
109
|
+
|
|
110
|
+
Each library adapter targets the same three intents:
|
|
111
|
+
|
|
112
|
+
1. Trend
|
|
113
|
+
2. Comparison line
|
|
114
|
+
3. Comparison bar
|
|
115
|
+
|
|
116
|
+
This keeps cross-library migration low-friction.
|
|
117
|
+
|
|
118
|
+
## Testing
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npm run build --workspace=packages/adapters
|
|
122
|
+
npm run test --workspace=packages/adapters
|
|
123
|
+
npm run lint --workspace=packages/adapters
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Contributing
|
|
127
|
+
|
|
128
|
+
When adding a new adapter module:
|
|
129
|
+
|
|
130
|
+
1. Reuse shared contract and coordinate helpers first
|
|
131
|
+
2. Export the module in `packages/adapters/package.json`
|
|
132
|
+
3. Add basic-usecase tests (trend, comparison line, comparison bar)
|
|
133
|
+
4. Update this README with a copy-paste usage snippet
|
|
134
|
+
|
|
135
|
+
Keep transforms pure and deterministic; avoid embedding rendering-layer concerns in adapter logic.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SeriesEntry } from '@benchkit/format';
|
|
2
|
+
import { RegressionOptions } from './regression';
|
|
3
|
+
/**
|
|
4
|
+
* Chart.js trend chart options.
|
|
5
|
+
*/
|
|
6
|
+
export interface TrendChartOptions extends RegressionOptions {
|
|
7
|
+
metricName?: string;
|
|
8
|
+
color?: string;
|
|
9
|
+
regressionColor?: string;
|
|
10
|
+
improvementColor?: string;
|
|
11
|
+
pointRadius?: number;
|
|
12
|
+
borderWidth?: number;
|
|
13
|
+
fill?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Chart.js dataset format.
|
|
17
|
+
*/
|
|
18
|
+
export interface ChartJsDataset {
|
|
19
|
+
label: string;
|
|
20
|
+
data: Array<{
|
|
21
|
+
x: string;
|
|
22
|
+
y: number | null;
|
|
23
|
+
}>;
|
|
24
|
+
borderColor?: string | string[];
|
|
25
|
+
backgroundColor?: string | string[];
|
|
26
|
+
borderWidth?: number;
|
|
27
|
+
pointRadius?: number;
|
|
28
|
+
pointBackgroundColor?: string | string[];
|
|
29
|
+
pointBorderColor?: string | string[];
|
|
30
|
+
fill?: boolean;
|
|
31
|
+
tension?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Transform a benchkit series entry into a Chart.js trend chart dataset.
|
|
35
|
+
*
|
|
36
|
+
* Includes regression highlighting via point colors.
|
|
37
|
+
*/
|
|
38
|
+
export declare function trendChartDataset(metricName: string, entry: SeriesEntry, options?: TrendChartOptions): {
|
|
39
|
+
labels: string[];
|
|
40
|
+
dataset: ChartJsDataset;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Transform two runs into a Chart.js dual-trace comparison dataset.
|
|
44
|
+
*
|
|
45
|
+
* Useful for before/after comparisons (e.g., baseline vs current PR).
|
|
46
|
+
*/
|
|
47
|
+
export interface ComparisonChartOptions {
|
|
48
|
+
baselineLabel?: string;
|
|
49
|
+
currentLabel?: string;
|
|
50
|
+
baselineColor?: string;
|
|
51
|
+
currentColor?: string;
|
|
52
|
+
pointRadius?: number;
|
|
53
|
+
borderWidth?: number;
|
|
54
|
+
}
|
|
55
|
+
export declare function comparisonChartDataset(baselineData: Array<{
|
|
56
|
+
x: string;
|
|
57
|
+
y: number;
|
|
58
|
+
}>, currentData: Array<{
|
|
59
|
+
x: string;
|
|
60
|
+
y: number;
|
|
61
|
+
}>, options?: ComparisonChartOptions): {
|
|
62
|
+
labels: string[];
|
|
63
|
+
datasets: ChartJsDataset[];
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Transform latest metric values into a Chart.js bar chart dataset.
|
|
67
|
+
*
|
|
68
|
+
* Useful for leaderboard-style comparisons.
|
|
69
|
+
*/
|
|
70
|
+
export interface ComparisonBarOptions {
|
|
71
|
+
color?: string;
|
|
72
|
+
}
|
|
73
|
+
export declare function comparisonBarDataset(labels: string[], values: number[], options?: ComparisonBarOptions): {
|
|
74
|
+
labels: string[];
|
|
75
|
+
dataset: ChartJsDataset;
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=chartjs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chartjs.d.ts","sourceRoot":"","sources":["../src/chartjs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAqB,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAC7C,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACrC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE,iBAAsB,GAC9B;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,CA+C/C;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,KAAK,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAC7C,WAAW,EAAE,KAAK,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5C,OAAO,GAAE,sBAA2B,GACnC;IACD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B,CAwCA;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAEhB;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,GAAE,oBAAyB,GACjC;IACD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,cAAc,CAAC;CACzB,CAiBA"}
|
package/dist/chartjs.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { detectRegressions } from './regression';
|
|
2
|
+
/**
|
|
3
|
+
* Transform a benchkit series entry into a Chart.js trend chart dataset.
|
|
4
|
+
*
|
|
5
|
+
* Includes regression highlighting via point colors.
|
|
6
|
+
*/
|
|
7
|
+
export function trendChartDataset(metricName, entry, options = {}) {
|
|
8
|
+
const { color = '#3b82f6', regressionColor = '#ef4444', improvementColor = '#10b981', pointRadius = 4, borderWidth = 2, fill = false, threshold = 10, window = 5, direction = 'smaller_is_better', } = options;
|
|
9
|
+
const pointsWithRegressions = detectRegressions(entry, {
|
|
10
|
+
threshold,
|
|
11
|
+
window,
|
|
12
|
+
direction,
|
|
13
|
+
});
|
|
14
|
+
const labels = pointsWithRegressions.map((p) => new Date(p.timestamp).toISOString().split('T')[0]);
|
|
15
|
+
const data = pointsWithRegressions.map((p) => ({
|
|
16
|
+
x: p.timestamp,
|
|
17
|
+
y: p.value ?? null,
|
|
18
|
+
}));
|
|
19
|
+
const pointColors = pointsWithRegressions.map((p) => {
|
|
20
|
+
if (!p.regression)
|
|
21
|
+
return color;
|
|
22
|
+
return p.regression.isBigger ? regressionColor : improvementColor;
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
labels,
|
|
26
|
+
dataset: {
|
|
27
|
+
label: metricName,
|
|
28
|
+
data,
|
|
29
|
+
borderColor: color,
|
|
30
|
+
pointBackgroundColor: pointColors,
|
|
31
|
+
pointBorderColor: pointColors,
|
|
32
|
+
borderWidth,
|
|
33
|
+
pointRadius,
|
|
34
|
+
fill,
|
|
35
|
+
tension: 0.3,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function comparisonChartDataset(baselineData, currentData, options = {}) {
|
|
40
|
+
const { baselineLabel = 'Baseline', currentLabel = 'Current', baselineColor = '#9ca3af', currentColor = '#3b82f6', pointRadius = 3, borderWidth = 2, } = options;
|
|
41
|
+
const allLabels = new Set();
|
|
42
|
+
baselineData.forEach((p) => allLabels.add(p.x));
|
|
43
|
+
currentData.forEach((p) => allLabels.add(p.x));
|
|
44
|
+
const labels = Array.from(allLabels);
|
|
45
|
+
return {
|
|
46
|
+
labels,
|
|
47
|
+
datasets: [
|
|
48
|
+
{
|
|
49
|
+
label: baselineLabel,
|
|
50
|
+
data: baselineData.map((p) => ({ x: p.x, y: p.y })),
|
|
51
|
+
borderColor: [baselineColor],
|
|
52
|
+
pointBackgroundColor: [baselineColor],
|
|
53
|
+
borderWidth,
|
|
54
|
+
pointRadius,
|
|
55
|
+
tension: 0.3,
|
|
56
|
+
fill: false,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: currentLabel,
|
|
60
|
+
data: currentData.map((p) => ({ x: p.x, y: p.y })),
|
|
61
|
+
borderColor: [currentColor],
|
|
62
|
+
pointBackgroundColor: [currentColor],
|
|
63
|
+
borderWidth,
|
|
64
|
+
pointRadius,
|
|
65
|
+
tension: 0.3,
|
|
66
|
+
fill: false,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export function comparisonBarDataset(labels, values, options = {}) {
|
|
72
|
+
const { color = '#3b82f6' } = options;
|
|
73
|
+
return {
|
|
74
|
+
labels,
|
|
75
|
+
dataset: {
|
|
76
|
+
label: 'Value',
|
|
77
|
+
data: values.map((v, i) => ({
|
|
78
|
+
x: labels[i],
|
|
79
|
+
y: v,
|
|
80
|
+
})),
|
|
81
|
+
borderColor: color,
|
|
82
|
+
backgroundColor: `${color}33`,
|
|
83
|
+
borderWidth: 1,
|
|
84
|
+
fill: true,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=chartjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chartjs.js","sourceRoot":"","sources":["../src/chartjs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SeriesEntry, SeriesFile } from '@benchkit/format';
|
|
2
|
+
import { AdapterTagFilters, ComparisonCoordinatePoint, CoordinatePoint, LatestValueRow } from './shared-contract.js';
|
|
3
|
+
export declare function seriesEntryToCoordinates(entry: SeriesEntry, options?: {
|
|
4
|
+
maxPoints?: number;
|
|
5
|
+
tags?: AdapterTagFilters;
|
|
6
|
+
}): CoordinatePoint[];
|
|
7
|
+
export declare function alignComparisonCoordinates(baseline: CoordinatePoint[], current: CoordinatePoint[]): ComparisonCoordinatePoint[];
|
|
8
|
+
export declare function getLatestValueRows(series: SeriesFile, options?: {
|
|
9
|
+
maxPoints?: number;
|
|
10
|
+
tags?: AdapterTagFilters;
|
|
11
|
+
}): LatestValueRow[];
|
|
12
|
+
//# sourceMappingURL=coordinate-transforms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinate-transforms.d.ts","sourceRoot":"","sources":["../src/coordinate-transforms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,eAAe,EACf,cAAc,EAGf,MAAM,sBAAsB,CAAC;AAc9B,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,iBAAiB,CAAA;CAAO,GAC7D,eAAe,EAAE,CA8BnB;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,eAAe,EAAE,EAC3B,OAAO,EAAE,eAAe,EAAE,GACzB,yBAAyB,EAAE,CAyB7B;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,iBAAiB,CAAA;CAAO,GAC7D,cAAc,EAAE,CA8BlB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { normalizeMaxPoints, validateTagFilters, } from './shared-contract.js';
|
|
2
|
+
function hasMatchingTags(entryTags, expectedTags) {
|
|
3
|
+
if (!expectedTags || !Object.keys(expectedTags).length) {
|
|
4
|
+
return true;
|
|
5
|
+
}
|
|
6
|
+
const tags = entryTags ?? {};
|
|
7
|
+
return Object.entries(expectedTags).every(([key, value]) => tags[key] === value);
|
|
8
|
+
}
|
|
9
|
+
export function seriesEntryToCoordinates(entry, options = {}) {
|
|
10
|
+
const tags = validateTagFilters(options.tags);
|
|
11
|
+
if (!hasMatchingTags(entry.tags, tags)) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const coordinates = entry.points
|
|
15
|
+
.filter((point) => Number.isFinite(point.value))
|
|
16
|
+
.map((point) => ({
|
|
17
|
+
x: point.timestamp,
|
|
18
|
+
y: point.value,
|
|
19
|
+
tags: entry.tags,
|
|
20
|
+
}))
|
|
21
|
+
.sort((left, right) => left.x.localeCompare(right.x));
|
|
22
|
+
const maxPoints = normalizeMaxPoints(options.maxPoints);
|
|
23
|
+
if (coordinates.length <= maxPoints) {
|
|
24
|
+
return coordinates;
|
|
25
|
+
}
|
|
26
|
+
const step = coordinates.length / maxPoints;
|
|
27
|
+
const sampled = [];
|
|
28
|
+
for (let index = 0; index < maxPoints; index += 1) {
|
|
29
|
+
sampled.push(coordinates[Math.floor(index * step)]);
|
|
30
|
+
}
|
|
31
|
+
const lastPoint = coordinates[coordinates.length - 1];
|
|
32
|
+
sampled[sampled.length - 1] = lastPoint;
|
|
33
|
+
return sampled;
|
|
34
|
+
}
|
|
35
|
+
export function alignComparisonCoordinates(baseline, current) {
|
|
36
|
+
const merged = new Map();
|
|
37
|
+
for (const point of baseline) {
|
|
38
|
+
merged.set(point.x, {
|
|
39
|
+
x: point.x,
|
|
40
|
+
baseline: point.y,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
for (const point of current) {
|
|
44
|
+
const existing = merged.get(point.x);
|
|
45
|
+
const next = {
|
|
46
|
+
x: point.x,
|
|
47
|
+
current: point.y,
|
|
48
|
+
};
|
|
49
|
+
if (existing?.baseline !== undefined) {
|
|
50
|
+
next.baseline = existing.baseline;
|
|
51
|
+
}
|
|
52
|
+
merged.set(point.x, next);
|
|
53
|
+
}
|
|
54
|
+
return Array.from(merged.values()).sort((left, right) => left.x.localeCompare(right.x));
|
|
55
|
+
}
|
|
56
|
+
export function getLatestValueRows(series, options = {}) {
|
|
57
|
+
const tags = validateTagFilters(options.tags);
|
|
58
|
+
const rows = [];
|
|
59
|
+
for (const [label, entry] of Object.entries(series.series)) {
|
|
60
|
+
if (!hasMatchingTags(entry.tags, tags)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const lastPoint = [...entry.points]
|
|
64
|
+
.sort((left, right) => left.timestamp.localeCompare(right.timestamp))
|
|
65
|
+
.pop();
|
|
66
|
+
if (!lastPoint || !Number.isFinite(lastPoint.value)) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
rows.push({
|
|
70
|
+
label,
|
|
71
|
+
value: lastPoint.value,
|
|
72
|
+
tags: entry.tags,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
rows
|
|
76
|
+
.sort((left, right) => right.value - left.value || left.label.localeCompare(right.label));
|
|
77
|
+
const maxPoints = normalizeMaxPoints(options.maxPoints);
|
|
78
|
+
return rows.slice(0, maxPoints);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=coordinate-transforms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinate-transforms.js","sourceRoot":"","sources":["../src/coordinate-transforms.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAE9B,SAAS,eAAe,CACtB,SAAwC,EACxC,YAA2C;IAE3C,IAAI,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,IAAI,EAAE,CAAC;IAC7B,OAAO,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,KAAkB,EAClB,UAA4D,EAAE;IAE9D,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;QACvC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM;SAC7B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SAC/C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACf,CAAC,EAAE,KAAK,CAAC,SAAS;QAClB,CAAC,EAAE,KAAK,CAAC,KAAK;QACd,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAExD,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,IAAI,WAAW,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;QACpC,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,GAAG,SAAS,CAAC;IAC5C,MAAM,OAAO,GAAsB,EAAE,CAAC;IAEtC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,SAAS,GAAG,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC;IACxC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,QAA2B,EAC3B,OAA0B;IAE1B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAqC,CAAC;IAE5D,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE;YAClB,CAAC,EAAE,KAAK,CAAC,CAAC;YACV,QAAQ,EAAE,KAAK,CAAC,CAAC;SAClB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,IAAI,GAA8B;YACtC,CAAC,EAAE,KAAK,CAAC,CAAC;YACV,OAAO,EAAE,KAAK,CAAC,CAAC;SACjB,CAAC;QAEF,IAAI,QAAQ,EAAE,QAAQ,KAAK,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACpC,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,MAAkB,EAClB,UAA4D,EAAE;IAE9D,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C,MAAM,IAAI,GAAqB,EAAE,CAAC;IAElC,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3D,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACvC,SAAS;QACX,CAAC;QAED,MAAM,SAAS,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;aAChC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;aACpE,GAAG,EAAE,CAAC;QAET,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YACpD,SAAS;QACX,CAAC;QAED,IAAI,CAAC,IAAI,CAAC;YACR,KAAK;YACL,KAAK,EAAE,SAAS,CAAC,KAAK;YACtB,IAAI,EAAE,KAAK,CAAC,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IAED,IAAI;SACD,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAE5F,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinate-transforms.test.d.ts","sourceRoot":"","sources":["../src/coordinate-transforms.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { alignComparisonCoordinates, getLatestValueRows, seriesEntryToCoordinates, } from './coordinate-transforms.js';
|
|
4
|
+
const entry = {
|
|
5
|
+
tags: { branch: 'main', os: 'linux' },
|
|
6
|
+
points: [
|
|
7
|
+
{ timestamp: '2026-01-03T00:00:00.000Z', value: 3 },
|
|
8
|
+
{ timestamp: '2026-01-01T00:00:00.000Z', value: 1 },
|
|
9
|
+
{ timestamp: '2026-01-02T00:00:00.000Z', value: 2 },
|
|
10
|
+
],
|
|
11
|
+
};
|
|
12
|
+
test('seriesEntryToCoordinates sorts by timestamp', () => {
|
|
13
|
+
assert.deepEqual(seriesEntryToCoordinates(entry), [
|
|
14
|
+
{ x: '2026-01-01T00:00:00.000Z', y: 1, tags: { branch: 'main', os: 'linux' } },
|
|
15
|
+
{ x: '2026-01-02T00:00:00.000Z', y: 2, tags: { branch: 'main', os: 'linux' } },
|
|
16
|
+
{ x: '2026-01-03T00:00:00.000Z', y: 3, tags: { branch: 'main', os: 'linux' } },
|
|
17
|
+
]);
|
|
18
|
+
});
|
|
19
|
+
test('seriesEntryToCoordinates honors tag filters', () => {
|
|
20
|
+
assert.deepEqual(seriesEntryToCoordinates(entry, { tags: { branch: 'other' } }), []);
|
|
21
|
+
});
|
|
22
|
+
test('alignComparisonCoordinates merges sparse series by x', () => {
|
|
23
|
+
assert.deepEqual(alignComparisonCoordinates([
|
|
24
|
+
{ x: '2026-01-01T00:00:00.000Z', y: 1 },
|
|
25
|
+
{ x: '2026-01-03T00:00:00.000Z', y: 3 },
|
|
26
|
+
], [
|
|
27
|
+
{ x: '2026-01-02T00:00:00.000Z', y: 2 },
|
|
28
|
+
{ x: '2026-01-03T00:00:00.000Z', y: 4 },
|
|
29
|
+
]), [
|
|
30
|
+
{ x: '2026-01-01T00:00:00.000Z', baseline: 1 },
|
|
31
|
+
{ x: '2026-01-02T00:00:00.000Z', current: 2 },
|
|
32
|
+
{ x: '2026-01-03T00:00:00.000Z', baseline: 3, current: 4 },
|
|
33
|
+
]);
|
|
34
|
+
});
|
|
35
|
+
test('getLatestValueRows extracts latest values and sorts descending', () => {
|
|
36
|
+
const series = {
|
|
37
|
+
metric: 'latency_ms',
|
|
38
|
+
series: {
|
|
39
|
+
fast: {
|
|
40
|
+
points: [
|
|
41
|
+
{ timestamp: '2026-01-01T00:00:00.000Z', value: 1 },
|
|
42
|
+
{ timestamp: '2026-01-02T00:00:00.000Z', value: 2 },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
slow: {
|
|
46
|
+
tags: { branch: 'main' },
|
|
47
|
+
points: [
|
|
48
|
+
{ timestamp: '2026-01-01T00:00:00.000Z', value: 4 },
|
|
49
|
+
{ timestamp: '2026-01-02T00:00:00.000Z', value: 6 },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
assert.deepEqual(getLatestValueRows(series), [
|
|
55
|
+
{ label: 'slow', value: 6, tags: { branch: 'main' } },
|
|
56
|
+
{ label: 'fast', value: 2, tags: undefined },
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=coordinate-transforms.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinate-transforms.test.js","sourceRoot":"","sources":["../src/coordinate-transforms.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SeriesEntry, SeriesFile } from '@benchkit/format';
|
|
2
|
+
import { AdapterBaseOptions } from './shared-contract.js';
|
|
3
|
+
export type EchartsOption = Record<string, unknown>;
|
|
4
|
+
export interface EchartsBaseOptions extends AdapterBaseOptions {
|
|
5
|
+
title?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface EchartsComparisonOptions extends EchartsBaseOptions {
|
|
8
|
+
baselineLabel?: string;
|
|
9
|
+
currentLabel?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function trendLineOption(entry: SeriesEntry, options?: EchartsBaseOptions): EchartsOption;
|
|
12
|
+
export declare function comparisonLineOption(baseline: Array<{
|
|
13
|
+
x: string;
|
|
14
|
+
y: number;
|
|
15
|
+
}>, current: Array<{
|
|
16
|
+
x: string;
|
|
17
|
+
y: number;
|
|
18
|
+
}>, options?: EchartsComparisonOptions): EchartsOption;
|
|
19
|
+
export declare function comparisonBarOption(series: SeriesFile, options?: EchartsBaseOptions): EchartsOption;
|
|
20
|
+
//# sourceMappingURL=echarts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"echarts.d.ts","sourceRoot":"","sources":["../src/echarts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAO3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,MAAM,WAAW,kBAAmB,SAAQ,kBAAkB;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAyB,SAAQ,kBAAkB;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAWD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE,kBAAuB,GAC/B,aAAa,CAwBf;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,KAAK,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EACzC,OAAO,EAAE,KAAK,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EACxC,OAAO,GAAE,wBAA6B,GACrC,aAAa,CA4Bf;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,kBAAuB,GAC/B,aAAa,CAuBf"}
|
package/dist/echarts.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { alignComparisonCoordinates, getLatestValueRows, seriesEntryToCoordinates, } from './coordinate-transforms.js';
|
|
2
|
+
function baseOption(title) {
|
|
3
|
+
return {
|
|
4
|
+
title: title ? { text: title } : undefined,
|
|
5
|
+
tooltip: { trigger: 'axis' },
|
|
6
|
+
legend: { show: true },
|
|
7
|
+
grid: { left: 48, right: 24, top: 40, bottom: 40 },
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function trendLineOption(entry, options = {}) {
|
|
11
|
+
const points = seriesEntryToCoordinates(entry, {
|
|
12
|
+
maxPoints: options.maxPoints,
|
|
13
|
+
tags: options.tags,
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
...baseOption(options.title),
|
|
17
|
+
xAxis: {
|
|
18
|
+
type: 'category',
|
|
19
|
+
data: points.map((point) => point.x),
|
|
20
|
+
},
|
|
21
|
+
yAxis: {
|
|
22
|
+
type: 'value',
|
|
23
|
+
},
|
|
24
|
+
series: [
|
|
25
|
+
{
|
|
26
|
+
type: 'line',
|
|
27
|
+
name: options.metricName ?? 'Value',
|
|
28
|
+
data: points.map((point) => point.y),
|
|
29
|
+
showSymbol: false,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function comparisonLineOption(baseline, current, options = {}) {
|
|
35
|
+
const rows = alignComparisonCoordinates(baseline.map((point) => ({ x: point.x, y: point.y })), current.map((point) => ({ x: point.x, y: point.y })));
|
|
36
|
+
return {
|
|
37
|
+
...baseOption(options.title),
|
|
38
|
+
xAxis: {
|
|
39
|
+
type: 'category',
|
|
40
|
+
data: rows.map((row) => row.x),
|
|
41
|
+
},
|
|
42
|
+
yAxis: {
|
|
43
|
+
type: 'value',
|
|
44
|
+
},
|
|
45
|
+
series: [
|
|
46
|
+
{
|
|
47
|
+
type: 'line',
|
|
48
|
+
name: options.baselineLabel ?? 'Baseline',
|
|
49
|
+
data: rows.map((row) => row.baseline ?? null),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'line',
|
|
53
|
+
name: options.currentLabel ?? 'Current',
|
|
54
|
+
data: rows.map((row) => row.current ?? null),
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function comparisonBarOption(series, options = {}) {
|
|
60
|
+
const rows = getLatestValueRows(series, {
|
|
61
|
+
maxPoints: options.maxPoints,
|
|
62
|
+
tags: options.tags,
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
...baseOption(options.title),
|
|
66
|
+
xAxis: {
|
|
67
|
+
type: 'category',
|
|
68
|
+
data: rows.map((row) => row.label),
|
|
69
|
+
},
|
|
70
|
+
yAxis: {
|
|
71
|
+
type: 'value',
|
|
72
|
+
},
|
|
73
|
+
series: [
|
|
74
|
+
{
|
|
75
|
+
type: 'bar',
|
|
76
|
+
name: options.metricName ?? 'Value',
|
|
77
|
+
data: rows.map((row) => row.value),
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=echarts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"echarts.js","sourceRoot":"","sources":["../src/echarts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"echarts.test.d.ts","sourceRoot":"","sources":["../src/echarts.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { comparisonBarOption, comparisonLineOption, trendLineOption, } from './echarts.js';
|
|
4
|
+
test('trendLineOption returns basic line option object', () => {
|
|
5
|
+
const entry = {
|
|
6
|
+
points: [
|
|
7
|
+
{ timestamp: '2026-01-01T00:00:00.000Z', value: 1 },
|
|
8
|
+
{ timestamp: '2026-01-02T00:00:00.000Z', value: 2 },
|
|
9
|
+
],
|
|
10
|
+
};
|
|
11
|
+
const option = trendLineOption(entry, { metricName: 'latency_ms' });
|
|
12
|
+
assert.deepEqual(option.xAxis.data, [
|
|
13
|
+
'2026-01-01T00:00:00.000Z',
|
|
14
|
+
'2026-01-02T00:00:00.000Z',
|
|
15
|
+
]);
|
|
16
|
+
assert.equal(option.series[0].name, 'latency_ms');
|
|
17
|
+
assert.deepEqual(option.series[0].data, [1, 2]);
|
|
18
|
+
});
|
|
19
|
+
test('comparisonLineOption aligns baseline/current arrays', () => {
|
|
20
|
+
const option = comparisonLineOption([{ x: '2026-01-01T00:00:00.000Z', y: 1 }], [{ x: '2026-01-02T00:00:00.000Z', y: 2 }]);
|
|
21
|
+
assert.deepEqual(option.xAxis.data, [
|
|
22
|
+
'2026-01-01T00:00:00.000Z',
|
|
23
|
+
'2026-01-02T00:00:00.000Z',
|
|
24
|
+
]);
|
|
25
|
+
assert.deepEqual(option.series[0].data, [1, null]);
|
|
26
|
+
assert.deepEqual(option.series[1].data, [null, 2]);
|
|
27
|
+
});
|
|
28
|
+
test('comparisonBarOption maps latest series values', () => {
|
|
29
|
+
const series = {
|
|
30
|
+
metric: 'latency_ms',
|
|
31
|
+
series: {
|
|
32
|
+
a: {
|
|
33
|
+
points: [{ timestamp: '2026-01-01T00:00:00.000Z', value: 4 }],
|
|
34
|
+
},
|
|
35
|
+
b: {
|
|
36
|
+
points: [{ timestamp: '2026-01-01T00:00:00.000Z', value: 2 }],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
const option = comparisonBarOption(series);
|
|
41
|
+
assert.deepEqual(option.xAxis.data, ['a', 'b']);
|
|
42
|
+
assert.deepEqual(option.series[0].data, [4, 2]);
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=echarts.test.js.map
|