@checkstack/healthcheck-frontend 0.12.0 → 0.12.1

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,45 @@
1
1
  # @checkstack/healthcheck-frontend
2
2
 
3
+ ## 0.12.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d1a2796: Enforce stricter code quality standards and eliminate AI slop anti-patterns.
8
+
9
+ **New utility**
10
+
11
+ - `extractErrorMessage(error, fallback?)` in `@checkstack/common` for consistent error extraction
12
+
13
+ **ESLint rules**
14
+
15
+ - `react-hooks/rules-of-hooks` and `exhaustive-deps` for hook correctness
16
+ - `no-console` in frontend packages — forces `toast` over silent `console.error`
17
+ - `no-restricted-syntax` banning `instanceof Error` — forces `extractErrorMessage`
18
+ - Custom `no-eslint-disable-any` rule preventing `@typescript-eslint/no-explicit-any` circumvention
19
+
20
+ **Refactoring**
21
+
22
+ - Replace 141 `instanceof Error` boilerplate patterns across the codebase
23
+ - Replace swallowed `console.error` with user-visible `toast.error()` feedback
24
+ - Remove 15 redundant `as` type casts in IntegrationsPage and ProviderConnectionsPage
25
+ - Consolidate 3 identical callback handlers into `handleDialogClose`
26
+ - Fix conditional React hook call in `FormField.tsx`
27
+ - Fix unstable useMemo deps in `Dashboard.tsx`
28
+ - Replace `useEffect`→`setState` with derived `useMemo` in `RegisterPage.tsx`
29
+ - Rewrite `keystore.test.ts` with typed `DrizzleMockChain` (eliminating 7 `any` suppressions)
30
+ - Delete obvious comments in `encryption.ts` and Teams `provider.ts`
31
+
32
+ - Updated dependencies [d1a2796]
33
+ - Updated dependencies [3c34b07]
34
+ - @checkstack/common@0.6.5
35
+ - @checkstack/ui@1.2.1
36
+ - @checkstack/auth-frontend@0.5.18
37
+ - @checkstack/dashboard-frontend@0.3.26
38
+ - @checkstack/frontend-api@0.3.9
39
+ - @checkstack/catalog-common@1.3.1
40
+ - @checkstack/healthcheck-common@0.10.1
41
+ - @checkstack/signal-frontend@0.0.15
42
+
3
43
  ## 0.12.0
4
44
 
5
45
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/healthcheck-frontend",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "type": "module",
5
5
  "main": "src/index.tsx",
6
6
  "checkstack": {
@@ -13,11 +13,11 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@checkstack/auth-frontend": "0.5.17",
16
- "@checkstack/catalog-common": "1.2.11",
16
+ "@checkstack/catalog-common": "1.3.0",
17
17
  "@checkstack/common": "0.6.4",
18
- "@checkstack/dashboard-frontend": "0.3.23",
18
+ "@checkstack/dashboard-frontend": "0.3.25",
19
19
  "@checkstack/frontend-api": "0.3.8",
20
- "@checkstack/healthcheck-common": "0.8.4",
20
+ "@checkstack/healthcheck-common": "0.10.0",
21
21
  "@checkstack/signal-frontend": "0.0.14",
22
22
  "@checkstack/ui": "1.2.0",
23
23
  "ajv": "^8.18.0",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "@checkstack/scripts": "0.1.2",
35
- "@checkstack/tsconfig": "0.0.4",
35
+ "@checkstack/tsconfig": "0.0.5",
36
36
  "@types/react": "^18.2.0",
37
37
  "typescript": "^5.0.0"
38
38
  }
@@ -230,10 +230,6 @@ function extractComputedValue(value: unknown): unknown {
230
230
 
231
231
  // _type is required for all aggregated state objects
232
232
  if (!("_type" in obj)) {
233
- console.error(
234
- "[AutoChart] Missing _type discriminator in aggregated state:",
235
- obj,
236
- );
237
233
  return value;
238
234
  }
239
235
 
@@ -252,9 +248,6 @@ function extractComputedValue(value: unknown): unknown {
252
248
  return obj.max;
253
249
  }
254
250
  default: {
255
- console.error(
256
- `[AutoChart] Unrecognized aggregated state type: ${String(obj._type)}`,
257
- );
258
251
  return value;
259
252
  }
260
253
  }
@@ -36,7 +36,7 @@ import {
36
36
  CardContent,
37
37
  CardHeader,
38
38
  CardTitle,
39
- AnimatedNumber,
39
+
40
40
  } from "@checkstack/ui";
41
41
  import { formatDistanceToNow } from "date-fns";
42
42
  import { ChevronDown, ChevronRight } from "lucide-react";
@@ -71,22 +71,6 @@ interface ExpandedRowProps {
71
71
  systemId: string;
72
72
  }
73
73
 
74
- // Helper to get color class for availability percentage
75
- const getAvailabilityColorClass = (
76
- value: number | null,
77
- totalRuns: number,
78
- ): string => {
79
- if (value === null || totalRuns === 0) {
80
- return "text-muted-foreground";
81
- }
82
- if (value >= 99.9) {
83
- return "text-green-600 dark:text-green-400";
84
- }
85
- if (value >= 99) {
86
- return "text-yellow-600 dark:text-yellow-400";
87
- }
88
- return "text-red-600 dark:text-red-400";
89
- };
90
74
 
91
75
  const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
92
76
  const healthCheckClient = usePluginClient(HealthCheckApi);
@@ -193,12 +177,11 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
193
177
  }
194
178
  const runs = displayRuns;
195
179
 
196
- // Listen for realtime health check updates to refresh history table and availability stats
180
+ // Listen for realtime health check updates to refresh history table
197
181
  // Charts are refreshed automatically by useHealthCheckData
198
182
  useSignal(HEALTH_CHECK_RUN_COMPLETED, ({ systemId: changedId }) => {
199
183
  if (changedId === systemId) {
200
184
  void refetch();
201
- void refetchAvailability();
202
185
  }
203
186
  });
204
187
 
@@ -208,12 +191,6 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
208
191
  : `Window mode (${item.stateThresholds.windowSize} runs): Degraded at ${item.stateThresholds.degraded.minFailureCount}+ failures, Unhealthy at ${item.stateThresholds.unhealthy.minFailureCount}+ failures`
209
192
  : "Using default thresholds";
210
193
 
211
- // Fetch availability stats
212
- const { data: availabilityData, refetch: refetchAvailability } =
213
- healthCheckClient.getAvailabilityStats.useQuery({
214
- systemId,
215
- configurationId: item.configurationId,
216
- });
217
194
 
218
195
  // Render charts - charts handle data transformation internally
219
196
  const renderCharts = () => {
@@ -289,48 +266,7 @@ const ExpandedDetails: React.FC<ExpandedRowProps> = ({ item, systemId }) => {
289
266
  </div>
290
267
  </div>
291
268
 
292
- {/* Availability Stats - Prominent Display */}
293
- {availabilityData && (
294
- <div className="grid grid-cols-2 gap-4">
295
- <div className="flex flex-col gap-1 p-4 rounded-lg border bg-card">
296
- <span className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
297
- 31-Day Availability
298
- </span>
299
- <div className="flex items-baseline gap-2">
300
- <AnimatedNumber
301
- value={availabilityData.availability31Days ?? undefined}
302
- suffix="%"
303
- className={`text-2xl font-bold ${getAvailabilityColorClass(availabilityData.availability31Days, availabilityData.totalRuns31Days)}`}
304
- />
305
- {availabilityData.totalRuns31Days > 0 && (
306
- <span className="text-sm text-muted-foreground">
307
- ({availabilityData.totalRuns31Days.toLocaleString()} runs)
308
- </span>
309
- )}
310
- </div>
311
- </div>
312
- <div className="flex flex-col gap-1 p-4 rounded-lg border bg-card">
313
- <span className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
314
- 365-Day Availability
315
- </span>
316
- <div className="flex items-baseline gap-2">
317
- <AnimatedNumber
318
- value={availabilityData.availability365Days ?? undefined}
319
- suffix="%"
320
- className={`text-2xl font-bold ${getAvailabilityColorClass(availabilityData.availability365Days, availabilityData.totalRuns365Days)}`}
321
- />
322
- {availabilityData.totalRuns365Days > 0 && (
323
- <span className="text-sm text-muted-foreground">
324
- ({availabilityData.totalRuns365Days.toLocaleString()} runs)
325
- </span>
326
- )}
327
- </div>
328
- </div>
329
- </div>
330
- )}
331
269
 
332
- {/* Divider */}
333
- <div className="h-px bg-border" />
334
270
 
335
271
  {/* Date Range Filter with Loading Spinner */}
336
272
  <div className="flex items-center gap-3 flex-wrap">
@@ -35,7 +35,7 @@ import {
35
35
  healthcheckRoutes,
36
36
  healthCheckAccess,
37
37
  } from "@checkstack/healthcheck-common";
38
- import { resolveRoute } from "@checkstack/common";
38
+ import { resolveRoute, extractErrorMessage} from "@checkstack/common";
39
39
  import { DEFAULT_RETENTION_CONFIG } from "@checkstack/healthcheck-common";
40
40
 
41
41
  type SelectedPanel = { configId: string; panel: "thresholds" | "retention" };
@@ -132,7 +132,7 @@ export const SystemHealthCheckAssignment: React.FC<Props> = ({
132
132
  void refetchAssociations();
133
133
  },
134
134
  onError: (error) => {
135
- toast.error(error instanceof Error ? error.message : "Failed to update");
135
+ toast.error(extractErrorMessage(error, "Failed to update"));
136
136
  },
137
137
  });
138
138
 
@@ -144,7 +144,7 @@ export const SystemHealthCheckAssignment: React.FC<Props> = ({
144
144
  },
145
145
  onError: (error) => {
146
146
  toast.error(
147
- error instanceof Error ? error.message : "Failed to update"
147
+ extractErrorMessage(error, "Failed to update")
148
148
  );
149
149
  },
150
150
  }
@@ -158,7 +158,7 @@ export const SystemHealthCheckAssignment: React.FC<Props> = ({
158
158
  setSelectedPanel(undefined);
159
159
  },
160
160
  onError: (error) => {
161
- toast.error(error instanceof Error ? error.message : "Failed to save");
161
+ toast.error(extractErrorMessage(error, "Failed to save"));
162
162
  },
163
163
  });
164
164
 
@@ -26,7 +26,7 @@ export function useCollectors(strategyId: string): UseCollectorsResult {
26
26
  );
27
27
 
28
28
  const collectors = data ?? [];
29
- const error = queryError instanceof Error ? queryError : undefined;
29
+ const error = queryError ?? undefined;
30
30
 
31
31
  return {
32
32
  collectors,
@@ -21,7 +21,7 @@ import {
21
21
  } from "@checkstack/ui";
22
22
  import { Plus, History, Activity } from "lucide-react";
23
23
  import { Link } from "react-router-dom";
24
- import { resolveRoute } from "@checkstack/common";
24
+ import { resolveRoute, extractErrorMessage} from "@checkstack/common";
25
25
  import { useState } from "react";
26
26
 
27
27
  const HealthCheckConfigPageContent = () => {
@@ -70,7 +70,7 @@ const HealthCheckConfigPageContent = () => {
70
70
  void refetchConfigurations();
71
71
  },
72
72
  onError: (error) => {
73
- toast.error(error instanceof Error ? error.message : "Failed to delete");
73
+ toast.error(extractErrorMessage(error, "Failed to delete"));
74
74
  },
75
75
  });
76
76
 
@@ -79,7 +79,7 @@ const HealthCheckConfigPageContent = () => {
79
79
  void refetchConfigurations();
80
80
  },
81
81
  onError: (error) => {
82
- toast.error(error instanceof Error ? error.message : "Failed to pause");
82
+ toast.error(extractErrorMessage(error, "Failed to pause"));
83
83
  },
84
84
  });
85
85
 
@@ -88,7 +88,7 @@ const HealthCheckConfigPageContent = () => {
88
88
  void refetchConfigurations();
89
89
  },
90
90
  onError: (error) => {
91
- toast.error(error instanceof Error ? error.message : "Failed to resume");
91
+ toast.error(extractErrorMessage(error, "Failed to resume"));
92
92
  },
93
93
  });
94
94
 
@@ -11,7 +11,7 @@ import {
11
11
  } from "@checkstack/healthcheck-common";
12
12
  import { PageLayout, Button, useToast } from "@checkstack/ui";
13
13
  import { Save, Settings } from "lucide-react";
14
- import { resolveRoute } from "@checkstack/common";
14
+ import { resolveRoute, extractErrorMessage} from "@checkstack/common";
15
15
  import { useCollectors } from "../hooks/useCollectors";
16
16
  import { EditorTree, type TreeNodeId } from "../components/editor/EditorTree";
17
17
  import { EditorPanel } from "../components/editor/EditorPanel";
@@ -249,7 +249,7 @@ const HealthCheckIDEPageContent = () => {
249
249
  navigate(resolveRoute(healthcheckRoutes.routes.config));
250
250
  },
251
251
  onError: (error) => {
252
- toast.error(error instanceof Error ? error.message : "Failed to create");
252
+ toast.error(extractErrorMessage(error, "Failed to create"));
253
253
  },
254
254
  });
255
255
 
@@ -260,7 +260,7 @@ const HealthCheckIDEPageContent = () => {
260
260
  navigate(resolveRoute(healthcheckRoutes.routes.config));
261
261
  },
262
262
  onError: (error) => {
263
- toast.error(error instanceof Error ? error.message : "Failed to update");
263
+ toast.error(extractErrorMessage(error, "Failed to update"));
264
264
  },
265
265
  });
266
266