@miragon/pulse-analytics-client 0.0.0-bootstrap.0 → 0.21.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.
Files changed (200) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -3
  3. package/dist/index.d.ts +12 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +14 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/metric-names.d.ts +43 -0
  8. package/dist/metric-names.d.ts.map +1 -0
  9. package/dist/metric-names.js +53 -0
  10. package/dist/metric-names.js.map +1 -0
  11. package/dist/metrics-contract-labels.test.d.ts +2 -0
  12. package/dist/metrics-contract-labels.test.d.ts.map +1 -0
  13. package/dist/metrics-contract-labels.test.js +169 -0
  14. package/dist/metrics-contract-labels.test.js.map +1 -0
  15. package/dist/metrics-contract.test.d.ts +2 -0
  16. package/dist/metrics-contract.test.d.ts.map +1 -0
  17. package/dist/metrics-contract.test.js +247 -0
  18. package/dist/metrics-contract.test.js.map +1 -0
  19. package/dist/prometheus-client.test.d.ts +2 -0
  20. package/dist/prometheus-client.test.d.ts.map +1 -0
  21. package/dist/prometheus-client.test.js +265 -0
  22. package/dist/prometheus-client.test.js.map +1 -0
  23. package/dist/prometheus.d.ts +100 -0
  24. package/dist/prometheus.d.ts.map +1 -0
  25. package/dist/prometheus.js +295 -0
  26. package/dist/prometheus.js.map +1 -0
  27. package/dist/prometheus.test.d.ts +2 -0
  28. package/dist/prometheus.test.d.ts.map +1 -0
  29. package/dist/prometheus.test.js +54 -0
  30. package/dist/prometheus.test.js.map +1 -0
  31. package/dist/promql-labels.test-support.d.ts +53 -0
  32. package/dist/promql-labels.test-support.d.ts.map +1 -0
  33. package/dist/promql-labels.test-support.js +237 -0
  34. package/dist/promql-labels.test-support.js.map +1 -0
  35. package/dist/promql-labels.test.d.ts +2 -0
  36. package/dist/promql-labels.test.d.ts.map +1 -0
  37. package/dist/promql-labels.test.js +190 -0
  38. package/dist/promql-labels.test.js.map +1 -0
  39. package/dist/promql-parse.test-support.d.ts +57 -0
  40. package/dist/promql-parse.test-support.d.ts.map +1 -0
  41. package/dist/promql-parse.test-support.js +284 -0
  42. package/dist/promql-parse.test-support.js.map +1 -0
  43. package/dist/promql-reading-rules.test-support.d.ts +29 -0
  44. package/dist/promql-reading-rules.test-support.d.ts.map +1 -0
  45. package/dist/promql-reading-rules.test-support.js +79 -0
  46. package/dist/promql-reading-rules.test-support.js.map +1 -0
  47. package/dist/promql-reading-rules.test.d.ts +2 -0
  48. package/dist/promql-reading-rules.test.d.ts.map +1 -0
  49. package/dist/promql-reading-rules.test.js +61 -0
  50. package/dist/promql-reading-rules.test.js.map +1 -0
  51. package/dist/promql-sources.test-support.d.ts +29 -0
  52. package/dist/promql-sources.test-support.d.ts.map +1 -0
  53. package/dist/promql-sources.test-support.js +112 -0
  54. package/dist/promql-sources.test-support.js.map +1 -0
  55. package/dist/promql-sources.test.d.ts +2 -0
  56. package/dist/promql-sources.test.d.ts.map +1 -0
  57. package/dist/promql-sources.test.js +93 -0
  58. package/dist/promql-sources.test.js.map +1 -0
  59. package/dist/queries/as-of.test.d.ts +2 -0
  60. package/dist/queries/as-of.test.d.ts.map +1 -0
  61. package/dist/queries/as-of.test.js +80 -0
  62. package/dist/queries/as-of.test.js.map +1 -0
  63. package/dist/queries/cluster-compare.d.ts +62 -0
  64. package/dist/queries/cluster-compare.d.ts.map +1 -0
  65. package/dist/queries/cluster-compare.js +70 -0
  66. package/dist/queries/cluster-compare.js.map +1 -0
  67. package/dist/queries/cluster-compare.test.d.ts +2 -0
  68. package/dist/queries/cluster-compare.test.d.ts.map +1 -0
  69. package/dist/queries/cluster-compare.test.js +81 -0
  70. package/dist/queries/cluster-compare.test.js.map +1 -0
  71. package/dist/queries/dashboard.d.ts +60 -0
  72. package/dist/queries/dashboard.d.ts.map +1 -0
  73. package/dist/queries/dashboard.js +292 -0
  74. package/dist/queries/dashboard.js.map +1 -0
  75. package/dist/queries/dashboard.test.d.ts +2 -0
  76. package/dist/queries/dashboard.test.d.ts.map +1 -0
  77. package/dist/queries/dashboard.test.js +423 -0
  78. package/dist/queries/dashboard.test.js.map +1 -0
  79. package/dist/queries/element.d.ts +68 -0
  80. package/dist/queries/element.d.ts.map +1 -0
  81. package/dist/queries/element.js +98 -0
  82. package/dist/queries/element.js.map +1 -0
  83. package/dist/queries/element.test.d.ts +2 -0
  84. package/dist/queries/element.test.d.ts.map +1 -0
  85. package/dist/queries/element.test.js +105 -0
  86. package/dist/queries/element.test.js.map +1 -0
  87. package/dist/queries/engine-compare.d.ts +52 -0
  88. package/dist/queries/engine-compare.d.ts.map +1 -0
  89. package/dist/queries/engine-compare.js +57 -0
  90. package/dist/queries/engine-compare.js.map +1 -0
  91. package/dist/queries/engine-compare.test.d.ts +2 -0
  92. package/dist/queries/engine-compare.test.d.ts.map +1 -0
  93. package/dist/queries/engine-compare.test.js +159 -0
  94. package/dist/queries/engine-compare.test.js.map +1 -0
  95. package/dist/queries/engine-landscape.d.ts +92 -0
  96. package/dist/queries/engine-landscape.d.ts.map +1 -0
  97. package/dist/queries/engine-landscape.js +183 -0
  98. package/dist/queries/engine-landscape.js.map +1 -0
  99. package/dist/queries/engine-landscape.test.d.ts +2 -0
  100. package/dist/queries/engine-landscape.test.d.ts.map +1 -0
  101. package/dist/queries/engine-landscape.test.js +171 -0
  102. package/dist/queries/engine-landscape.test.js.map +1 -0
  103. package/dist/queries/failures.d.ts +33 -0
  104. package/dist/queries/failures.d.ts.map +1 -0
  105. package/dist/queries/failures.js +31 -0
  106. package/dist/queries/failures.js.map +1 -0
  107. package/dist/queries/health.d.ts +77 -0
  108. package/dist/queries/health.d.ts.map +1 -0
  109. package/dist/queries/health.js +126 -0
  110. package/dist/queries/health.js.map +1 -0
  111. package/dist/queries/health.test.d.ts +2 -0
  112. package/dist/queries/health.test.d.ts.map +1 -0
  113. package/dist/queries/health.test.js +187 -0
  114. package/dist/queries/health.test.js.map +1 -0
  115. package/dist/queries/helpers.d.ts +184 -0
  116. package/dist/queries/helpers.d.ts.map +1 -0
  117. package/dist/queries/helpers.js +219 -0
  118. package/dist/queries/helpers.js.map +1 -0
  119. package/dist/queries/helpers.test.d.ts +2 -0
  120. package/dist/queries/helpers.test.d.ts.map +1 -0
  121. package/dist/queries/helpers.test.js +53 -0
  122. package/dist/queries/helpers.test.js.map +1 -0
  123. package/dist/queries/index.d.ts +19 -0
  124. package/dist/queries/index.d.ts.map +1 -0
  125. package/dist/queries/index.js +10 -0
  126. package/dist/queries/index.js.map +1 -0
  127. package/dist/queries/performance.d.ts +103 -0
  128. package/dist/queries/performance.d.ts.map +1 -0
  129. package/dist/queries/performance.js +173 -0
  130. package/dist/queries/performance.js.map +1 -0
  131. package/dist/queries/performance.test.d.ts +2 -0
  132. package/dist/queries/performance.test.d.ts.map +1 -0
  133. package/dist/queries/performance.test.js +265 -0
  134. package/dist/queries/performance.test.js.map +1 -0
  135. package/dist/queries/version-compare.d.ts +41 -0
  136. package/dist/queries/version-compare.d.ts.map +1 -0
  137. package/dist/queries/version-compare.js +44 -0
  138. package/dist/queries/version-compare.js.map +1 -0
  139. package/dist/queries/version-compare.test.d.ts +2 -0
  140. package/dist/queries/version-compare.test.d.ts.map +1 -0
  141. package/dist/queries/version-compare.test.js +162 -0
  142. package/dist/queries/version-compare.test.js.map +1 -0
  143. package/dist/queries/windows.d.ts +40 -0
  144. package/dist/queries/windows.d.ts.map +1 -0
  145. package/dist/queries/windows.js +56 -0
  146. package/dist/queries/windows.js.map +1 -0
  147. package/dist/query-honesty.test.d.ts +2 -0
  148. package/dist/query-honesty.test.d.ts.map +1 -0
  149. package/dist/query-honesty.test.js +294 -0
  150. package/dist/query-honesty.test.js.map +1 -0
  151. package/dist/query-scenarios.test-support.d.ts +49 -0
  152. package/dist/query-scenarios.test-support.d.ts.map +1 -0
  153. package/dist/query-scenarios.test-support.js +146 -0
  154. package/dist/query-scenarios.test-support.js.map +1 -0
  155. package/dist/schemas/cluster-compare.d.ts +11 -0
  156. package/dist/schemas/cluster-compare.d.ts.map +1 -0
  157. package/dist/schemas/cluster-compare.js +35 -0
  158. package/dist/schemas/cluster-compare.js.map +1 -0
  159. package/dist/schemas/engine-compare.d.ts +10 -0
  160. package/dist/schemas/engine-compare.d.ts.map +1 -0
  161. package/dist/schemas/engine-compare.js +27 -0
  162. package/dist/schemas/engine-compare.js.map +1 -0
  163. package/dist/schemas/engine-landscape.d.ts +5 -0
  164. package/dist/schemas/engine-landscape.d.ts.map +1 -0
  165. package/dist/schemas/engine-landscape.js +8 -0
  166. package/dist/schemas/engine-landscape.js.map +1 -0
  167. package/dist/schemas/failures.d.ts +8 -0
  168. package/dist/schemas/failures.d.ts.map +1 -0
  169. package/dist/schemas/failures.js +9 -0
  170. package/dist/schemas/failures.js.map +1 -0
  171. package/dist/schemas/health.d.ts +5 -0
  172. package/dist/schemas/health.d.ts.map +1 -0
  173. package/dist/schemas/health.js +6 -0
  174. package/dist/schemas/health.js.map +1 -0
  175. package/dist/schemas/index.d.ts +10 -0
  176. package/dist/schemas/index.d.ts.map +1 -0
  177. package/dist/schemas/index.js +10 -0
  178. package/dist/schemas/index.js.map +1 -0
  179. package/dist/schemas/path.d.ts +15 -0
  180. package/dist/schemas/path.d.ts.map +1 -0
  181. package/dist/schemas/path.js +21 -0
  182. package/dist/schemas/path.js.map +1 -0
  183. package/dist/schemas/performance.d.ts +23 -0
  184. package/dist/schemas/performance.d.ts.map +1 -0
  185. package/dist/schemas/performance.js +21 -0
  186. package/dist/schemas/performance.js.map +1 -0
  187. package/dist/schemas/shared.d.ts +34 -0
  188. package/dist/schemas/shared.d.ts.map +1 -0
  189. package/dist/schemas/shared.js +34 -0
  190. package/dist/schemas/shared.js.map +1 -0
  191. package/dist/schemas/version-compare.d.ts +10 -0
  192. package/dist/schemas/version-compare.d.ts.map +1 -0
  193. package/dist/schemas/version-compare.js +25 -0
  194. package/dist/schemas/version-compare.js.map +1 -0
  195. package/dist/widgets.d.ts +138 -0
  196. package/dist/widgets.d.ts.map +1 -0
  197. package/dist/widgets.js +2 -0
  198. package/dist/widgets.js.map +1 -0
  199. package/metrics-contract.json +174 -0
  200. package/package.json +72 -5
@@ -0,0 +1,105 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { elementBottleneck } from "./element.js";
3
+ /** Mock Prometheus client that dispatches canned samples by the PromQL it sees. */
4
+ function mockClient() {
5
+ const instant = vi.fn(async (q) => {
6
+ if (q.includes("activity_type")) {
7
+ return [
8
+ { metric: { activity_id: "A", activity_type: "serviceTask" }, value: 10 },
9
+ { metric: { activity_id: "B", activity_type: "userTask" }, value: 5 },
10
+ ];
11
+ }
12
+ // Durations in ms, the histogram's unit.
13
+ if (q.includes("histogram_quantile")) {
14
+ return [
15
+ { metric: { activity_id: "A" }, value: 15_000 },
16
+ { metric: { activity_id: "B" }, value: 6_000 },
17
+ ];
18
+ }
19
+ if (q.includes("duration_milliseconds_sum")) {
20
+ return [
21
+ { metric: { activity_id: "A" }, value: 100_000 },
22
+ { metric: { activity_id: "B" }, value: 20_000 },
23
+ ];
24
+ }
25
+ if (q.includes("incident_created")) {
26
+ return [{ metric: { activity_id: "A" }, value: 2 }];
27
+ }
28
+ // counts: activity_ended_total grouped by activity_id only
29
+ return [
30
+ { metric: { activity_id: "A" }, value: 10 },
31
+ { metric: { activity_id: "B" }, value: 5 },
32
+ ];
33
+ });
34
+ const ch = { instant };
35
+ return { ch, instant };
36
+ }
37
+ describe("elementBottleneck", () => {
38
+ it("ranks activities by time contribution and maps incident rate; wait time is null on metrics", async () => {
39
+ const { ch } = mockClient();
40
+ const res = await elementBottleneck(ch, {
41
+ processDefinitionKey: "miraveloLeasing",
42
+ period: "7d",
43
+ minBucketSize: 1,
44
+ maxResults: 10,
45
+ });
46
+ expect(res.activities).toHaveLength(2);
47
+ const [a, b] = res.activities;
48
+ expect(a).toMatchObject({
49
+ activity_id: "A",
50
+ activity_type: "serviceTask",
51
+ execution_count: 10,
52
+ avg_duration_sec: 10,
53
+ p95_duration_sec: 15,
54
+ total_time_sec: 100,
55
+ incident_count: 2,
56
+ incident_rate_pct: 20,
57
+ bottleneck_score_sec: 100,
58
+ avg_wait_sec: null,
59
+ total_wait_sec: null,
60
+ });
61
+ // B has fewer/shorter executions → ranked below A
62
+ expect(b.activity_id).toBe("B");
63
+ expect(b.incident_count).toBe(0);
64
+ expect(res.suppressedActivities).toBe(0);
65
+ });
66
+ it("suppresses activities below minBucketSize", async () => {
67
+ const { ch } = mockClient();
68
+ const res = await elementBottleneck(ch, {
69
+ processDefinitionKey: "miraveloLeasing",
70
+ period: "7d",
71
+ minBucketSize: 8,
72
+ maxResults: 10,
73
+ });
74
+ // only A (count 10) clears the threshold; B (count 5) is suppressed
75
+ expect(res.activities.map((r) => r.activity_id)).toEqual(["A"]);
76
+ expect(res.suppressedActivities).toBe(1);
77
+ });
78
+ it("reports a missing histogram sample as an unmeasured p95, never 0 s", async () => {
79
+ const instant = vi.fn(async (q) => q.includes("histogram_quantile") ||
80
+ q.includes("duration_milliseconds_sum") ||
81
+ q.includes("incident")
82
+ ? []
83
+ : [{ metric: { activity_id: "A", activity_type: "serviceTask" }, value: 4 }]);
84
+ const res = await elementBottleneck({ instant }, { processDefinitionKey: "k", period: "7d", minBucketSize: 1, maxResults: 5 });
85
+ expect(res.activities[0]).toMatchObject({
86
+ execution_count: 4,
87
+ avg_duration_sec: null,
88
+ p95_duration_sec: null,
89
+ incident_rate_pct: 0,
90
+ });
91
+ });
92
+ it("scopes every query to the process definition key and period", async () => {
93
+ const { ch, instant } = mockClient();
94
+ await elementBottleneck(ch, {
95
+ processDefinitionKey: "myKey",
96
+ period: "30d",
97
+ minBucketSize: 1,
98
+ maxResults: 5,
99
+ });
100
+ const queries = instant.mock.calls.map((c) => c[0]);
101
+ expect(queries.every((q) => q.includes('process_definition_key="myKey"'))).toBe(true);
102
+ expect(queries.every((q) => q.includes("[30d]"))).toBe(true);
103
+ });
104
+ });
105
+ //# sourceMappingURL=element.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element.test.js","sourceRoot":"","sources":["../../src/queries/element.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { type PrometheusClient } from "../prometheus.js";
2
+ import { type CompareKpiDelta, type CompareKpis } from "./helpers.js";
3
+ export interface EngineCompareKpi extends CompareKpis {
4
+ engineId: string;
5
+ bucket: "engineA" | "engineB";
6
+ }
7
+ export type EngineCompareDelta = CompareKpiDelta;
8
+ export interface EngineCompareResult {
9
+ engineA: string;
10
+ engineB: string;
11
+ processDefinitionKey: string;
12
+ windowDays: number;
13
+ activityId: string | null;
14
+ minBucketSize: number;
15
+ suppressed: boolean;
16
+ kpis: EngineCompareKpi[];
17
+ delta: EngineCompareDelta;
18
+ /**
19
+ * When the figures were read from Prometheus (ISO timestamp): the "Stand"
20
+ * a view shows. Optional only for payloads recorded before it existed.
21
+ */
22
+ asOf?: string;
23
+ }
24
+ /**
25
+ * Side-by-side comparison of ONE process definition as it runs on two engines,
26
+ * over a shared rolling window. The engine is a metric label (`engine_id`) and
27
+ * all engines write to one Prometheus, so the partition is exact with no
28
+ * per-engine DB fan-out.
29
+ *
30
+ * `processDefinitionKey` is required by design. Comparing two engines' entire
31
+ * workloads would compare their PROCESS MIXES: engines host different
32
+ * processes, so an engine running an inherently error-prone process reads
33
+ * "worse" no matter how healthy it is — a textbook confound (the same
34
+ * aggregation can even invert the per-process truth, Simpson's paradox).
35
+ * Holding the process fixed is what makes a delta attributable to the engine.
36
+ * The cross-engine picture without that scope is `engineLandscape`, which
37
+ * reports mix-independent signals instead (absolute load, engine-owned job
38
+ * backlog) and names the keys deployed on several engines — the valid inputs
39
+ * here.
40
+ *
41
+ * `incident_count`/`incident_rate_pct` cover every activity; with `activityId`
42
+ * the `element_incident_*` fields add the count at that element.
43
+ */
44
+ export declare function engineCompare(ch: PrometheusClient, params: {
45
+ processDefinitionKey: string;
46
+ engineA: string;
47
+ engineB: string;
48
+ windowDays: number;
49
+ activityId?: string;
50
+ minBucketSize: number;
51
+ }): Promise<EngineCompareResult>;
52
+ //# sourceMappingURL=engine-compare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-compare.d.ts","sourceRoot":"","sources":["../../src/queries/engine-compare.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACpF,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,cAAc,CAAA;AAErB,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,SAAS,GAAG,SAAS,CAAA;CAC9B;AAED,MAAM,MAAM,kBAAkB,GAAG,eAAe,CAAA;AAEhD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,oBAAoB,EAAE,MAAM,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,OAAO,CAAA;IACnB,IAAI,EAAE,gBAAgB,EAAE,CAAA;IACxB,KAAK,EAAE,kBAAkB,CAAA;IACzB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,aAAa,CACjC,EAAE,EAAE,gBAAgB,EACpB,MAAM,EAAE;IACN,oBAAoB,EAAE,MAAM,CAAA;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;CACtB,GACA,OAAO,CAAC,mBAAmB,CAAC,CAuB9B"}
@@ -0,0 +1,57 @@
1
+ import { escapeLabelValue, selector } from "../prometheus.js";
2
+ import { asOfNow, belowMinBucket, compareKpiDelta, queryCompareKpis, } from "./helpers.js";
3
+ /**
4
+ * Side-by-side comparison of ONE process definition as it runs on two engines,
5
+ * over a shared rolling window. The engine is a metric label (`engine_id`) and
6
+ * all engines write to one Prometheus, so the partition is exact with no
7
+ * per-engine DB fan-out.
8
+ *
9
+ * `processDefinitionKey` is required by design. Comparing two engines' entire
10
+ * workloads would compare their PROCESS MIXES: engines host different
11
+ * processes, so an engine running an inherently error-prone process reads
12
+ * "worse" no matter how healthy it is — a textbook confound (the same
13
+ * aggregation can even invert the per-process truth, Simpson's paradox).
14
+ * Holding the process fixed is what makes a delta attributable to the engine.
15
+ * The cross-engine picture without that scope is `engineLandscape`, which
16
+ * reports mix-independent signals instead (absolute load, engine-owned job
17
+ * backlog) and names the keys deployed on several engines — the valid inputs
18
+ * here.
19
+ *
20
+ * `incident_count`/`incident_rate_pct` cover every activity; with `activityId`
21
+ * the `element_incident_*` fields add the count at that element.
22
+ */
23
+ export async function engineCompare(ch, params) {
24
+ const minBucket = Math.max(1, Math.floor(params.minBucketSize));
25
+ const windowDays = Math.max(1, Math.floor(params.windowDays));
26
+ const range = `${windowDays}d`;
27
+ const asOf = asOfNow();
28
+ const [a, b] = await Promise.all([
29
+ engineKpi(ch, params, "engineA", params.engineA, range),
30
+ engineKpi(ch, params, "engineB", params.engineB, range),
31
+ ]);
32
+ return {
33
+ asOf,
34
+ engineA: params.engineA,
35
+ engineB: params.engineB,
36
+ processDefinitionKey: params.processDefinitionKey,
37
+ windowDays,
38
+ activityId: params.activityId ?? null,
39
+ minBucketSize: minBucket,
40
+ suppressed: belowMinBucket([a, b], minBucket),
41
+ kpis: [a, b],
42
+ delta: compareKpiDelta(a, b, { baseline: windowDays, other: windowDays }),
43
+ };
44
+ }
45
+ async function engineKpi(ch, params, bucket, engineId, range) {
46
+ const engine = `engine_id="${escapeLabelValue(engineId)}"`;
47
+ const keyMatcher = `process_definition_key="${escapeLabelValue(params.processDefinitionKey)}"`;
48
+ const kpis = await queryCompareKpis(ch, {
49
+ sel: selector(keyMatcher, engine),
50
+ completedSel: selector(keyMatcher, `state="COMPLETED"`, engine),
51
+ elementIncidentSel: params.activityId
52
+ ? selector(keyMatcher, `activity_id="${escapeLabelValue(params.activityId)}"`, engine)
53
+ : undefined,
54
+ }, { range });
55
+ return { engineId, bucket, ...kpis };
56
+ }
57
+ //# sourceMappingURL=engine-compare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-compare.js","sourceRoot":"","sources":["../../src/queries/engine-compare.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAyB,MAAM,kBAAkB,CAAA;AACpF,OAAO,EACL,OAAO,EACP,cAAc,EACd,eAAe,EACf,gBAAgB,GAGjB,MAAM,cAAc,CAAA;AA0BrB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,EAAoB,EACpB,MAOC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAA;IAC/D,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAA;IAC7D,MAAM,KAAK,GAAG,GAAG,UAAU,GAAG,CAAA;IAC9B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;IAEtB,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC/B,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC;QACvD,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC;KACxD,CAAC,CAAA;IAEF,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;QACjD,UAAU;QACV,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,IAAI;QACrC,aAAa,EAAE,SAAS;QACxB,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC;QAC7C,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACZ,KAAK,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;KAC1E,CAAA;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CACtB,EAAoB,EACpB,MAA6D,EAC7D,MAA6B,EAC7B,QAAgB,EAChB,KAAa;IAEb,MAAM,MAAM,GAAG,cAAc,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAA;IAC1D,MAAM,UAAU,GAAG,2BAA2B,gBAAgB,CAAC,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAA;IAC9F,MAAM,IAAI,GAAG,MAAM,gBAAgB,CACjC,EAAE,EACF;QACE,GAAG,EAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QACjC,YAAY,EAAE,QAAQ,CAAC,UAAU,EAAE,mBAAmB,EAAE,MAAM,CAAC;QAC/D,kBAAkB,EAAE,MAAM,CAAC,UAAU;YACnC,CAAC,CAAC,QAAQ,CAAC,UAAU,EAAE,gBAAgB,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;YACtF,CAAC,CAAC,SAAS;KACd,EACD,EAAE,KAAK,EAAE,CACV,CAAA;IACD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAAA;AACtC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=engine-compare.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-compare.test.d.ts","sourceRoot":"","sources":["../../src/queries/engine-compare.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,159 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { engineCompare } from "./engine-compare.js";
3
+ const v = (value) => ({ metric: {}, value });
4
+ /** Mock Prometheus client with distinct canned KPIs per engine_id partition. */
5
+ function mockClient(byEngine = {
6
+ "prod-a": { started: 100, completed: 80, incidents: 10, avg: 10, p95: 20 },
7
+ "prod-b": { started: 50, completed: 40, incidents: 10, avg: 12, p95: 30 },
8
+ }) {
9
+ const instant = vi.fn(async (q) => {
10
+ const engine = q.includes('engine_id="prod-a"') ? byEngine["prod-a"] : byEngine["prod-b"];
11
+ const maybe = (value) => (value === null ? [] : [v(value)]);
12
+ // Canned durations are seconds; the histogram answers in ms.
13
+ const ms = (sec) => (sec === null ? null : sec * 1000);
14
+ if (q.includes("histogram_quantile"))
15
+ return maybe(ms(engine.p95));
16
+ if (q.includes("duration_milliseconds_sum"))
17
+ return maybe(ms(engine.avg));
18
+ if (q.includes("incident_created") && q.includes("activity_id")) {
19
+ return [v(engine.elementIncidents ?? 0)];
20
+ }
21
+ if (q.includes("incident_created"))
22
+ return [v(engine.incidents)];
23
+ if (q.includes('state="COMPLETED"'))
24
+ return [v(engine.completed)];
25
+ return [v(engine.started)];
26
+ });
27
+ const ch = { instant };
28
+ return { ch, instant };
29
+ }
30
+ const params = {
31
+ processDefinitionKey: "order",
32
+ engineA: "prod-a",
33
+ engineB: "prod-b",
34
+ windowDays: 14,
35
+ minBucketSize: 10,
36
+ };
37
+ describe("engineCompare", () => {
38
+ it("computes per-engine KPIs and the B-vs-A delta for one process", async () => {
39
+ const { ch } = mockClient();
40
+ const res = await engineCompare(ch, params);
41
+ expect(res).toMatchObject({
42
+ engineA: "prod-a",
43
+ engineB: "prod-b",
44
+ processDefinitionKey: "order",
45
+ windowDays: 14,
46
+ activityId: null,
47
+ minBucketSize: 10,
48
+ suppressed: false,
49
+ });
50
+ expect(res.kpis).toEqual([
51
+ {
52
+ engineId: "prod-a",
53
+ bucket: "engineA",
54
+ instance_count: 100,
55
+ completed_count: 80,
56
+ incident_count: 10,
57
+ incident_rate_pct: 10,
58
+ element_incident_count: null,
59
+ element_incident_rate_pct: null,
60
+ avg_duration_sec: 10,
61
+ p95_duration_sec: 20,
62
+ },
63
+ {
64
+ engineId: "prod-b",
65
+ bucket: "engineB",
66
+ instance_count: 50,
67
+ completed_count: 40,
68
+ incident_count: 10,
69
+ incident_rate_pct: 20,
70
+ element_incident_count: null,
71
+ element_incident_rate_pct: null,
72
+ avg_duration_sec: 12,
73
+ p95_duration_sec: 30,
74
+ },
75
+ ]);
76
+ expect(res.delta).toEqual({
77
+ started_per_day_delta_pct: -50,
78
+ incident_rate_delta_pp: 10,
79
+ element_incident_rate_delta_pp: null,
80
+ avg_duration_delta_pct: 20,
81
+ p95_duration_delta_pct: 50,
82
+ });
83
+ });
84
+ it("reports the element's incidents NEXT TO the process-wide ones, never instead", async () => {
85
+ const { ch, instant } = mockClient({
86
+ "prod-a": {
87
+ started: 100,
88
+ completed: 80,
89
+ incidents: 10,
90
+ elementIncidents: 4,
91
+ avg: 10,
92
+ p95: 20,
93
+ },
94
+ "prod-b": {
95
+ started: 50,
96
+ completed: 40,
97
+ incidents: 10,
98
+ elementIncidents: 1,
99
+ avg: 12,
100
+ p95: 30,
101
+ },
102
+ });
103
+ const res = await engineCompare(ch, { ...params, activityId: "Task_check" });
104
+ expect(res.kpis.map((k) => [k.incident_rate_pct, k.element_incident_rate_pct])).toEqual([
105
+ [10, 4],
106
+ [20, 2],
107
+ ]);
108
+ expect(res.delta.element_incident_rate_delta_pp).toBe(-2);
109
+ // One extra query per engine, only for the element count.
110
+ expect(instant.mock.calls.filter(([q]) => q.includes('activity_id="Task_check"'))).toHaveLength(2);
111
+ });
112
+ it("flags the comparison as suppressed when one engine misses minBucketSize", async () => {
113
+ const { ch } = mockClient();
114
+ // prod-b only has 50 instances
115
+ expect((await engineCompare(ch, { ...params, minBucketSize: 60 })).suppressed).toBe(true);
116
+ });
117
+ it("flags it suppressed when too few instances COMPLETED — the durations' sample", async () => {
118
+ const { ch } = mockClient({
119
+ "prod-a": { started: 100, completed: 80, incidents: 0, avg: 10, p95: 20 },
120
+ "prod-b": { started: 100, completed: 3, incidents: 0, avg: 1, p95: 2 },
121
+ });
122
+ expect((await engineCompare(ch, params)).suppressed).toBe(true);
123
+ });
124
+ it("reads 'nothing ended' as an unmeasured duration and suppresses its delta (N78)", async () => {
125
+ // prod-b started work but nothing ended yet: the duration ratio is NaN,
126
+ // which the client drops — formerly a 0 s duration and a −100 % delta.
127
+ const { ch } = mockClient({
128
+ "prod-a": { started: 100, completed: 80, incidents: 5, avg: 10, p95: 20 },
129
+ "prod-b": { started: 40, completed: 0, incidents: 0, avg: null, p95: null },
130
+ });
131
+ const res = await engineCompare(ch, params);
132
+ expect(res.kpis[1]).toMatchObject({ avg_duration_sec: null, p95_duration_sec: null });
133
+ expect(res.delta.avg_duration_delta_pct).toBeNull();
134
+ expect(res.delta.p95_duration_delta_pct).toBeNull();
135
+ expect(res.suppressed).toBe(true);
136
+ });
137
+ it("returns null deltas instead of dividing by a zero baseline", async () => {
138
+ const instant = vi.fn(async () => []);
139
+ const res = await engineCompare({ instant }, { ...params, windowDays: 7, minBucketSize: 1 });
140
+ expect(res.suppressed).toBe(true);
141
+ expect(Object.values(res.delta).every((d) => d === null)).toBe(true);
142
+ expect(res.kpis.every((k) => k.incident_rate_pct === null)).toBe(true);
143
+ });
144
+ it("partitions every query by exactly one engine_id and always holds the process fixed", async () => {
145
+ const { ch, instant } = mockClient();
146
+ await engineCompare(ch, params);
147
+ const queries = instant.mock.calls.map((c) => c[0]);
148
+ expect(queries).toHaveLength(10);
149
+ const aQueries = queries.filter((q) => q.includes('engine_id="prod-a"'));
150
+ const bQueries = queries.filter((q) => q.includes('engine_id="prod-b"'));
151
+ expect(aQueries).toHaveLength(5);
152
+ expect(bQueries).toHaveLength(5);
153
+ // The scope that makes the delta attributable to the engine rather than to
154
+ // a different process mix — never absent.
155
+ expect(queries.every((q) => q.includes('process_definition_key="order"'))).toBe(true);
156
+ expect(queries.every((q) => q.includes("[14d]"))).toBe(true);
157
+ });
158
+ });
159
+ //# sourceMappingURL=engine-compare.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-compare.test.js","sourceRoot":"","sources":["../../src/queries/engine-compare.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAGnD,MAAM,CAAC,GAAG,CAAC,KAAa,EAAc,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;AAYhE,gFAAgF;AAChF,SAAS,UAAU,CACjB,WAAgD;IAC9C,QAAQ,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;IAC1E,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;CAC1E;IAED,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAS,EAAyB,EAAE;QAC/D,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QACzF,MAAM,KAAK,GAAG,CAAC,KAAoB,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAC1E,6DAA6D;QAC7D,MAAM,EAAE,GAAG,CAAC,GAAkB,EAAE,EAAE,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,CAAA;QACrE,IAAI,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC;YAAE,OAAO,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QAClE,IAAI,CAAC,CAAC,QAAQ,CAAC,2BAA2B,CAAC;YAAE,OAAO,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QACzE,IAAI,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YAChE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC,CAAA;QAC1C,CAAC;QACD,IAAI,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;QAChE,IAAI,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;QACjE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;IAC5B,CAAC,CAAC,CAAA;IACF,MAAM,EAAE,GAAqB,EAAE,OAAO,EAAE,CAAA;IACxC,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAA;AACxB,CAAC;AAED,MAAM,MAAM,GAAG;IACb,oBAAoB,EAAE,OAAO;IAC7B,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,QAAQ;IACjB,UAAU,EAAE,EAAE;IACd,aAAa,EAAE,EAAE;CAClB,CAAA;AAED,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,EAAE,EAAE,EAAE,GAAG,UAAU,EAAE,CAAA;QAC3B,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAE3C,MAAM,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC;YACxB,OAAO,EAAE,QAAQ;YACjB,OAAO,EAAE,QAAQ;YACjB,oBAAoB,EAAE,OAAO;YAC7B,UAAU,EAAE,EAAE;YACd,UAAU,EAAE,IAAI;YAChB,aAAa,EAAE,EAAE;YACjB,UAAU,EAAE,KAAK;SAClB,CAAC,CAAA;QACF,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;YACvB;gBACE,QAAQ,EAAE,QAAQ;gBAClB,MAAM,EAAE,SAAS;gBACjB,cAAc,EAAE,GAAG;gBACnB,eAAe,EAAE,EAAE;gBACnB,cAAc,EAAE,EAAE;gBAClB,iBAAiB,EAAE,EAAE;gBACrB,sBAAsB,EAAE,IAAI;gBAC5B,yBAAyB,EAAE,IAAI;gBAC/B,gBAAgB,EAAE,EAAE;gBACpB,gBAAgB,EAAE,EAAE;aACrB;YACD;gBACE,QAAQ,EAAE,QAAQ;gBAClB,MAAM,EAAE,SAAS;gBACjB,cAAc,EAAE,EAAE;gBAClB,eAAe,EAAE,EAAE;gBACnB,cAAc,EAAE,EAAE;gBAClB,iBAAiB,EAAE,EAAE;gBACrB,sBAAsB,EAAE,IAAI;gBAC5B,yBAAyB,EAAE,IAAI;gBAC/B,gBAAgB,EAAE,EAAE;gBACpB,gBAAgB,EAAE,EAAE;aACrB;SACF,CAAC,CAAA;QACF,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;YACxB,yBAAyB,EAAE,CAAC,EAAE;YAC9B,sBAAsB,EAAE,EAAE;YAC1B,8BAA8B,EAAE,IAAI;YACpC,sBAAsB,EAAE,EAAE;YAC1B,sBAAsB,EAAE,EAAE;SAC3B,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;YACjC,QAAQ,EAAE;gBACR,OAAO,EAAE,GAAG;gBACZ,SAAS,EAAE,EAAE;gBACb,SAAS,EAAE,EAAE;gBACb,gBAAgB,EAAE,CAAC;gBACnB,GAAG,EAAE,EAAE;gBACP,GAAG,EAAE,EAAE;aACR;YACD,QAAQ,EAAE;gBACR,OAAO,EAAE,EAAE;gBACX,SAAS,EAAE,EAAE;gBACb,SAAS,EAAE,EAAE;gBACb,gBAAgB,EAAE,CAAC;gBACnB,GAAG,EAAE,EAAE;gBACP,GAAG,EAAE,EAAE;aACR;SACF,CAAC,CAAA;QACF,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAA;QAC5E,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACtF,CAAC,EAAE,EAAE,CAAC,CAAC;YACP,CAAC,EAAE,EAAE,CAAC,CAAC;SACR,CAAC,CAAA;QACF,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QACzD,0DAA0D;QAC1D,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,YAAY,CAC7F,CAAC,CACF,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,EAAE,EAAE,EAAE,GAAG,UAAU,EAAE,CAAA;QAC3B,+BAA+B;QAC/B,MAAM,CAAC,CAAC,MAAM,aAAa,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3F,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC;YACxB,QAAQ,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;YACzE,QAAQ,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE;SACvE,CAAC,CAAA;QACF,MAAM,CAAC,CAAC,MAAM,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACjE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE;QAC9F,wEAAwE;QACxE,uEAAuE;QACvE,MAAM,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC;YACxB,QAAQ,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;YACzE,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE;SAC5E,CAAC,CAAA;QACF,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAC3C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAA;QACrF,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,QAAQ,EAAE,CAAA;QACnD,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,QAAQ,EAAE,CAAA;QACnD,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,IAA2B,EAAE,CAAC,EAAE,CAAC,CAAA;QAC5D,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC,CAAA;QAC5F,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACpE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACxE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oFAAoF,EAAE,KAAK,IAAI,EAAE;QAClG,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,CAAA;QACpC,MAAM,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QAC/B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACnD,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;QAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAA;QACxE,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAA;QACxE,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;QAChC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;QAChC,2EAA2E;QAC3E,0CAA0C;QAC1C,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,gCAAgC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrF,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,92 @@
1
+ import { type EngineFilterInput, type PrometheusClient } from "../prometheus.js";
2
+ /**
3
+ * Per-engine row of the landscape. Absolute counts only — see the module note.
4
+ * Every count is `null` for an engine that reports nothing (`reporting:
5
+ * false`): its figures are unknown, never a plausible 0.
6
+ */
7
+ export interface EngineLandscapeEngine {
8
+ engineId: string;
9
+ /**
10
+ * `false` when the engine contributed no series at all: the plugin is down,
11
+ * the engine is unreachable, or it was never scraped. Only reachable when the
12
+ * caller passes the expected engine ids — an engine that reports nothing is
13
+ * otherwise indistinguishable from one that does not exist.
14
+ */
15
+ reporting: boolean;
16
+ runningInstances: number | null;
17
+ openIncidents: number | null;
18
+ failedJobs: number | null;
19
+ executableJobs: number | null;
20
+ suspendedJobs: number | null;
21
+ jobsDueFuture: number | null;
22
+ openExternalTasks: number | null;
23
+ deployedDefinitionKeys: number | null;
24
+ /** Keys present on this engine only — the load no other engine shares. */
25
+ exclusiveDefinitionKeys: number | null;
26
+ }
27
+ /** One row of the process × engine inventory matrix. */
28
+ export interface EngineLandscapeProcess {
29
+ processDefinitionKey: string;
30
+ /** Engines this key is deployed on (or reports live state for), ascending. */
31
+ engineIds: string[];
32
+ /** Present on ≥2 engines — the only case where an A/B engine compare holds. */
33
+ shared: boolean;
34
+ /** Running instances per engine id; engines without running work are absent. */
35
+ runningByEngine: Record<string, number>;
36
+ runningTotal: number;
37
+ openIncidentsTotal: number;
38
+ failedJobsTotal: number;
39
+ }
40
+ export interface EngineLandscapeResult {
41
+ /** Ascending by engine id — deliberately not a ranking (see the module note). */
42
+ engines: EngineLandscapeEngine[];
43
+ /** Shared keys first, then by running instances descending, then by key. */
44
+ processes: EngineLandscapeProcess[];
45
+ /** Keys present on ≥2 engines — the valid drill-down targets for engineCompare. */
46
+ sharedProcessKeys: string[];
47
+ totals: {
48
+ engineCount: number;
49
+ reportingEngineCount: number;
50
+ processKeyCount: number;
51
+ sharedProcessKeyCount: number;
52
+ /** The sums over the reporting engines; `null` while no engine reports (unknown, not 0). */
53
+ runningInstances: number | null;
54
+ openIncidents: number | null;
55
+ failedJobs: number | null;
56
+ };
57
+ /**
58
+ * When the figures were read from Prometheus (ISO timestamp): the "Stand"
59
+ * a view shows. Optional only for payloads recorded before it existed.
60
+ */
61
+ asOf?: string;
62
+ }
63
+ /**
64
+ * Cross-engine landscape: what runs where, how much of it, and which engines
65
+ * carry a backlog.
66
+ *
67
+ * Deliberately NOT an engine-vs-engine scoreboard. Rates (failure rate,
68
+ * incident rate, duration) aggregated per engine measure that engine's PROCESS
69
+ * MIX, not the engine: an engine hosting an inherently error-prone process
70
+ * reads "unhealthy" while running perfectly. So this query reports the two
71
+ * things that survive a different mix per engine:
72
+ *
73
+ * 1. Absolute counts (running instances, open incidents, failed jobs) — they
74
+ * say where work and trouble physically sit, which is what an operator
75
+ * acts on, and they stay additive across engines.
76
+ * 2. The engine-owned backlog gauges (`jobs_executable`, `jobs_suspended`,
77
+ * `jobs_due_future`, `external_tasks_open`), whose contract labels are
78
+ * `engine_id` ONLY. With no process dimension there is no mix to confound
79
+ * them, so these genuinely do rank engines against each other.
80
+ *
81
+ * The inventory matrix then names the one place a KPI comparison IS sound: a
82
+ * process definition key present on two or more engines (`sharedProcessKeys`)
83
+ * — the drill-down {@link engineCompare} requires.
84
+ *
85
+ * Pass `engine` as the array of CONFIGURED engine ids to also surface engines
86
+ * that report nothing (`reporting: false`); without it the landscape can only
87
+ * show engines Prometheus has series for.
88
+ */
89
+ export declare function engineLandscape(ch: PrometheusClient, params?: {
90
+ engine?: EngineFilterInput;
91
+ }): Promise<EngineLandscapeResult>;
92
+ //# sourceMappingURL=engine-landscape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-landscape.d.ts","sourceRoot":"","sources":["../../src/queries/engine-landscape.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAEtB,MAAM,kBAAkB,CAAA;AAQzB;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,SAAS,EAAE,OAAO,CAAA;IAIlB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IAIzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAEhC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,0EAA0E;IAC1E,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAA;CACvC;AAED,wDAAwD;AACxD,MAAM,WAAW,sBAAsB;IACrC,oBAAoB,EAAE,MAAM,CAAA;IAC5B,8EAA8E;IAC9E,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,+EAA+E;IAC/E,MAAM,EAAE,OAAO,CAAA;IACf,gFAAgF;IAChF,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,YAAY,EAAE,MAAM,CAAA;IACpB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,4EAA4E;IAC5E,SAAS,EAAE,sBAAsB,EAAE,CAAA;IACnC,mFAAmF;IACnF,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,MAAM,EAAE;QACN,WAAW,EAAE,MAAM,CAAA;QACnB,oBAAoB,EAAE,MAAM,CAAA;QAC5B,eAAe,EAAE,MAAM,CAAA;QACvB,qBAAqB,EAAE,MAAM,CAAA;QAC7B,4FAA4F;QAC5F,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;QAC/B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;QAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAC1B,CAAA;IACD;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AASD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,gBAAgB,EACpB,MAAM,GAAE;IAAE,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAAO,GAC1C,OAAO,CAAC,qBAAqB,CAAC,CA4HhC"}