@checkstack/anomaly-frontend 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.
@@ -0,0 +1,70 @@
1
+ import { useMemo } from "react";
2
+ import { usePluginClient } from "@checkstack/frontend-api";
3
+ import { HealthCheckApi } from "@checkstack/healthcheck-common";
4
+ import { useStrategySchemas } from "@checkstack/healthcheck-frontend";
5
+
6
+ import type { AnomalyDirection } from "@checkstack/anomaly-common";
7
+
8
+ export type AnomalyFieldMeta = {
9
+ path: string;
10
+ type: string;
11
+ defaultEnabled: boolean;
12
+ defaultDirection?: AnomalyDirection;
13
+ defaultSensitivity?: number;
14
+ defaultConfirmationWindow?: number;
15
+ defaultDriftEnabled?: boolean;
16
+ defaultDriftThreshold?: number;
17
+ };
18
+
19
+ export function useAnomalyFields(configurationId: string | undefined) {
20
+ const healthCheckClient = usePluginClient(HealthCheckApi);
21
+
22
+ const { data: hcConfig } = healthCheckClient.getConfiguration.useQuery(
23
+ { id: configurationId ?? "" },
24
+ { enabled: !!configurationId }
25
+ );
26
+
27
+ const { schemas } = useStrategySchemas(hcConfig?.strategyId ?? "");
28
+
29
+ const availableFields = useMemo(() => {
30
+ if (!schemas?.resultSchema) return [];
31
+
32
+ type JsonSchemaNode = {
33
+ type?: string;
34
+ properties?: Record<string, JsonSchemaNode>;
35
+ [key: string]: unknown;
36
+ };
37
+
38
+ const schema = schemas.resultSchema as JsonSchemaNode;
39
+
40
+ const extractFields = (obj: JsonSchemaNode | undefined, prefix = ""): AnomalyFieldMeta[] => {
41
+ const keys: AnomalyFieldMeta[] = [];
42
+ if (obj?.properties) {
43
+ for (const [key, value] of Object.entries(obj.properties)) {
44
+ const path = prefix ? `${prefix}.${key}` : key;
45
+ if (value.type === "object" || value.properties) {
46
+ keys.push(...extractFields(value, path));
47
+ } else {
48
+ // Default to true unless explicitly disabled in schema
49
+ const defaultEnabled = value["x-anomaly-enabled"] !== false;
50
+ keys.push({
51
+ path,
52
+ type: value.type || "string",
53
+ defaultEnabled,
54
+ defaultDirection: value["x-anomaly-direction"] as AnomalyDirection | undefined,
55
+ defaultSensitivity: typeof value["x-anomaly-sensitivity"] === "number" ? value["x-anomaly-sensitivity"] : undefined,
56
+ defaultConfirmationWindow: typeof value["x-anomaly-confirmation-window"] === "number" ? value["x-anomaly-confirmation-window"] : undefined,
57
+ defaultDriftEnabled: typeof value["x-anomaly-drift-enabled"] === "boolean" ? value["x-anomaly-drift-enabled"] : undefined,
58
+ defaultDriftThreshold: typeof value["x-anomaly-drift-threshold"] === "number" ? value["x-anomaly-drift-threshold"] : undefined,
59
+ });
60
+ }
61
+ }
62
+ }
63
+ return keys;
64
+ };
65
+
66
+ return extractFields(schema).filter(k => k.path !== "error");
67
+ }, [schemas]);
68
+
69
+ return availableFields;
70
+ }
package/src/index.tsx ADDED
@@ -0,0 +1 @@
1
+ export { plugin as default } from "./plugin";
package/src/plugin.tsx ADDED
@@ -0,0 +1,78 @@
1
+ import { definePluginMetadata } from "@checkstack/common";
2
+ import { FrontendPlugin, createSlotExtension } from "@checkstack/frontend-api";
3
+ import {
4
+ AssignmentIDENodeSlot,
5
+ AssignmentIDEPanelSlot,
6
+ HealthCheckConfigIDENodeSlot,
7
+ HealthCheckConfigIDEPanelSlot,
8
+ type AssignmentIDEContext,
9
+ type HealthCheckConfigIDEContext
10
+ } from "@checkstack/healthcheck-frontend";
11
+ import { SystemStateBadgesSlot, SystemDetailsSlot } from "@checkstack/catalog-common";
12
+ import { AnomalyConfigPanel } from "./components/AnomalyConfigPanel";
13
+ import { AnomalyTemplatePanel } from "./components/AnomalyTemplatePanel";
14
+ import { SystemAnomalyBadge } from "./components/SystemAnomalyBadge";
15
+ import { SystemAnomalyWidget } from "./components/SystemAnomalyWidget";
16
+ import { IDETreeNode } from "@checkstack/ui";
17
+ import { Activity } from "lucide-react";
18
+
19
+ const pluginMetadata = definePluginMetadata({
20
+ pluginId: "anomaly",
21
+ });
22
+
23
+ export const plugin: FrontendPlugin = {
24
+ metadata: pluginMetadata,
25
+ extensions: [
26
+ createSlotExtension(SystemStateBadgesSlot, {
27
+ id: "anomaly.system-badge",
28
+ component: SystemAnomalyBadge,
29
+ }),
30
+ createSlotExtension(SystemDetailsSlot, {
31
+ id: "anomaly.system-details.widget",
32
+ component: SystemAnomalyWidget,
33
+ }),
34
+ createSlotExtension(HealthCheckConfigIDENodeSlot, {
35
+ id: "anomaly.config-ide.node",
36
+ component: (context: HealthCheckConfigIDEContext) => (
37
+ <IDETreeNode
38
+ nodeId={`anomaly-template:${context.configurationId}`}
39
+ label="Anomaly Defaults"
40
+ icon={Activity}
41
+ selected={context.selectedNode === `anomaly-template:${context.configurationId}`}
42
+ onClick={() => context.onSelectNode(`anomaly-template:${context.configurationId}`)}
43
+ />
44
+ ),
45
+ }),
46
+ createSlotExtension(HealthCheckConfigIDEPanelSlot, {
47
+ id: "anomaly.config-ide.panel",
48
+ component: (context: HealthCheckConfigIDEContext) => {
49
+ if (!context.selectedNode?.startsWith("anomaly-template:")) {
50
+ return <></>;
51
+ }
52
+ return <AnomalyTemplatePanel context={context} />;
53
+ },
54
+ }),
55
+ createSlotExtension(AssignmentIDENodeSlot, {
56
+ id: "anomaly.assignment-ide.node",
57
+ component: (context: AssignmentIDEContext) => (
58
+ <IDETreeNode
59
+ nodeId={`anomaly:${context.configurationId}`}
60
+ label="Anomaly Exceptions"
61
+ icon={Activity}
62
+ selected={context.selectedNode === `anomaly:${context.configurationId}`}
63
+ onClick={() => context.onSelectNode(`anomaly:${context.configurationId}`)}
64
+ indent
65
+ />
66
+ ),
67
+ }),
68
+ createSlotExtension(AssignmentIDEPanelSlot, {
69
+ id: "anomaly.assignment-ide.panel",
70
+ component: (context: AssignmentIDEContext) => {
71
+ if (!context.selectedNode?.startsWith("anomaly:")) {
72
+ return <></>;
73
+ }
74
+ return <AnomalyConfigPanel context={context} />;
75
+ },
76
+ }),
77
+ ],
78
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "@checkstack/tsconfig/frontend.json",
3
+ "compilerOptions": {
4
+ "jsx": "react-jsx"
5
+ },
6
+ "include": [
7
+ "src"
8
+ ]
9
+ }