@checkstack/healthcheck-ping-backend 0.1.14 → 0.2.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/CHANGELOG.md +24 -0
- package/package.json +1 -1
- package/src/ping-collector.test.ts +2 -2
- package/src/ping-collector.ts +19 -32
- package/src/strategy.test.ts +5 -5
- package/src/strategy.ts +25 -56
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @checkstack/healthcheck-ping-backend
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3dd1914: Migrate health check strategies to VersionedAggregated with \_type discriminator
|
|
8
|
+
|
|
9
|
+
All 13 health check strategies now use `VersionedAggregated` for their `aggregatedResult` property, enabling automatic bucket merging with 100% mathematical fidelity.
|
|
10
|
+
|
|
11
|
+
**Key changes:**
|
|
12
|
+
|
|
13
|
+
- **`_type` discriminator**: All aggregated state objects now include a required `_type` field (`"average"`, `"rate"`, `"counter"`, `"minmax"`) for reliable type detection
|
|
14
|
+
- The `HealthCheckStrategy` interface now requires `aggregatedResult` to be a `VersionedAggregated<AggregatedResultShape>`
|
|
15
|
+
- Strategy/collector `mergeResult` methods return state objects with `_type` (e.g., `{ _type: "average", _sum, _count, avg }`)
|
|
16
|
+
- `mergeAggregatedBucketResults`, `combineBuckets`, and `reaggregateBuckets` now require `registry` and `strategyId` parameters
|
|
17
|
+
- `HealthCheckService` constructor now requires both `registry` and `collectorRegistry` parameters
|
|
18
|
+
- Frontend `extractComputedValue` now uses `_type` discriminator for robust type detection
|
|
19
|
+
|
|
20
|
+
**Breaking Change**: State objects now require `_type`. Merge functions automatically add `_type` to output. The bucket merging functions and `HealthCheckService` now require additional required parameters.
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- Updated dependencies [3dd1914]
|
|
25
|
+
- @checkstack/backend-api@0.7.0
|
|
26
|
+
|
|
3
27
|
## 0.1.14
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -117,8 +117,8 @@ describe("PingCollector", () => {
|
|
|
117
117
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
118
118
|
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
119
119
|
|
|
120
|
-
expect(aggregated.avgPacketLoss).toBe(5);
|
|
121
|
-
expect(aggregated.avgLatency).toBe(15);
|
|
120
|
+
expect(aggregated.avgPacketLoss.avg).toBe(5);
|
|
121
|
+
expect(aggregated.avgLatency.avg).toBe(15);
|
|
122
122
|
});
|
|
123
123
|
});
|
|
124
124
|
|
package/src/ping-collector.ts
CHANGED
|
@@ -5,8 +5,9 @@ import {
|
|
|
5
5
|
type CollectorResult,
|
|
6
6
|
type CollectorStrategy,
|
|
7
7
|
mergeAverage,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
VersionedAggregated,
|
|
9
|
+
aggregatedAverage,
|
|
10
|
+
type InferAggregatedResult,
|
|
10
11
|
} from "@checkstack/backend-api";
|
|
11
12
|
import {
|
|
12
13
|
healthResultNumber,
|
|
@@ -74,29 +75,24 @@ const pingResultSchema = healthResultSchema({
|
|
|
74
75
|
|
|
75
76
|
export type PingResult = z.infer<typeof pingResultSchema>;
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
// Aggregated result fields definition
|
|
79
|
+
const pingAggregatedFields = {
|
|
80
|
+
avgPacketLoss: aggregatedAverage({
|
|
79
81
|
"x-chart-type": "gauge",
|
|
80
82
|
"x-chart-label": "Avg Packet Loss",
|
|
81
83
|
"x-chart-unit": "%",
|
|
82
84
|
}),
|
|
83
|
-
avgLatency:
|
|
85
|
+
avgLatency: aggregatedAverage({
|
|
84
86
|
"x-chart-type": "line",
|
|
85
87
|
"x-chart-label": "Avg Latency",
|
|
86
88
|
"x-chart-unit": "ms",
|
|
87
89
|
}),
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const pingAggregatedInternalSchema = z.object({
|
|
91
|
-
_packetLoss: averageStateSchema.optional(),
|
|
92
|
-
_latency: averageStateSchema.optional(),
|
|
93
|
-
});
|
|
90
|
+
};
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
export type PingAggregatedResult = z.infer<typeof pingAggregatedSchema>;
|
|
92
|
+
// Type inferred from field definitions
|
|
93
|
+
export type PingAggregatedResult = InferAggregatedResult<
|
|
94
|
+
typeof pingAggregatedFields
|
|
95
|
+
>;
|
|
100
96
|
|
|
101
97
|
// ============================================================================
|
|
102
98
|
// PING COLLECTOR
|
|
@@ -122,9 +118,9 @@ export class PingCollector implements CollectorStrategy<
|
|
|
122
118
|
|
|
123
119
|
config = new Versioned({ version: 1, schema: pingConfigSchema });
|
|
124
120
|
result = new Versioned({ version: 1, schema: pingResultSchema });
|
|
125
|
-
aggregatedResult = new
|
|
121
|
+
aggregatedResult = new VersionedAggregated({
|
|
126
122
|
version: 1,
|
|
127
|
-
|
|
123
|
+
fields: pingAggregatedFields,
|
|
128
124
|
});
|
|
129
125
|
|
|
130
126
|
async execute({
|
|
@@ -160,21 +156,12 @@ export class PingCollector implements CollectorStrategy<
|
|
|
160
156
|
): PingAggregatedResult {
|
|
161
157
|
const metadata = run.metadata;
|
|
162
158
|
|
|
163
|
-
const lossState = mergeAverage(
|
|
164
|
-
existing?._packetLoss as AverageState | undefined,
|
|
165
|
-
metadata?.packetLoss,
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
const latencyState = mergeAverage(
|
|
169
|
-
existing?._latency as AverageState | undefined,
|
|
170
|
-
metadata?.avgLatency,
|
|
171
|
-
);
|
|
172
|
-
|
|
173
159
|
return {
|
|
174
|
-
avgPacketLoss:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
160
|
+
avgPacketLoss: mergeAverage(
|
|
161
|
+
existing?.avgPacketLoss,
|
|
162
|
+
metadata?.packetLoss,
|
|
163
|
+
),
|
|
164
|
+
avgLatency: mergeAverage(existing?.avgLatency, metadata?.avgLatency),
|
|
178
165
|
};
|
|
179
166
|
}
|
|
180
167
|
}
|
package/src/strategy.test.ts
CHANGED
|
@@ -176,10 +176,10 @@ describe("PingHealthCheckStrategy", () => {
|
|
|
176
176
|
aggregated = strategy.mergeResult(aggregated, runs[1]);
|
|
177
177
|
|
|
178
178
|
// (0 + 33) / 2 = 16.5
|
|
179
|
-
expect(aggregated.avgPacketLoss).toBeCloseTo(16.5, 1);
|
|
180
|
-
expect(aggregated.avgLatency).toBeCloseTo(15, 1);
|
|
181
|
-
expect(aggregated.maxLatency).toBe(25);
|
|
182
|
-
expect(aggregated.errorCount).toBe(0);
|
|
179
|
+
expect(aggregated.avgPacketLoss.avg).toBeCloseTo(16.5, 1);
|
|
180
|
+
expect(aggregated.avgLatency.avg).toBeCloseTo(15, 1);
|
|
181
|
+
expect(aggregated.maxLatency.max).toBe(25);
|
|
182
|
+
expect(aggregated.errorCount.count).toBe(0);
|
|
183
183
|
});
|
|
184
184
|
|
|
185
185
|
it("should count errors", () => {
|
|
@@ -199,7 +199,7 @@ describe("PingHealthCheckStrategy", () => {
|
|
|
199
199
|
|
|
200
200
|
const aggregated = strategy.mergeResult(undefined, run);
|
|
201
201
|
|
|
202
|
-
expect(aggregated.errorCount).toBe(1);
|
|
202
|
+
expect(aggregated.errorCount.count).toBe(1);
|
|
203
203
|
});
|
|
204
204
|
});
|
|
205
205
|
});
|
package/src/strategy.ts
CHANGED
|
@@ -2,17 +2,16 @@ import {
|
|
|
2
2
|
HealthCheckStrategy,
|
|
3
3
|
HealthCheckRunForAggregation,
|
|
4
4
|
Versioned,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
VersionedAggregated,
|
|
6
|
+
aggregatedAverage,
|
|
7
|
+
aggregatedMinMax,
|
|
8
|
+
aggregatedCounter,
|
|
7
9
|
mergeAverage,
|
|
8
|
-
averageStateSchema,
|
|
9
10
|
mergeCounter,
|
|
10
|
-
counterStateSchema,
|
|
11
11
|
mergeMinMax,
|
|
12
|
-
|
|
13
|
-
type
|
|
14
|
-
type
|
|
15
|
-
type MinMaxState,
|
|
12
|
+
z,
|
|
13
|
+
type ConnectedClient,
|
|
14
|
+
type InferAggregatedResult,
|
|
16
15
|
} from "@checkstack/backend-api";
|
|
17
16
|
import {
|
|
18
17
|
healthResultNumber,
|
|
@@ -90,43 +89,30 @@ const pingResultSchema = healthResultSchema({
|
|
|
90
89
|
|
|
91
90
|
type PingResult = z.infer<typeof pingResultSchema>;
|
|
92
91
|
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const pingAggregatedDisplaySchema = healthResultSchema({
|
|
97
|
-
avgPacketLoss: healthResultNumber({
|
|
92
|
+
/** Aggregated field definitions for bucket merging */
|
|
93
|
+
const pingAggregatedFields = {
|
|
94
|
+
avgPacketLoss: aggregatedAverage({
|
|
98
95
|
"x-chart-type": "gauge",
|
|
99
96
|
"x-chart-label": "Avg Packet Loss",
|
|
100
97
|
"x-chart-unit": "%",
|
|
101
98
|
}),
|
|
102
|
-
avgLatency:
|
|
99
|
+
avgLatency: aggregatedAverage({
|
|
103
100
|
"x-chart-type": "line",
|
|
104
101
|
"x-chart-label": "Avg Latency",
|
|
105
102
|
"x-chart-unit": "ms",
|
|
106
103
|
}),
|
|
107
|
-
maxLatency:
|
|
104
|
+
maxLatency: aggregatedMinMax({
|
|
108
105
|
"x-chart-type": "line",
|
|
109
106
|
"x-chart-label": "Max Latency",
|
|
110
107
|
"x-chart-unit": "ms",
|
|
111
108
|
}),
|
|
112
|
-
errorCount:
|
|
109
|
+
errorCount: aggregatedCounter({
|
|
113
110
|
"x-chart-type": "counter",
|
|
114
111
|
"x-chart-label": "Errors",
|
|
115
112
|
}),
|
|
116
|
-
}
|
|
113
|
+
};
|
|
117
114
|
|
|
118
|
-
|
|
119
|
-
_packetLoss: averageStateSchema.optional(),
|
|
120
|
-
_latency: averageStateSchema.optional(),
|
|
121
|
-
_maxLatency: minMaxStateSchema.optional(),
|
|
122
|
-
_errors: counterStateSchema.optional(),
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const pingAggregatedSchema = pingAggregatedDisplaySchema.merge(
|
|
126
|
-
pingAggregatedInternalSchema,
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
type PingAggregatedResult = z.infer<typeof pingAggregatedSchema>;
|
|
115
|
+
type PingAggregatedResult = InferAggregatedResult<typeof pingAggregatedFields>;
|
|
130
116
|
|
|
131
117
|
// ============================================================================
|
|
132
118
|
// STRATEGY
|
|
@@ -136,7 +122,7 @@ export class PingHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
136
122
|
PingConfig,
|
|
137
123
|
PingTransportClient,
|
|
138
124
|
PingResult,
|
|
139
|
-
|
|
125
|
+
typeof pingAggregatedFields
|
|
140
126
|
> {
|
|
141
127
|
id = "ping";
|
|
142
128
|
displayName = "Ping Health Check";
|
|
@@ -170,9 +156,9 @@ export class PingHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
170
156
|
],
|
|
171
157
|
});
|
|
172
158
|
|
|
173
|
-
aggregatedResult
|
|
159
|
+
aggregatedResult = new VersionedAggregated({
|
|
174
160
|
version: 1,
|
|
175
|
-
|
|
161
|
+
fields: pingAggregatedFields,
|
|
176
162
|
});
|
|
177
163
|
|
|
178
164
|
mergeResult(
|
|
@@ -181,36 +167,19 @@ export class PingHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
181
167
|
): PingAggregatedResult {
|
|
182
168
|
const metadata = run.metadata;
|
|
183
169
|
|
|
184
|
-
const
|
|
185
|
-
existing?.
|
|
170
|
+
const avgPacketLoss = mergeAverage(
|
|
171
|
+
existing?.avgPacketLoss,
|
|
186
172
|
metadata?.packetLoss,
|
|
187
173
|
);
|
|
188
174
|
|
|
189
|
-
const
|
|
190
|
-
existing?._latency as AverageState | undefined,
|
|
191
|
-
metadata?.avgLatency,
|
|
192
|
-
);
|
|
175
|
+
const avgLatency = mergeAverage(existing?.avgLatency, metadata?.avgLatency);
|
|
193
176
|
|
|
194
|
-
const
|
|
195
|
-
existing?._maxLatency as MinMaxState | undefined,
|
|
196
|
-
metadata?.maxLatency,
|
|
197
|
-
);
|
|
177
|
+
const maxLatency = mergeMinMax(existing?.maxLatency, metadata?.maxLatency);
|
|
198
178
|
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
metadata?.error !== undefined,
|
|
202
|
-
);
|
|
179
|
+
const hasError = metadata?.error !== undefined;
|
|
180
|
+
const errorCount = mergeCounter(existing?.errorCount, hasError);
|
|
203
181
|
|
|
204
|
-
return {
|
|
205
|
-
avgPacketLoss: Math.round(packetLossState.avg * 10) / 10,
|
|
206
|
-
avgLatency: Math.round(latencyState.avg * 10) / 10,
|
|
207
|
-
maxLatency: maxLatencyState.max,
|
|
208
|
-
errorCount: errorState.count,
|
|
209
|
-
_packetLoss: packetLossState,
|
|
210
|
-
_latency: latencyState,
|
|
211
|
-
_maxLatency: maxLatencyState,
|
|
212
|
-
_errors: errorState,
|
|
213
|
-
};
|
|
182
|
+
return { avgPacketLoss, avgLatency, maxLatency, errorCount };
|
|
214
183
|
}
|
|
215
184
|
|
|
216
185
|
async createClient(
|