@checkstack/anomaly-frontend 0.5.5 → 0.5.7

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.7
4
+
5
+ ### Patch Changes
6
+
7
+ - 0b6f01b: feat(anomaly): contribute anomaly signals to the backend system.issues aggregator
8
+
9
+ The anomaly plugin now registers a `system.issues` contributor (sourceId
10
+ `anomaly`) from its backend `init`, so the AI assistant surfaces confirmed
11
+ anomalies and suspicious states alongside incidents, SLOs, health checks, and
12
+ dependency problems.
13
+
14
+ The contributor enforces its own `anomaly_feed.read` access gate (returning an
15
+ empty map - never throwing - when the principal lacks access; service users are
16
+ trusted), then reads the current problem rows for every system from the shared,
17
+ durable `anomalies` table via a new global `getActiveSignalAnomalies` service
18
+ method (state = anomaly | suspicious, suppressed rows excluded). The answer is
19
+ therefore identical on every pod, and only systems with a current problem appear
20
+ in the result.
21
+
22
+ The row->signal mapping (source/tone/label/detail/href/accessRule/iconName) is
23
+ extracted into a new pure `deriveAnomalySignals` deriver in
24
+ `@checkstack/anomaly-common`, shared by both the backend contributor and the
25
+ frontend `AnomalySignalsFiller` so the two surfaces stay in lockstep. The
26
+ frontend filler now delegates to that deriver with unchanged behavior.
27
+
28
+ - Updated dependencies [0b6f01b]
29
+ - Updated dependencies [0b6f01b]
30
+ - @checkstack/anomaly-common@1.4.0
31
+ - @checkstack/healthcheck-common@1.6.0
32
+ - @checkstack/healthcheck-frontend@0.23.7
33
+
34
+ ## 0.5.6
35
+
36
+ ### Patch Changes
37
+
38
+ - @checkstack/healthcheck-frontend@0.23.6
39
+
3
40
  ## 0.5.5
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.5",
3
+ "version": "0.5.7",
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.5",
20
+ "@checkstack/healthcheck-common": "1.6.0",
21
+ "@checkstack/healthcheck-frontend": "0.23.7",
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;