@checkstack/healthcheck-backend 0.0.2 → 0.0.3
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 +25 -0
- package/package.json +1 -1
- package/src/router.ts +9 -4
- package/src/schema-utils.ts +83 -0
- package/src/service.ts +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @checkstack/healthcheck-backend
|
|
2
2
|
|
|
3
|
+
## 0.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- cb82e4d: Improved `counter` and `pie` auto-chart types to show frequency distributions instead of just the latest value. Both chart types now count occurrences of each unique value across all runs/buckets, making them more intuitive for visualizing data like HTTP status codes.
|
|
8
|
+
|
|
9
|
+
Changed HTTP health check chart annotations: `statusCode` now uses `pie` chart (distribution view), `contentType` now uses `counter` chart (frequency count).
|
|
10
|
+
|
|
11
|
+
Fixed scrollbar hopping when health check signals update the accordion content. All charts now update silently without layout shift or loading state flicker.
|
|
12
|
+
|
|
13
|
+
Refactored health check visualization architecture:
|
|
14
|
+
|
|
15
|
+
- `HealthCheckStatusTimeline` and `HealthCheckLatencyChart` now accept `HealthCheckDiagramSlotContext` directly, handling data transformation internally
|
|
16
|
+
- `HealthCheckDiagram` refactored to accept context from parent, ensuring all visualizations share the same data source and update together on signals
|
|
17
|
+
- `HealthCheckSystemOverview` simplified to use `useHealthCheckData` hook for consolidated data fetching with automatic signal-driven refresh
|
|
18
|
+
|
|
19
|
+
Added `silentRefetch()` method to `usePagination` hook for background data refreshes without showing loading indicators.
|
|
20
|
+
|
|
21
|
+
Fixed `useSignal` hook to use a ref pattern internally, preventing stale closure issues. Callbacks now always access the latest values without requiring manual memoization or refs in consumer components.
|
|
22
|
+
|
|
23
|
+
Added signal handling to `useHealthCheckData` hook for automatic chart refresh when health check runs complete.
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [cb82e4d]
|
|
26
|
+
- @checkstack/healthcheck-common@0.0.3
|
|
27
|
+
|
|
3
28
|
## 0.0.2
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/package.json
CHANGED
package/src/router.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { implement, ORPCError } from "@orpc/server";
|
|
2
2
|
import {
|
|
3
3
|
autoAuthMiddleware,
|
|
4
|
-
|
|
4
|
+
toJsonSchema,
|
|
5
5
|
type RpcContext,
|
|
6
6
|
type HealthCheckRegistry,
|
|
7
7
|
} from "@checkstack/backend-api";
|
|
@@ -9,6 +9,7 @@ import { healthCheckContract } from "@checkstack/healthcheck-common";
|
|
|
9
9
|
import { HealthCheckService } from "./service";
|
|
10
10
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
11
11
|
import * as schema from "./schema";
|
|
12
|
+
import { toJsonSchemaWithChartMeta } from "./schema-utils";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Creates the healthcheck router using contract-based implementation.
|
|
@@ -34,9 +35,13 @@ export const createHealthCheckRouter = (
|
|
|
34
35
|
id: s.id,
|
|
35
36
|
displayName: s.displayName,
|
|
36
37
|
description: s.description,
|
|
37
|
-
configSchema:
|
|
38
|
-
resultSchema: s.result
|
|
39
|
-
|
|
38
|
+
configSchema: toJsonSchema(s.config.schema),
|
|
39
|
+
resultSchema: s.result
|
|
40
|
+
? toJsonSchemaWithChartMeta(s.result.schema)
|
|
41
|
+
: undefined,
|
|
42
|
+
aggregatedResultSchema: toJsonSchemaWithChartMeta(
|
|
43
|
+
s.aggregatedResult.schema
|
|
44
|
+
),
|
|
40
45
|
}));
|
|
41
46
|
}),
|
|
42
47
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema utilities for health check JSON Schema conversion.
|
|
3
|
+
*
|
|
4
|
+
* Extends the base toJsonSchema to also include chart metadata from the
|
|
5
|
+
* healthResultRegistry for auto-chart rendering.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z, toJsonSchema } from "@checkstack/backend-api";
|
|
9
|
+
import { getHealthResultMeta } from "@checkstack/healthcheck-common";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Adds health result chart metadata to JSON Schema properties.
|
|
13
|
+
* Recursively processes nested objects and arrays.
|
|
14
|
+
*/
|
|
15
|
+
function addHealthResultMeta(
|
|
16
|
+
zodSchema: z.ZodTypeAny,
|
|
17
|
+
jsonSchema: Record<string, unknown>
|
|
18
|
+
): void {
|
|
19
|
+
// Handle arrays - recurse into items
|
|
20
|
+
if (zodSchema instanceof z.ZodArray) {
|
|
21
|
+
const itemsSchema = (zodSchema as z.ZodArray<z.ZodTypeAny>).element;
|
|
22
|
+
const jsonItems = jsonSchema.items as Record<string, unknown> | undefined;
|
|
23
|
+
if (jsonItems) {
|
|
24
|
+
addHealthResultMeta(itemsSchema, jsonItems);
|
|
25
|
+
}
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Handle optional - unwrap and recurse
|
|
30
|
+
if (zodSchema instanceof z.ZodOptional) {
|
|
31
|
+
const innerSchema = zodSchema.unwrap() as z.ZodTypeAny;
|
|
32
|
+
addHealthResultMeta(innerSchema, jsonSchema);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Type guard to check if this is an object schema
|
|
37
|
+
if (!("shape" in zodSchema)) return;
|
|
38
|
+
|
|
39
|
+
const objectSchema = zodSchema as z.ZodObject<z.ZodRawShape>;
|
|
40
|
+
const properties = jsonSchema.properties as
|
|
41
|
+
| Record<string, Record<string, unknown>>
|
|
42
|
+
| undefined;
|
|
43
|
+
|
|
44
|
+
if (!properties) return;
|
|
45
|
+
|
|
46
|
+
for (const [key, fieldSchema] of Object.entries(objectSchema.shape)) {
|
|
47
|
+
const zodField = fieldSchema as z.ZodTypeAny;
|
|
48
|
+
const jsonField = properties[key];
|
|
49
|
+
|
|
50
|
+
if (!jsonField) continue;
|
|
51
|
+
|
|
52
|
+
// Get health result metadata from registry (x-chart-type, etc.)
|
|
53
|
+
const healthMeta = getHealthResultMeta(zodField);
|
|
54
|
+
if (healthMeta) {
|
|
55
|
+
if (healthMeta["x-chart-type"])
|
|
56
|
+
jsonField["x-chart-type"] = healthMeta["x-chart-type"];
|
|
57
|
+
if (healthMeta["x-chart-label"])
|
|
58
|
+
jsonField["x-chart-label"] = healthMeta["x-chart-label"];
|
|
59
|
+
if (healthMeta["x-chart-unit"])
|
|
60
|
+
jsonField["x-chart-unit"] = healthMeta["x-chart-unit"];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Recurse into nested objects and arrays
|
|
64
|
+
addHealthResultMeta(zodField, jsonField);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Converts a Zod schema to JSON Schema with chart metadata.
|
|
70
|
+
*
|
|
71
|
+
* This extends the base toJsonSchema to also include health check
|
|
72
|
+
* chart metadata (x-chart-type, x-chart-label, x-chart-unit) from
|
|
73
|
+
* the healthResultRegistry for auto-chart rendering.
|
|
74
|
+
*/
|
|
75
|
+
export function toJsonSchemaWithChartMeta(
|
|
76
|
+
zodSchema: z.ZodTypeAny
|
|
77
|
+
): Record<string, unknown> {
|
|
78
|
+
// Use the base toJsonSchema which handles config metadata
|
|
79
|
+
const jsonSchema = toJsonSchema(zodSchema);
|
|
80
|
+
// Add health result chart metadata
|
|
81
|
+
addHealthResultMeta(zodSchema, jsonSchema);
|
|
82
|
+
return jsonSchema;
|
|
83
|
+
}
|
package/src/service.ts
CHANGED
|
@@ -614,10 +614,15 @@ export class HealthCheckService {
|
|
|
614
614
|
if (!bucketMap.has(key)) {
|
|
615
615
|
bucketMap.set(key, { bucketStart, runs: [] });
|
|
616
616
|
}
|
|
617
|
+
// run.result is StoredHealthCheckResult: { status, latencyMs, message, metadata }
|
|
618
|
+
// Strategy's aggregateResult expects metadata to be the strategy-specific fields
|
|
619
|
+
const storedResult = run.result as {
|
|
620
|
+
metadata?: Record<string, unknown>;
|
|
621
|
+
} | null;
|
|
617
622
|
bucketMap.get(key)!.runs.push({
|
|
618
623
|
status: run.status,
|
|
619
624
|
latencyMs: run.latencyMs ?? undefined,
|
|
620
|
-
metadata:
|
|
625
|
+
metadata: storedResult?.metadata ?? undefined,
|
|
621
626
|
});
|
|
622
627
|
}
|
|
623
628
|
|
|
@@ -637,7 +642,7 @@ export class HealthCheckService {
|
|
|
637
642
|
|
|
638
643
|
const latencies = bucket.runs
|
|
639
644
|
.map((r) => r.latencyMs)
|
|
640
|
-
.filter((l): l is number => l
|
|
645
|
+
.filter((l): l is number => typeof l === "number");
|
|
641
646
|
const avgLatencyMs =
|
|
642
647
|
latencies.length > 0
|
|
643
648
|
? Math.round(latencies.reduce((a, b) => a + b, 0) / latencies.length)
|