@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/healthcheck-ping-backend",
3
- "version": "0.1.14",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {
@@ -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
 
@@ -5,8 +5,9 @@ import {
5
5
  type CollectorResult,
6
6
  type CollectorStrategy,
7
7
  mergeAverage,
8
- averageStateSchema,
9
- type AverageState,
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
- const pingAggregatedDisplaySchema = healthResultSchema({
78
- avgPacketLoss: healthResultNumber({
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: healthResultNumber({
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
- const pingAggregatedSchema = pingAggregatedDisplaySchema.merge(
96
- pingAggregatedInternalSchema,
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 Versioned({
121
+ aggregatedResult = new VersionedAggregated({
126
122
  version: 1,
127
- schema: pingAggregatedSchema,
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: Math.round(lossState.avg * 10) / 10,
175
- avgLatency: Math.round(latencyState.avg * 10) / 10,
176
- _packetLoss: lossState,
177
- _latency: latencyState,
160
+ avgPacketLoss: mergeAverage(
161
+ existing?.avgPacketLoss,
162
+ metadata?.packetLoss,
163
+ ),
164
+ avgLatency: mergeAverage(existing?.avgLatency, metadata?.avgLatency),
178
165
  };
179
166
  }
180
167
  }
@@ -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
- z,
6
- type ConnectedClient,
5
+ VersionedAggregated,
6
+ aggregatedAverage,
7
+ aggregatedMinMax,
8
+ aggregatedCounter,
7
9
  mergeAverage,
8
- averageStateSchema,
9
10
  mergeCounter,
10
- counterStateSchema,
11
11
  mergeMinMax,
12
- minMaxStateSchema,
13
- type AverageState,
14
- type CounterState,
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
- * Aggregated metadata for buckets.
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: healthResultNumber({
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: healthResultNumber({
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: healthResultNumber({
109
+ errorCount: aggregatedCounter({
113
110
  "x-chart-type": "counter",
114
111
  "x-chart-label": "Errors",
115
112
  }),
116
- });
113
+ };
117
114
 
118
- const pingAggregatedInternalSchema = z.object({
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
- PingAggregatedResult
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: Versioned<PingAggregatedResult> = new Versioned({
159
+ aggregatedResult = new VersionedAggregated({
174
160
  version: 1,
175
- schema: pingAggregatedSchema,
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 packetLossState = mergeAverage(
185
- existing?._packetLoss as AverageState | undefined,
170
+ const avgPacketLoss = mergeAverage(
171
+ existing?.avgPacketLoss,
186
172
  metadata?.packetLoss,
187
173
  );
188
174
 
189
- const latencyState = mergeAverage(
190
- existing?._latency as AverageState | undefined,
191
- metadata?.avgLatency,
192
- );
175
+ const avgLatency = mergeAverage(existing?.avgLatency, metadata?.avgLatency);
193
176
 
194
- const maxLatencyState = mergeMinMax(
195
- existing?._maxLatency as MinMaxState | undefined,
196
- metadata?.maxLatency,
197
- );
177
+ const maxLatency = mergeMinMax(existing?.maxLatency, metadata?.maxLatency);
198
178
 
199
- const errorState = mergeCounter(
200
- existing?._errors as CounterState | undefined,
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(