@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.
Files changed (58) hide show
  1. package/README.md +135 -0
  2. package/dist/chartjs.d.ts +77 -0
  3. package/dist/chartjs.d.ts.map +1 -0
  4. package/dist/chartjs.js +88 -0
  5. package/dist/chartjs.js.map +1 -0
  6. package/dist/coordinate-transforms.d.ts +12 -0
  7. package/dist/coordinate-transforms.d.ts.map +1 -0
  8. package/dist/coordinate-transforms.js +80 -0
  9. package/dist/coordinate-transforms.js.map +1 -0
  10. package/dist/coordinate-transforms.test.d.ts +2 -0
  11. package/dist/coordinate-transforms.test.d.ts.map +1 -0
  12. package/dist/coordinate-transforms.test.js +59 -0
  13. package/dist/coordinate-transforms.test.js.map +1 -0
  14. package/dist/echarts.d.ts +20 -0
  15. package/dist/echarts.d.ts.map +1 -0
  16. package/dist/echarts.js +82 -0
  17. package/dist/echarts.js.map +1 -0
  18. package/dist/echarts.test.d.ts +2 -0
  19. package/dist/echarts.test.d.ts.map +1 -0
  20. package/dist/echarts.test.js +44 -0
  21. package/dist/echarts.test.js.map +1 -0
  22. package/dist/index.d.ts +20 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +14 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/recharts.d.ts +30 -0
  27. package/dist/recharts.d.ts.map +1 -0
  28. package/dist/recharts.js +33 -0
  29. package/dist/recharts.js.map +1 -0
  30. package/dist/recharts.test.d.ts +2 -0
  31. package/dist/recharts.test.d.ts.map +1 -0
  32. package/dist/recharts.test.js +46 -0
  33. package/dist/recharts.test.js.map +1 -0
  34. package/dist/regression.d.ts +45 -0
  35. package/dist/regression.d.ts.map +1 -0
  36. package/dist/regression.js +64 -0
  37. package/dist/regression.js.map +1 -0
  38. package/dist/shared-contract.d.ts +31 -0
  39. package/dist/shared-contract.d.ts.map +1 -0
  40. package/dist/shared-contract.js +29 -0
  41. package/dist/shared-contract.js.map +1 -0
  42. package/dist/shared-contract.test.d.ts +2 -0
  43. package/dist/shared-contract.test.d.ts.map +1 -0
  44. package/dist/shared-contract.test.js +23 -0
  45. package/dist/shared-contract.test.js.map +1 -0
  46. package/dist/transforms.d.ts +30 -0
  47. package/dist/transforms.d.ts.map +1 -0
  48. package/dist/transforms.js +64 -0
  49. package/dist/transforms.js.map +1 -0
  50. package/dist/visx.d.ts +35 -0
  51. package/dist/visx.d.ts.map +1 -0
  52. package/dist/visx.js +45 -0
  53. package/dist/visx.js.map +1 -0
  54. package/dist/visx.test.d.ts +2 -0
  55. package/dist/visx.test.d.ts.map +1 -0
  56. package/dist/visx.test.js +39 -0
  57. package/dist/visx.test.js.map +1 -0
  58. 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"}
@@ -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":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=coordinate-transforms.test.d.ts.map
@@ -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"}
@@ -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":"AAEA,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AAcpC,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;QAC1C,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;QAC5B,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;QACtB,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;KACnD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,KAAkB,EAClB,UAA8B,EAAE;IAEhC,MAAM,MAAM,GAAG,wBAAwB,CAAC,KAAK,EAAE;QAC7C,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC,CAAC;IAEH,OAAO;QACL,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC;QAC5B,KAAK,EAAE;YACL,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;SACrC;QACD,KAAK,EAAE;YACL,IAAI,EAAE,OAAO;SACd;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,OAAO,CAAC,UAAU,IAAI,OAAO;gBACnC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;gBACpC,UAAU,EAAE,KAAK;aAClB;SACF;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,QAAyC,EACzC,OAAwC,EACxC,UAAoC,EAAE;IAEtC,MAAM,IAAI,GAAG,0BAA0B,CACrC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EACrD,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CACrD,CAAC;IAEF,OAAO;QACL,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC;QAC5B,KAAK,EAAE;YACL,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;SAC/B;QACD,KAAK,EAAE;YACL,IAAI,EAAE,OAAO;SACd;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,OAAO,CAAC,aAAa,IAAI,UAAU;gBACzC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC;aAC9C;YACD;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,OAAO,CAAC,YAAY,IAAI,SAAS;gBACvC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC;aAC7C;SACF;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,MAAkB,EAClB,UAA8B,EAAE;IAEhC,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,EAAE;QACtC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC,CAAC;IAEH,OAAO;QACL,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC;QAC5B,KAAK,EAAE;YACL,IAAI,EAAE,UAAU;YAChB,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC;SACnC;QACD,KAAK,EAAE;YACL,IAAI,EAAE,OAAO;SACd;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,OAAO,CAAC,UAAU,IAAI,OAAO;gBACnC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC;aACnC;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=echarts.test.d.ts.map
@@ -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