@checkstack/healthcheck-frontend 0.0.2

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/src/slots.tsx ADDED
@@ -0,0 +1,185 @@
1
+ import {
2
+ createSlot,
3
+ createSlotExtension,
4
+ } from "@checkstack/frontend-api";
5
+ import type { PluginMetadata } from "@checkstack/common";
6
+ import type {
7
+ HealthCheckRun,
8
+ AggregatedBucket,
9
+ } from "@checkstack/healthcheck-common";
10
+
11
+ // =============================================================================
12
+ // TYPE DEFINITIONS
13
+ // =============================================================================
14
+
15
+ /**
16
+ * Extends HealthCheckRun with typed result instead of Record<string, unknown>.
17
+ */
18
+ export type TypedHealthCheckRun<TResult> = Omit<HealthCheckRun, "result"> & {
19
+ result: TResult;
20
+ };
21
+
22
+ /**
23
+ * Extends AggregatedBucket with typed aggregatedResult.
24
+ */
25
+ export type TypedAggregatedBucket<TAggregatedResult> = Omit<
26
+ AggregatedBucket,
27
+ "aggregatedResult"
28
+ > & {
29
+ aggregatedResult?: TAggregatedResult;
30
+ };
31
+
32
+ /**
33
+ * Context for raw per-run data visualization.
34
+ */
35
+ export interface RawDiagramContext<TResult = unknown> {
36
+ type: "raw";
37
+ systemId: string;
38
+ configurationId: string;
39
+ strategyId: string;
40
+ runs: TypedHealthCheckRun<TResult>[];
41
+ }
42
+
43
+ /**
44
+ * Context for aggregated bucket data visualization.
45
+ */
46
+ export interface AggregatedDiagramContext<TAggregatedResult = unknown> {
47
+ type: "aggregated";
48
+ systemId: string;
49
+ configurationId: string;
50
+ strategyId: string;
51
+ buckets: TypedAggregatedBucket<TAggregatedResult>[];
52
+ }
53
+
54
+ /**
55
+ * Discriminated union context for diagram slots.
56
+ * Platform provides either raw runs or aggregated buckets based on date range.
57
+ */
58
+ export type HealthCheckDiagramSlotContext =
59
+ | RawDiagramContext
60
+ | AggregatedDiagramContext;
61
+
62
+ // =============================================================================
63
+ // SLOT DEFINITION
64
+ // =============================================================================
65
+
66
+ /**
67
+ * Extension slot for custom health check diagrams.
68
+ * Strategy plugins can contribute their own visualizations for check results.
69
+ */
70
+ export const HealthCheckDiagramSlot = createSlot<HealthCheckDiagramSlotContext>(
71
+ "healthcheck.diagram"
72
+ );
73
+
74
+ // =============================================================================
75
+ // FALLBACK COMPONENT
76
+ // =============================================================================
77
+
78
+ /**
79
+ * Fallback shown when a strategy doesn't provide an aggregated component.
80
+ */
81
+ function AggregatedFallback() {
82
+ return (
83
+ <div className="text-sm text-muted-foreground p-4 text-center border border-dashed rounded-md">
84
+ Strategy does not support aggregated visualization.
85
+ <br />
86
+ Select a shorter time range for detailed per-run data.
87
+ </div>
88
+ );
89
+ }
90
+
91
+ // =============================================================================
92
+ // DIAGRAM EXTENSION FACTORY
93
+ // =============================================================================
94
+
95
+ /**
96
+ * Factory that creates a pre-typed diagram extension helper for a strategy.
97
+ * Strategies call this once in their common package to get a typed helper.
98
+ *
99
+ * @example
100
+ * ```tsx
101
+ * // In @checkstack/healthcheck-http-common
102
+ * export const createHttpDiagramExtension = createDiagramExtensionFactory<
103
+ * HttpResult,
104
+ * HttpAggregatedResult
105
+ * >(httpCheckMetadata);
106
+ *
107
+ * // In @checkstack/healthcheck-http-frontend
108
+ * createHttpDiagramExtension({
109
+ * id: "http-check.response-chart",
110
+ * rawComponent: HttpRunsChart,
111
+ * aggregatedComponent: HttpAggregatedChart, // optional
112
+ * });
113
+ * ```
114
+ */
115
+ export function createDiagramExtensionFactory<
116
+ TResult = unknown,
117
+ TAggregatedResult = unknown
118
+ >(strategyMetadata: PluginMetadata) {
119
+ return function createDiagramExtension(options: {
120
+ id: string;
121
+ /** Component for raw per-run data (required) */
122
+ rawComponent: React.ComponentType<RawDiagramContext<TResult>>;
123
+ /** Component for aggregated bucket data (optional) */
124
+ aggregatedComponent?: React.ComponentType<
125
+ AggregatedDiagramContext<TAggregatedResult>
126
+ >;
127
+ }) {
128
+ return createSlotExtension(HealthCheckDiagramSlot, {
129
+ id: options.id,
130
+ component: (ctx: HealthCheckDiagramSlotContext) => {
131
+ // Only render for matching strategy
132
+ if (ctx.strategyId !== strategyMetadata.pluginId) {
133
+ return;
134
+ }
135
+
136
+ if (ctx.type === "raw") {
137
+ const RawComponent = options.rawComponent;
138
+ return <RawComponent {...(ctx as RawDiagramContext<TResult>)} />;
139
+ }
140
+
141
+ if (options.aggregatedComponent) {
142
+ const AggComponent = options.aggregatedComponent;
143
+ return (
144
+ <AggComponent
145
+ {...(ctx as AggregatedDiagramContext<TAggregatedResult>)}
146
+ />
147
+ );
148
+ }
149
+
150
+ // Fallback if no aggregated component provided
151
+ return <AggregatedFallback />;
152
+ },
153
+ });
154
+ };
155
+ }
156
+
157
+ // =============================================================================
158
+ // LEGACY API (for backwards compatibility)
159
+ // =============================================================================
160
+
161
+ /**
162
+ * @deprecated Use createDiagramExtensionFactory instead for typed metadata.
163
+ *
164
+ * Legacy helper for creating strategy-specific diagram extensions.
165
+ * Wraps the component with strategy ID filtering.
166
+ */
167
+ export function createStrategyDiagramExtension(options: {
168
+ id: string;
169
+ forStrategies: PluginMetadata | PluginMetadata[];
170
+ component: React.ComponentType<HealthCheckDiagramSlotContext>;
171
+ }) {
172
+ const strategyIds = Array.isArray(options.forStrategies)
173
+ ? options.forStrategies.map((m) => m.pluginId)
174
+ : [options.forStrategies.pluginId];
175
+
176
+ return createSlotExtension(HealthCheckDiagramSlot, {
177
+ id: options.id,
178
+ component: (ctx: HealthCheckDiagramSlotContext) => {
179
+ if (!strategyIds.includes(ctx.strategyId)) {
180
+ return;
181
+ }
182
+ return <options.component {...ctx} />;
183
+ },
184
+ });
185
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "@checkstack/tsconfig/frontend.json",
3
+ "include": [
4
+ "src"
5
+ ]
6
+ }