@checkstack/healthcheck-ping-backend 0.1.14 → 0.2.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/CHANGELOG.md +48 -0
- package/package.json +4 -4
- 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 +27 -63
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @checkstack/healthcheck-ping-backend
|
|
2
2
|
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 869b4ab: ## Health Check Execution Improvements
|
|
8
|
+
|
|
9
|
+
### Breaking Changes (backend-api)
|
|
10
|
+
|
|
11
|
+
- `HealthCheckStrategy.createClient()` now accepts `unknown` instead of `TConfig` due to TypeScript contravariance constraints. Implementations should use `this.config.validate(config)` to narrow the type.
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
- **Platform-level hard timeout**: The executor now wraps the entire health check execution (connection + all collectors) in a single timeout, ensuring checks never hang indefinitely.
|
|
16
|
+
- **Parallel collector execution**: Collectors now run in parallel using `Promise.allSettled()`, improving performance while ensuring all collectors complete regardless of individual failures.
|
|
17
|
+
- **Base strategy config schema**: All strategy configs now extend `baseStrategyConfigSchema` which provides a standardized `timeout` field with sensible defaults (30s, min 100ms).
|
|
18
|
+
|
|
19
|
+
### Fixes
|
|
20
|
+
|
|
21
|
+
- Fixed HTTP and Jenkins strategies clearing timeouts before reading the full response body.
|
|
22
|
+
- Simplified registry type signatures by using default type parameters.
|
|
23
|
+
|
|
24
|
+
- Updated dependencies [869b4ab]
|
|
25
|
+
- @checkstack/backend-api@0.8.0
|
|
26
|
+
|
|
27
|
+
## 0.2.0
|
|
28
|
+
|
|
29
|
+
### Minor Changes
|
|
30
|
+
|
|
31
|
+
- 3dd1914: Migrate health check strategies to VersionedAggregated with \_type discriminator
|
|
32
|
+
|
|
33
|
+
All 13 health check strategies now use `VersionedAggregated` for their `aggregatedResult` property, enabling automatic bucket merging with 100% mathematical fidelity.
|
|
34
|
+
|
|
35
|
+
**Key changes:**
|
|
36
|
+
|
|
37
|
+
- **`_type` discriminator**: All aggregated state objects now include a required `_type` field (`"average"`, `"rate"`, `"counter"`, `"minmax"`) for reliable type detection
|
|
38
|
+
- The `HealthCheckStrategy` interface now requires `aggregatedResult` to be a `VersionedAggregated<AggregatedResultShape>`
|
|
39
|
+
- Strategy/collector `mergeResult` methods return state objects with `_type` (e.g., `{ _type: "average", _sum, _count, avg }`)
|
|
40
|
+
- `mergeAggregatedBucketResults`, `combineBuckets`, and `reaggregateBuckets` now require `registry` and `strategyId` parameters
|
|
41
|
+
- `HealthCheckService` constructor now requires both `registry` and `collectorRegistry` parameters
|
|
42
|
+
- Frontend `extractComputedValue` now uses `_type` discriminator for robust type detection
|
|
43
|
+
|
|
44
|
+
**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.
|
|
45
|
+
|
|
46
|
+
### Patch Changes
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [3dd1914]
|
|
49
|
+
- @checkstack/backend-api@0.7.0
|
|
50
|
+
|
|
3
51
|
## 0.1.14
|
|
4
52
|
|
|
5
53
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkstack/healthcheck-ping-backend",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"scripts": {
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
"lint:code": "eslint . --max-warnings 0"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@checkstack/backend-api": "0.
|
|
13
|
-
"@checkstack/common": "0.6.
|
|
14
|
-
"@checkstack/healthcheck-common": "0.8.
|
|
12
|
+
"@checkstack/backend-api": "0.7.0",
|
|
13
|
+
"@checkstack/common": "0.6.2",
|
|
14
|
+
"@checkstack/healthcheck-common": "0.8.2"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@types/bun": "^1.0.0",
|
|
@@ -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,17 @@ 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
|
-
|
|
12
|
+
z,
|
|
13
|
+
type ConnectedClient,
|
|
14
|
+
type InferAggregatedResult,
|
|
15
|
+
baseStrategyConfigSchema,
|
|
16
16
|
} from "@checkstack/backend-api";
|
|
17
17
|
import {
|
|
18
18
|
healthResultNumber,
|
|
@@ -33,13 +33,7 @@ import type {
|
|
|
33
33
|
* Configuration schema for Ping health checks.
|
|
34
34
|
* Global defaults only - action params moved to PingCollector.
|
|
35
35
|
*/
|
|
36
|
-
export const pingConfigSchema =
|
|
37
|
-
timeout: z
|
|
38
|
-
.number()
|
|
39
|
-
.min(100)
|
|
40
|
-
.default(5000)
|
|
41
|
-
.describe("Default timeout in milliseconds"),
|
|
42
|
-
});
|
|
36
|
+
export const pingConfigSchema = baseStrategyConfigSchema.extend({});
|
|
43
37
|
|
|
44
38
|
export type PingConfig = z.infer<typeof pingConfigSchema>;
|
|
45
39
|
|
|
@@ -90,43 +84,30 @@ const pingResultSchema = healthResultSchema({
|
|
|
90
84
|
|
|
91
85
|
type PingResult = z.infer<typeof pingResultSchema>;
|
|
92
86
|
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const pingAggregatedDisplaySchema = healthResultSchema({
|
|
97
|
-
avgPacketLoss: healthResultNumber({
|
|
87
|
+
/** Aggregated field definitions for bucket merging */
|
|
88
|
+
const pingAggregatedFields = {
|
|
89
|
+
avgPacketLoss: aggregatedAverage({
|
|
98
90
|
"x-chart-type": "gauge",
|
|
99
91
|
"x-chart-label": "Avg Packet Loss",
|
|
100
92
|
"x-chart-unit": "%",
|
|
101
93
|
}),
|
|
102
|
-
avgLatency:
|
|
94
|
+
avgLatency: aggregatedAverage({
|
|
103
95
|
"x-chart-type": "line",
|
|
104
96
|
"x-chart-label": "Avg Latency",
|
|
105
97
|
"x-chart-unit": "ms",
|
|
106
98
|
}),
|
|
107
|
-
maxLatency:
|
|
99
|
+
maxLatency: aggregatedMinMax({
|
|
108
100
|
"x-chart-type": "line",
|
|
109
101
|
"x-chart-label": "Max Latency",
|
|
110
102
|
"x-chart-unit": "ms",
|
|
111
103
|
}),
|
|
112
|
-
errorCount:
|
|
104
|
+
errorCount: aggregatedCounter({
|
|
113
105
|
"x-chart-type": "counter",
|
|
114
106
|
"x-chart-label": "Errors",
|
|
115
107
|
}),
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const pingAggregatedInternalSchema = z.object({
|
|
119
|
-
_packetLoss: averageStateSchema.optional(),
|
|
120
|
-
_latency: averageStateSchema.optional(),
|
|
121
|
-
_maxLatency: minMaxStateSchema.optional(),
|
|
122
|
-
_errors: counterStateSchema.optional(),
|
|
123
|
-
});
|
|
108
|
+
};
|
|
124
109
|
|
|
125
|
-
|
|
126
|
-
pingAggregatedInternalSchema,
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
type PingAggregatedResult = z.infer<typeof pingAggregatedSchema>;
|
|
110
|
+
type PingAggregatedResult = InferAggregatedResult<typeof pingAggregatedFields>;
|
|
130
111
|
|
|
131
112
|
// ============================================================================
|
|
132
113
|
// STRATEGY
|
|
@@ -136,7 +117,7 @@ export class PingHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
136
117
|
PingConfig,
|
|
137
118
|
PingTransportClient,
|
|
138
119
|
PingResult,
|
|
139
|
-
|
|
120
|
+
typeof pingAggregatedFields
|
|
140
121
|
> {
|
|
141
122
|
id = "ping";
|
|
142
123
|
displayName = "Ping Health Check";
|
|
@@ -170,9 +151,9 @@ export class PingHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
170
151
|
],
|
|
171
152
|
});
|
|
172
153
|
|
|
173
|
-
aggregatedResult
|
|
154
|
+
aggregatedResult = new VersionedAggregated({
|
|
174
155
|
version: 1,
|
|
175
|
-
|
|
156
|
+
fields: pingAggregatedFields,
|
|
176
157
|
});
|
|
177
158
|
|
|
178
159
|
mergeResult(
|
|
@@ -181,36 +162,19 @@ export class PingHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
181
162
|
): PingAggregatedResult {
|
|
182
163
|
const metadata = run.metadata;
|
|
183
164
|
|
|
184
|
-
const
|
|
185
|
-
existing?.
|
|
165
|
+
const avgPacketLoss = mergeAverage(
|
|
166
|
+
existing?.avgPacketLoss,
|
|
186
167
|
metadata?.packetLoss,
|
|
187
168
|
);
|
|
188
169
|
|
|
189
|
-
const
|
|
190
|
-
existing?._latency as AverageState | undefined,
|
|
191
|
-
metadata?.avgLatency,
|
|
192
|
-
);
|
|
170
|
+
const avgLatency = mergeAverage(existing?.avgLatency, metadata?.avgLatency);
|
|
193
171
|
|
|
194
|
-
const
|
|
195
|
-
existing?._maxLatency as MinMaxState | undefined,
|
|
196
|
-
metadata?.maxLatency,
|
|
197
|
-
);
|
|
172
|
+
const maxLatency = mergeMinMax(existing?.maxLatency, metadata?.maxLatency);
|
|
198
173
|
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
metadata?.error !== undefined,
|
|
202
|
-
);
|
|
174
|
+
const hasError = metadata?.error !== undefined;
|
|
175
|
+
const errorCount = mergeCounter(existing?.errorCount, hasError);
|
|
203
176
|
|
|
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
|
-
};
|
|
177
|
+
return { avgPacketLoss, avgLatency, maxLatency, errorCount };
|
|
214
178
|
}
|
|
215
179
|
|
|
216
180
|
async createClient(
|