@checkstack/anomaly-frontend 0.5.6 → 0.5.8

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,42 @@
1
1
  # @checkstack/anomaly-frontend
2
2
 
3
+ ## 0.5.8
4
+
5
+ ### Patch Changes
6
+
7
+ - @checkstack/healthcheck-frontend@0.23.8
8
+
9
+ ## 0.5.7
10
+
11
+ ### Patch Changes
12
+
13
+ - 0b6f01b: feat(anomaly): contribute anomaly signals to the backend system.issues aggregator
14
+
15
+ The anomaly plugin now registers a `system.issues` contributor (sourceId
16
+ `anomaly`) from its backend `init`, so the AI assistant surfaces confirmed
17
+ anomalies and suspicious states alongside incidents, SLOs, health checks, and
18
+ dependency problems.
19
+
20
+ The contributor enforces its own `anomaly_feed.read` access gate (returning an
21
+ empty map - never throwing - when the principal lacks access; service users are
22
+ trusted), then reads the current problem rows for every system from the shared,
23
+ durable `anomalies` table via a new global `getActiveSignalAnomalies` service
24
+ method (state = anomaly | suspicious, suppressed rows excluded). The answer is
25
+ therefore identical on every pod, and only systems with a current problem appear
26
+ in the result.
27
+
28
+ The row->signal mapping (source/tone/label/detail/href/accessRule/iconName) is
29
+ extracted into a new pure `deriveAnomalySignals` deriver in
30
+ `@checkstack/anomaly-common`, shared by both the backend contributor and the
31
+ frontend `AnomalySignalsFiller` so the two surfaces stay in lockstep. The
32
+ frontend filler now delegates to that deriver with unchanged behavior.
33
+
34
+ - Updated dependencies [0b6f01b]
35
+ - Updated dependencies [0b6f01b]
36
+ - @checkstack/anomaly-common@1.4.0
37
+ - @checkstack/healthcheck-common@1.6.0
38
+ - @checkstack/healthcheck-frontend@0.23.7
39
+
3
40
  ## 0.5.6
4
41
 
5
42
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/anomaly-frontend",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "license": "Elastic-2.0",
5
5
  "type": "module",
6
6
  "main": "src/index.tsx",
@@ -13,12 +13,12 @@
13
13
  "lint:code": "eslint . --max-warnings 0"
14
14
  },
15
15
  "dependencies": {
16
- "@checkstack/anomaly-common": "1.3.4",
16
+ "@checkstack/anomaly-common": "1.4.0",
17
17
  "@checkstack/catalog-common": "2.3.4",
18
18
  "@checkstack/common": "0.15.0",
19
19
  "@checkstack/frontend-api": "0.9.0",
20
- "@checkstack/healthcheck-common": "1.5.4",
21
- "@checkstack/healthcheck-frontend": "0.23.6",
20
+ "@checkstack/healthcheck-common": "1.6.0",
21
+ "@checkstack/healthcheck-frontend": "0.23.8",
22
22
  "@checkstack/notification-common": "1.3.3",
23
23
  "@checkstack/notification-frontend": "0.5.5",
24
24
  "@checkstack/signal-frontend": "0.2.4",
@@ -3,10 +3,14 @@ import { usePluginClient, type SlotContext } from "@checkstack/frontend-api";
3
3
  import { resolveRoute } from "@checkstack/common";
4
4
  import {
5
5
  SystemSignalsSlot,
6
- type SystemSignal,
7
6
  type SystemSignalsMap,
8
7
  } from "@checkstack/catalog-common";
9
- import { AnomalyApi } from "@checkstack/anomaly-common";
8
+ import {
9
+ AnomalyApi,
10
+ deriveAnomalySignals,
11
+ ANOMALY_SIGNAL_SOURCE_ID,
12
+ type AnomalySignalRow,
13
+ } from "@checkstack/anomaly-common";
10
14
  import {
11
15
  healthcheckRoutes,
12
16
  healthCheckAccess,
@@ -14,14 +18,12 @@ import {
14
18
 
15
19
  type Props = SlotContext<typeof SystemSignalsSlot>;
16
20
 
17
- const SOURCE_ID = "anomaly";
18
-
19
21
  /**
20
22
  * Reports confirmed anomalies and suspicious states as dashboard signals.
21
23
  * Reuses the two globally-deduped anomaly queries (the same ones the badge
22
- * uses) and emits one signal per anomaly for systems in the overview, deep-
23
- * linking to the affected check's history. Headless filler for
24
- * {@link SystemSignalsSlot}.
24
+ * uses) and the shared {@link deriveAnomalySignals} mapper - the SAME mapper the
25
+ * backend `system.issues` contributor runs - so frontend and backend signals
26
+ * stay identical. Headless filler for {@link SystemSignalsSlot}.
25
27
  */
26
28
  export const AnomalySignalsFiller: React.FC<Props> = ({
27
29
  systemIds,
@@ -39,46 +41,31 @@ export const AnomalySignalsFiller: React.FC<Props> = ({
39
41
  );
40
42
 
41
43
  const signals = useMemo<SystemSignalsMap>(() => {
42
- const result: SystemSignalsMap = {};
43
44
  const inOverview = new Set(systemIds);
45
+ const rows: AnomalySignalRow[] = [...confirmed, ...suspicious]
46
+ .filter((a) => inOverview.has(a.systemId))
47
+ .map((a) => ({
48
+ systemId: a.systemId,
49
+ configurationId: a.configurationId,
50
+ fieldPath: a.fieldPath,
51
+ startedAt: a.startedAt,
52
+ state: a.state,
53
+ }));
44
54
 
45
- const add = (
46
- systemId: string,
47
- configurationId: string,
48
- fieldPath: string,
49
- startedAt: string,
50
- tone: SystemSignal["tone"],
51
- label: string,
52
- ) => {
53
- if (!inOverview.has(systemId)) return;
54
- const signal: SystemSignal = {
55
- source: SOURCE_ID,
56
- tone,
57
- label,
58
- detail: fieldPath,
59
- href: resolveRoute(healthcheckRoutes.routes.historyDetail, {
60
- systemId,
61
- configurationId,
55
+ return deriveAnomalySignals({
56
+ rows,
57
+ buildHref: (row) =>
58
+ resolveRoute(healthcheckRoutes.routes.historyDetail, {
59
+ systemId: row.systemId,
60
+ configurationId: row.configurationId,
62
61
  }),
63
- // The history detail page is gated; render as text for users without it.
64
- accessRule: healthCheckAccess.details,
65
- since: new Date(startedAt).toISOString(),
66
- iconName: "ChartSpline",
67
- };
68
- (result[systemId] ??= []).push(signal);
69
- };
70
-
71
- for (const a of confirmed) {
72
- add(a.systemId, a.configurationId, a.fieldPath, a.startedAt, "warn", "Anomaly detected");
73
- }
74
- for (const a of suspicious) {
75
- add(a.systemId, a.configurationId, a.fieldPath, a.startedAt, "info", "Suspicious behaviour");
76
- }
77
- return result;
62
+ // The history detail page is gated; render as text for users without it.
63
+ accessRule: healthCheckAccess.details,
64
+ });
78
65
  }, [confirmed, suspicious, systemIds]);
79
66
 
80
67
  useEffect(() => {
81
- onSignals(SOURCE_ID, signals);
68
+ onSignals(ANOMALY_SIGNAL_SOURCE_ID, signals);
82
69
  }, [signals, onSignals]);
83
70
 
84
71
  return null;