@checkstack/healthcheck-postgres-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-postgres-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-postgres-backend",
3
- "version": "0.1.14",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {
@@ -89,8 +89,8 @@ describe("QueryCollector", () => {
89
89
  let aggregated = collector.mergeResult(undefined, runs[0]);
90
90
  aggregated = collector.mergeResult(aggregated, runs[1]);
91
91
 
92
- expect(aggregated.avgExecutionTimeMs).toBe(75);
93
- expect(aggregated.successRate).toBe(100);
92
+ expect(aggregated.avgExecutionTimeMs.avg).toBe(75);
93
+ expect(aggregated.successRate.rate).toBe(100);
94
94
  });
95
95
 
96
96
  it("should calculate success rate correctly", () => {
@@ -117,7 +117,7 @@ describe("QueryCollector", () => {
117
117
  let aggregated = collector.mergeResult(undefined, runs[0]);
118
118
  aggregated = collector.mergeResult(aggregated, runs[1]);
119
119
 
120
- expect(aggregated.successRate).toBe(50);
120
+ expect(aggregated.successRate.rate).toBe(50);
121
121
  });
122
122
  });
123
123
 
@@ -6,10 +6,10 @@ import {
6
6
  type CollectorStrategy,
7
7
  mergeAverage,
8
8
  mergeRate,
9
- averageStateSchema,
10
- rateStateSchema,
11
- type AverageState,
12
- type RateState,
9
+ VersionedAggregated,
10
+ aggregatedAverage,
11
+ aggregatedRate,
12
+ type InferAggregatedResult,
13
13
  } from "@checkstack/backend-api";
14
14
  import {
15
15
  healthResultNumber,
@@ -51,29 +51,24 @@ const queryResultSchema = healthResultSchema({
51
51
 
52
52
  export type QueryResult = z.infer<typeof queryResultSchema>;
53
53
 
54
- const queryAggregatedDisplaySchema = healthResultSchema({
55
- avgExecutionTimeMs: healthResultNumber({
54
+ // Aggregated result fields definition
55
+ const queryAggregatedFields = {
56
+ avgExecutionTimeMs: aggregatedAverage({
56
57
  "x-chart-type": "line",
57
58
  "x-chart-label": "Avg Execution Time",
58
59
  "x-chart-unit": "ms",
59
60
  }),
60
- successRate: healthResultNumber({
61
+ successRate: aggregatedRate({
61
62
  "x-chart-type": "gauge",
62
63
  "x-chart-label": "Success Rate",
63
64
  "x-chart-unit": "%",
64
65
  }),
65
- });
66
-
67
- const queryAggregatedInternalSchema = z.object({
68
- _executionTime: averageStateSchema.optional(),
69
- _success: rateStateSchema.optional(),
70
- });
66
+ };
71
67
 
72
- const queryAggregatedSchema = queryAggregatedDisplaySchema.merge(
73
- queryAggregatedInternalSchema,
74
- );
75
-
76
- export type QueryAggregatedResult = z.infer<typeof queryAggregatedSchema>;
68
+ // Type inferred from field definitions
69
+ export type QueryAggregatedResult = InferAggregatedResult<
70
+ typeof queryAggregatedFields
71
+ >;
77
72
 
78
73
  // ============================================================================
79
74
  // QUERY COLLECTOR
@@ -99,9 +94,9 @@ export class QueryCollector implements CollectorStrategy<
99
94
 
100
95
  config = new Versioned({ version: 1, schema: queryConfigSchema });
101
96
  result = new Versioned({ version: 1, schema: queryResultSchema });
102
- aggregatedResult = new Versioned({
97
+ aggregatedResult = new VersionedAggregated({
103
98
  version: 1,
104
- schema: queryAggregatedSchema,
99
+ fields: queryAggregatedFields,
105
100
  });
106
101
 
107
102
  async execute({
@@ -133,21 +128,12 @@ export class QueryCollector implements CollectorStrategy<
133
128
  ): QueryAggregatedResult {
134
129
  const metadata = run.metadata;
135
130
 
136
- const executionTimeState = mergeAverage(
137
- existing?._executionTime as AverageState | undefined,
138
- metadata?.executionTimeMs,
139
- );
140
-
141
- const successState = mergeRate(
142
- existing?._success as RateState | undefined,
143
- metadata?.success,
144
- );
145
-
146
131
  return {
147
- avgExecutionTimeMs: executionTimeState.avg,
148
- successRate: successState.rate,
149
- _executionTime: executionTimeState,
150
- _success: successState,
132
+ avgExecutionTimeMs: mergeAverage(
133
+ existing?.avgExecutionTimeMs,
134
+ metadata?.executionTimeMs,
135
+ ),
136
+ successRate: mergeRate(existing?.successRate, metadata?.success),
151
137
  };
152
138
  }
153
139
  }
@@ -162,9 +162,9 @@ describe("PostgresHealthCheckStrategy", () => {
162
162
  let aggregated = strategy.mergeResult(undefined, runs[0]);
163
163
  aggregated = strategy.mergeResult(aggregated, runs[1]);
164
164
 
165
- expect(aggregated.avgConnectionTime).toBe(75);
166
- expect(aggregated.successRate).toBe(100);
167
- expect(aggregated.errorCount).toBe(0);
165
+ expect(aggregated.avgConnectionTime.avg).toBe(75);
166
+ expect(aggregated.successRate.rate).toBe(100);
167
+ expect(aggregated.errorCount.count).toBe(0);
168
168
  });
169
169
 
170
170
  it("should count errors", () => {
@@ -184,8 +184,8 @@ describe("PostgresHealthCheckStrategy", () => {
184
184
 
185
185
  const aggregated = strategy.mergeResult(undefined, run);
186
186
 
187
- expect(aggregated.errorCount).toBe(1);
188
- expect(aggregated.successRate).toBe(0);
187
+ expect(aggregated.errorCount.count).toBe(1);
188
+ expect(aggregated.successRate.rate).toBe(0);
189
189
  });
190
190
  });
191
191
  });
package/src/strategy.ts CHANGED
@@ -3,23 +3,21 @@ import {
3
3
  HealthCheckStrategy,
4
4
  HealthCheckRunForAggregation,
5
5
  Versioned,
6
+ VersionedAggregated,
7
+ aggregatedAverage,
8
+ aggregatedMinMax,
9
+ aggregatedRate,
10
+ aggregatedCounter,
11
+ mergeAverage,
12
+ mergeRate,
13
+ mergeCounter,
14
+ mergeMinMax,
6
15
  z,
7
16
  configString,
8
17
  configNumber,
9
18
  configBoolean,
10
19
  type ConnectedClient,
11
- mergeAverage,
12
- mergeRate,
13
- mergeCounter,
14
- mergeMinMax,
15
- averageStateSchema,
16
- minMaxStateSchema,
17
- rateStateSchema,
18
- counterStateSchema,
19
- type AverageState,
20
- type RateState,
21
- type CounterState,
22
- type MinMaxState,
20
+ type InferAggregatedResult,
23
21
  } from "@checkstack/backend-api";
24
22
  import {
25
23
  healthResultBoolean,
@@ -82,45 +80,32 @@ const postgresResultSchema = healthResultSchema({
82
80
 
83
81
  type PostgresResult = z.infer<typeof postgresResultSchema>;
84
82
 
85
- /**
86
- * Aggregated metadata for buckets.
87
- */
88
- // UI-visible aggregated fields (for charts)
89
- const postgresAggregatedDisplaySchema = healthResultSchema({
90
- avgConnectionTime: healthResultNumber({
83
+ /** Aggregated field definitions for bucket merging */
84
+ const postgresAggregatedFields = {
85
+ avgConnectionTime: aggregatedAverage({
91
86
  "x-chart-type": "line",
92
87
  "x-chart-label": "Avg Connection Time",
93
88
  "x-chart-unit": "ms",
94
89
  }),
95
- maxConnectionTime: healthResultNumber({
90
+ maxConnectionTime: aggregatedMinMax({
96
91
  "x-chart-type": "line",
97
92
  "x-chart-label": "Max Connection Time",
98
93
  "x-chart-unit": "ms",
99
94
  }),
100
- successRate: healthResultNumber({
95
+ successRate: aggregatedRate({
101
96
  "x-chart-type": "gauge",
102
97
  "x-chart-label": "Success Rate",
103
98
  "x-chart-unit": "%",
104
99
  }),
105
- errorCount: healthResultNumber({
100
+ errorCount: aggregatedCounter({
106
101
  "x-chart-type": "counter",
107
102
  "x-chart-label": "Errors",
108
103
  }),
109
- });
110
-
111
- // Internal state for incremental aggregation
112
- const postgresAggregatedInternalSchema = z.object({
113
- _connectionTime: averageStateSchema.optional(),
114
- _maxConnectionTime: minMaxStateSchema.optional(),
115
- _success: rateStateSchema.optional(),
116
- _errors: counterStateSchema.optional(),
117
- });
118
-
119
- const postgresAggregatedSchema = postgresAggregatedDisplaySchema.merge(
120
- postgresAggregatedInternalSchema,
121
- );
104
+ };
122
105
 
123
- type PostgresAggregatedResult = z.infer<typeof postgresAggregatedSchema>;
106
+ type PostgresAggregatedResult = InferAggregatedResult<
107
+ typeof postgresAggregatedFields
108
+ >;
124
109
 
125
110
  // ============================================================================
126
111
  // DATABASE CLIENT INTERFACE (for testability)
@@ -162,7 +147,7 @@ export class PostgresHealthCheckStrategy implements HealthCheckStrategy<
162
147
  PostgresConfig,
163
148
  PostgresTransportClient,
164
149
  PostgresResult,
165
- PostgresAggregatedResult
150
+ typeof postgresAggregatedFields
166
151
  > {
167
152
  id = "postgres";
168
153
  displayName = "PostgreSQL Health Check";
@@ -200,9 +185,9 @@ export class PostgresHealthCheckStrategy implements HealthCheckStrategy<
200
185
  ],
201
186
  });
202
187
 
203
- aggregatedResult: Versioned<PostgresAggregatedResult> = new Versioned({
188
+ aggregatedResult = new VersionedAggregated({
204
189
  version: 1,
205
- schema: postgresAggregatedSchema,
190
+ fields: postgresAggregatedFields,
206
191
  });
207
192
 
208
193
  mergeResult(
@@ -211,40 +196,23 @@ export class PostgresHealthCheckStrategy implements HealthCheckStrategy<
211
196
  ): PostgresAggregatedResult {
212
197
  const metadata = run.metadata;
213
198
 
214
- // Merge connection time average
215
- const connectionTimeState = mergeAverage(
216
- existing?._connectionTime as AverageState | undefined,
199
+ const avgConnectionTime = mergeAverage(
200
+ existing?.avgConnectionTime,
217
201
  metadata?.connectionTimeMs,
218
202
  );
219
203
 
220
- // Merge max connection time
221
- const maxConnectionTimeState = mergeMinMax(
222
- existing?._maxConnectionTime as MinMaxState | undefined,
204
+ const maxConnectionTime = mergeMinMax(
205
+ existing?.maxConnectionTime,
223
206
  metadata?.connectionTimeMs,
224
207
  );
225
208
 
226
- // Merge success rate
227
- const successState = mergeRate(
228
- existing?._success as RateState | undefined,
229
- metadata?.connected,
230
- );
209
+ const isSuccess = metadata?.connected ?? false;
210
+ const successRate = mergeRate(existing?.successRate, isSuccess);
231
211
 
232
- // Merge error count
233
- const errorState = mergeCounter(
234
- existing?._errors as CounterState | undefined,
235
- metadata?.error !== undefined,
236
- );
212
+ const hasError = metadata?.error !== undefined;
213
+ const errorCount = mergeCounter(existing?.errorCount, hasError);
237
214
 
238
- return {
239
- avgConnectionTime: connectionTimeState.avg,
240
- maxConnectionTime: maxConnectionTimeState.max,
241
- successRate: successState.rate,
242
- errorCount: errorState.count,
243
- _connectionTime: connectionTimeState,
244
- _maxConnectionTime: maxConnectionTimeState,
245
- _success: successState,
246
- _errors: errorState,
247
- };
215
+ return { avgConnectionTime, maxConnectionTime, successRate, errorCount };
248
216
  }
249
217
 
250
218
  async createClient(