@checkstack/healthcheck-tcp-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/banner-collector.test.ts +2 -2
- package/src/banner-collector.ts +20 -36
- package/src/strategy.test.ts +5 -5
- package/src/strategy.ts +24 -52
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @checkstack/healthcheck-tcp-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
|
@@ -90,8 +90,8 @@ describe("BannerCollector", () => {
|
|
|
90
90
|
let aggregated = collector.mergeResult(undefined, runs[0]);
|
|
91
91
|
aggregated = collector.mergeResult(aggregated, runs[1]);
|
|
92
92
|
|
|
93
|
-
expect(aggregated.avgReadTimeMs).toBe(75);
|
|
94
|
-
expect(aggregated.bannerRate).toBe(50);
|
|
93
|
+
expect(aggregated.avgReadTimeMs.avg).toBe(75);
|
|
94
|
+
expect(aggregated.bannerRate.rate).toBe(50);
|
|
95
95
|
});
|
|
96
96
|
});
|
|
97
97
|
|
package/src/banner-collector.ts
CHANGED
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
type CollectorResult,
|
|
6
6
|
type CollectorStrategy,
|
|
7
7
|
mergeAverage,
|
|
8
|
-
averageStateSchema,
|
|
9
8
|
mergeRate,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
VersionedAggregated,
|
|
10
|
+
aggregatedAverage,
|
|
11
|
+
aggregatedRate,
|
|
12
|
+
type InferAggregatedResult,
|
|
13
13
|
} from "@checkstack/backend-api";
|
|
14
14
|
import {
|
|
15
15
|
healthResultNumber,
|
|
@@ -56,31 +56,24 @@ const bannerResultSchema = healthResultSchema({
|
|
|
56
56
|
|
|
57
57
|
export type BannerResult = z.infer<typeof bannerResultSchema>;
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
// Aggregated result fields definition
|
|
60
|
+
const bannerAggregatedFields = {
|
|
61
|
+
avgReadTimeMs: aggregatedAverage({
|
|
61
62
|
"x-chart-type": "line",
|
|
62
63
|
"x-chart-label": "Avg Read Time",
|
|
63
64
|
"x-chart-unit": "ms",
|
|
64
65
|
}),
|
|
65
|
-
bannerRate:
|
|
66
|
+
bannerRate: aggregatedRate({
|
|
66
67
|
"x-chart-type": "gauge",
|
|
67
68
|
"x-chart-label": "Banner Rate",
|
|
68
69
|
"x-chart-unit": "%",
|
|
69
70
|
}),
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const bannerAggregatedInternalSchema = z.object({
|
|
73
|
-
_readTime: averageStateSchema
|
|
74
|
-
.optional(),
|
|
75
|
-
_banner: rateStateSchema
|
|
76
|
-
.optional(),
|
|
77
|
-
});
|
|
71
|
+
};
|
|
78
72
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
export type BannerAggregatedResult = z.infer<typeof bannerAggregatedSchema>;
|
|
73
|
+
// Type inferred from field definitions
|
|
74
|
+
export type BannerAggregatedResult = InferAggregatedResult<
|
|
75
|
+
typeof bannerAggregatedFields
|
|
76
|
+
>;
|
|
84
77
|
|
|
85
78
|
// ============================================================================
|
|
86
79
|
// BANNER COLLECTOR
|
|
@@ -106,9 +99,9 @@ export class BannerCollector implements CollectorStrategy<
|
|
|
106
99
|
|
|
107
100
|
config = new Versioned({ version: 1, schema: bannerConfigSchema });
|
|
108
101
|
result = new Versioned({ version: 1, schema: bannerResultSchema });
|
|
109
|
-
aggregatedResult = new
|
|
102
|
+
aggregatedResult = new VersionedAggregated({
|
|
110
103
|
version: 1,
|
|
111
|
-
|
|
104
|
+
fields: bannerAggregatedFields,
|
|
112
105
|
});
|
|
113
106
|
|
|
114
107
|
async execute({
|
|
@@ -143,21 +136,12 @@ export class BannerCollector implements CollectorStrategy<
|
|
|
143
136
|
): BannerAggregatedResult {
|
|
144
137
|
const metadata = run.metadata;
|
|
145
138
|
|
|
146
|
-
const readTimeState = mergeAverage(
|
|
147
|
-
existing?._readTime as AverageState | undefined,
|
|
148
|
-
metadata?.readTimeMs,
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
const bannerState = mergeRate(
|
|
152
|
-
existing?._banner as RateState | undefined,
|
|
153
|
-
metadata?.hasBanner,
|
|
154
|
-
);
|
|
155
|
-
|
|
156
139
|
return {
|
|
157
|
-
avgReadTimeMs:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
140
|
+
avgReadTimeMs: mergeAverage(
|
|
141
|
+
existing?.avgReadTimeMs,
|
|
142
|
+
metadata?.readTimeMs,
|
|
143
|
+
),
|
|
144
|
+
bannerRate: mergeRate(existing?.bannerRate, metadata?.hasBanner),
|
|
161
145
|
};
|
|
162
146
|
}
|
|
163
147
|
}
|
package/src/strategy.test.ts
CHANGED
|
@@ -121,9 +121,9 @@ describe("TcpHealthCheckStrategy", () => {
|
|
|
121
121
|
let aggregated = strategy.mergeResult(undefined, runs[0]);
|
|
122
122
|
aggregated = strategy.mergeResult(aggregated, runs[1]);
|
|
123
123
|
|
|
124
|
-
expect(aggregated.avgConnectionTime).toBe(15);
|
|
125
|
-
expect(aggregated.successRate).toBe(100);
|
|
126
|
-
expect(aggregated.errorCount).toBe(0);
|
|
124
|
+
expect(aggregated.avgConnectionTime.avg).toBe(15);
|
|
125
|
+
expect(aggregated.successRate.rate).toBe(100);
|
|
126
|
+
expect(aggregated.errorCount.count).toBe(0);
|
|
127
127
|
});
|
|
128
128
|
|
|
129
129
|
it("should count errors and calculate success rate", () => {
|
|
@@ -157,8 +157,8 @@ describe("TcpHealthCheckStrategy", () => {
|
|
|
157
157
|
let aggregated = strategy.mergeResult(undefined, runs[0]);
|
|
158
158
|
aggregated = strategy.mergeResult(aggregated, runs[1]);
|
|
159
159
|
|
|
160
|
-
expect(aggregated.successRate).toBe(50);
|
|
161
|
-
expect(aggregated.errorCount).toBe(1);
|
|
160
|
+
expect(aggregated.successRate.rate).toBe(50);
|
|
161
|
+
expect(aggregated.errorCount.count).toBe(1);
|
|
162
162
|
});
|
|
163
163
|
});
|
|
164
164
|
});
|
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
|
+
aggregatedRate,
|
|
8
|
+
aggregatedCounter,
|
|
7
9
|
mergeAverage,
|
|
8
|
-
averageStateSchema,
|
|
9
10
|
mergeRate,
|
|
10
|
-
rateStateSchema,
|
|
11
11
|
mergeCounter,
|
|
12
|
-
|
|
13
|
-
type
|
|
14
|
-
type
|
|
15
|
-
type CounterState,
|
|
12
|
+
z,
|
|
13
|
+
type ConnectedClient,
|
|
14
|
+
type InferAggregatedResult,
|
|
16
15
|
} from "@checkstack/backend-api";
|
|
17
16
|
import {
|
|
18
17
|
healthResultBoolean,
|
|
@@ -79,41 +78,25 @@ const tcpResultSchema = healthResultSchema({
|
|
|
79
78
|
|
|
80
79
|
type TcpResult = z.infer<typeof tcpResultSchema>;
|
|
81
80
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// UI-visible aggregated fields
|
|
86
|
-
const tcpAggregatedDisplaySchema = healthResultSchema({
|
|
87
|
-
avgConnectionTime: healthResultNumber({
|
|
81
|
+
/** Aggregated field definitions for bucket merging */
|
|
82
|
+
const tcpAggregatedFields = {
|
|
83
|
+
avgConnectionTime: aggregatedAverage({
|
|
88
84
|
"x-chart-type": "line",
|
|
89
85
|
"x-chart-label": "Avg Connection Time",
|
|
90
86
|
"x-chart-unit": "ms",
|
|
91
87
|
}),
|
|
92
|
-
successRate:
|
|
88
|
+
successRate: aggregatedRate({
|
|
93
89
|
"x-chart-type": "gauge",
|
|
94
90
|
"x-chart-label": "Success Rate",
|
|
95
91
|
"x-chart-unit": "%",
|
|
96
92
|
}),
|
|
97
|
-
errorCount:
|
|
93
|
+
errorCount: aggregatedCounter({
|
|
98
94
|
"x-chart-type": "counter",
|
|
99
95
|
"x-chart-label": "Errors",
|
|
100
96
|
}),
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Internal state for incremental aggregation
|
|
104
|
-
const tcpAggregatedInternalSchema = z.object({
|
|
105
|
-
_connectionTime: averageStateSchema
|
|
106
|
-
.optional(),
|
|
107
|
-
_success: rateStateSchema
|
|
108
|
-
.optional(),
|
|
109
|
-
_errors: counterStateSchema.optional(),
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const tcpAggregatedSchema = tcpAggregatedDisplaySchema.merge(
|
|
113
|
-
tcpAggregatedInternalSchema,
|
|
114
|
-
);
|
|
97
|
+
};
|
|
115
98
|
|
|
116
|
-
type TcpAggregatedResult =
|
|
99
|
+
type TcpAggregatedResult = InferAggregatedResult<typeof tcpAggregatedFields>;
|
|
117
100
|
|
|
118
101
|
// ============================================================================
|
|
119
102
|
// SOCKET INTERFACE (for testability)
|
|
@@ -186,7 +169,7 @@ export class TcpHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
186
169
|
TcpConfig,
|
|
187
170
|
TcpTransportClient,
|
|
188
171
|
TcpResult,
|
|
189
|
-
|
|
172
|
+
typeof tcpAggregatedFields
|
|
190
173
|
> {
|
|
191
174
|
id = "tcp";
|
|
192
175
|
displayName = "TCP Health Check";
|
|
@@ -228,9 +211,9 @@ export class TcpHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
228
211
|
],
|
|
229
212
|
});
|
|
230
213
|
|
|
231
|
-
aggregatedResult
|
|
214
|
+
aggregatedResult = new VersionedAggregated({
|
|
232
215
|
version: 1,
|
|
233
|
-
|
|
216
|
+
fields: tcpAggregatedFields,
|
|
234
217
|
});
|
|
235
218
|
|
|
236
219
|
mergeResult(
|
|
@@ -239,29 +222,18 @@ export class TcpHealthCheckStrategy implements HealthCheckStrategy<
|
|
|
239
222
|
): TcpAggregatedResult {
|
|
240
223
|
const metadata = run.metadata;
|
|
241
224
|
|
|
242
|
-
const
|
|
243
|
-
existing?.
|
|
225
|
+
const avgConnectionTime = mergeAverage(
|
|
226
|
+
existing?.avgConnectionTime,
|
|
244
227
|
metadata?.connectionTimeMs,
|
|
245
228
|
);
|
|
246
229
|
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
metadata?.connected,
|
|
250
|
-
);
|
|
230
|
+
const isSuccess = metadata?.connected ?? false;
|
|
231
|
+
const successRate = mergeRate(existing?.successRate, isSuccess);
|
|
251
232
|
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
metadata?.error !== undefined,
|
|
255
|
-
);
|
|
233
|
+
const hasError = metadata?.error !== undefined;
|
|
234
|
+
const errorCount = mergeCounter(existing?.errorCount, hasError);
|
|
256
235
|
|
|
257
|
-
return {
|
|
258
|
-
avgConnectionTime: connectionTimeState.avg,
|
|
259
|
-
successRate: successState.rate,
|
|
260
|
-
errorCount: errorState.count,
|
|
261
|
-
_connectionTime: connectionTimeState,
|
|
262
|
-
_success: successState,
|
|
263
|
-
_errors: errorState,
|
|
264
|
-
};
|
|
236
|
+
return { avgConnectionTime, successRate, errorCount };
|
|
265
237
|
}
|
|
266
238
|
|
|
267
239
|
async createClient(
|