@oneuptime/common 10.0.65 → 10.0.66

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 (210) hide show
  1. package/Models/DatabaseModels/DockerHostOwnerTeam.ts +464 -0
  2. package/Models/DatabaseModels/DockerHostOwnerUser.ts +463 -0
  3. package/Models/DatabaseModels/Index.ts +24 -0
  4. package/Models/DatabaseModels/KubernetesClusterOwnerTeam.ts +464 -0
  5. package/Models/DatabaseModels/KubernetesClusterOwnerUser.ts +463 -0
  6. package/Models/DatabaseModels/KubernetesResource.ts +548 -0
  7. package/Models/DatabaseModels/MetricPipelineRule.ts +804 -0
  8. package/Models/DatabaseModels/MetricRecordingRule.ts +470 -0
  9. package/Models/DatabaseModels/Monitor.ts +2 -0
  10. package/Models/DatabaseModels/Project.ts +53 -0
  11. package/Models/DatabaseModels/Service.ts +79 -0
  12. package/Models/DatabaseModels/TraceDropFilter.ts +508 -0
  13. package/Models/DatabaseModels/TracePipeline.ts +436 -0
  14. package/Models/DatabaseModels/TracePipelineProcessor.ts +454 -0
  15. package/Models/DatabaseModels/TraceRecordingRule.ts +470 -0
  16. package/Models/DatabaseModels/TraceScrubRule.ts +546 -0
  17. package/Server/API/KubernetesResourceAPI.ts +129 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.ts +399 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.ts +205 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.ts +335 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.ts +29 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.ts +53 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -1
  24. package/Server/Services/DockerHostOwnerTeamService.ts +10 -0
  25. package/Server/Services/DockerHostOwnerUserService.ts +10 -0
  26. package/Server/Services/KubernetesClusterOwnerTeamService.ts +10 -0
  27. package/Server/Services/KubernetesClusterOwnerUserService.ts +10 -0
  28. package/Server/Services/KubernetesResourceService.ts +351 -0
  29. package/Server/Services/MetricPipelineRuleService.ts +10 -0
  30. package/Server/Services/MetricRecordingRuleService.ts +10 -0
  31. package/Server/Services/TraceDropFilterService.ts +10 -0
  32. package/Server/Services/TracePipelineProcessorService.ts +10 -0
  33. package/Server/Services/TracePipelineService.ts +10 -0
  34. package/Server/Services/TraceRecordingRuleService.ts +10 -0
  35. package/Server/Services/TraceScrubRuleService.ts +10 -0
  36. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +71 -9
  37. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +483 -75
  38. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +379 -6
  39. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +502 -0
  40. package/Tests/Utils/MetricUnitUtil.test.ts +216 -0
  41. package/Tests/Utils/Metrics/MetricFormulaEvaluator.test.ts +269 -0
  42. package/Tests/Utils/Metrics/MetricResultUnitConverter.test.ts +231 -0
  43. package/Tests/Utils/RecordingRuleExpression.test.ts +177 -0
  44. package/Types/Kubernetes/KubernetesInventoryExtractor.ts +327 -0
  45. package/Types/Kubernetes/KubernetesObjectParser.ts +1949 -0
  46. package/Types/Metrics/MetricDownsamplingRetentionDays.ts +49 -0
  47. package/Types/Metrics/MetricFormulaConfigData.ts +4 -0
  48. package/Types/Metrics/MetricPipelineRuleFilterCondition.ts +136 -0
  49. package/Types/Metrics/MetricPipelineRuleType.ts +27 -0
  50. package/Types/Metrics/RecordingRuleDefinition.ts +180 -0
  51. package/Types/Monitor/CriteriaFilter.ts +43 -0
  52. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +70 -0
  53. package/Types/Permission.ts +520 -0
  54. package/Types/Trace/TraceAggregationType.ts +17 -0
  55. package/Types/Trace/TraceDropFilterAction.ts +6 -0
  56. package/Types/Trace/TracePipelineProcessorType.ts +56 -0
  57. package/Types/Trace/TraceRecordingRuleDefinition.ts +218 -0
  58. package/Types/Trace/TraceScrubAction.ts +7 -0
  59. package/Types/Trace/TraceScrubField.ts +8 -0
  60. package/Types/Trace/TraceScrubPatternType.ts +10 -0
  61. package/UI/Components/CardSelect/CardSelect.tsx +9 -1
  62. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +6 -10
  63. package/UI/Components/Forms/Fields/FormField.tsx +1 -0
  64. package/UI/Components/Forms/Types/Field.ts +1 -0
  65. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +57 -0
  66. package/UI/Components/Page/Page.tsx +6 -0
  67. package/Utils/MetricUnitUtil.ts +289 -0
  68. package/Utils/Metrics/MetricFormulaEvaluator.ts +610 -0
  69. package/Utils/Metrics/MetricResultUnitConverter.ts +91 -0
  70. package/Utils/Metrics/RecordingRuleExpression.ts +359 -0
  71. package/Utils/ValueFormatter.ts +137 -13
  72. package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js +480 -0
  73. package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js.map +1 -0
  74. package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js +479 -0
  75. package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js.map +1 -0
  76. package/build/dist/Models/DatabaseModels/Index.js +24 -0
  77. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  78. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js +480 -0
  79. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js.map +1 -0
  80. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js +479 -0
  81. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js.map +1 -0
  82. package/build/dist/Models/DatabaseModels/KubernetesResource.js +590 -0
  83. package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -0
  84. package/build/dist/Models/DatabaseModels/MetricPipelineRule.js +836 -0
  85. package/build/dist/Models/DatabaseModels/MetricPipelineRule.js.map +1 -0
  86. package/build/dist/Models/DatabaseModels/MetricRecordingRule.js +497 -0
  87. package/build/dist/Models/DatabaseModels/MetricRecordingRule.js.map +1 -0
  88. package/build/dist/Models/DatabaseModels/Monitor.js +2 -0
  89. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  90. package/build/dist/Models/DatabaseModels/Project.js +53 -0
  91. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  92. package/build/dist/Models/DatabaseModels/Service.js +79 -0
  93. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  94. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +536 -0
  95. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -0
  96. package/build/dist/Models/DatabaseModels/TracePipeline.js +462 -0
  97. package/build/dist/Models/DatabaseModels/TracePipeline.js.map +1 -0
  98. package/build/dist/Models/DatabaseModels/TracePipelineProcessor.js +476 -0
  99. package/build/dist/Models/DatabaseModels/TracePipelineProcessor.js.map +1 -0
  100. package/build/dist/Models/DatabaseModels/TraceRecordingRule.js +497 -0
  101. package/build/dist/Models/DatabaseModels/TraceRecordingRule.js.map +1 -0
  102. package/build/dist/Models/DatabaseModels/TraceScrubRule.js +575 -0
  103. package/build/dist/Models/DatabaseModels/TraceScrubRule.js.map +1 -0
  104. package/build/dist/Server/API/KubernetesResourceAPI.js +98 -0
  105. package/build/dist/Server/API/KubernetesResourceAPI.js.map +1 -0
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.js +144 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.js.map +1 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.js +82 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.js.map +1 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.js +118 -0
  111. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.js.map +1 -0
  112. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.js +16 -0
  113. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.js.map +1 -0
  114. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.js +24 -0
  115. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.js.map +1 -0
  116. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  117. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  118. package/build/dist/Server/Services/DockerHostOwnerTeamService.js +9 -0
  119. package/build/dist/Server/Services/DockerHostOwnerTeamService.js.map +1 -0
  120. package/build/dist/Server/Services/DockerHostOwnerUserService.js +9 -0
  121. package/build/dist/Server/Services/DockerHostOwnerUserService.js.map +1 -0
  122. package/build/dist/Server/Services/KubernetesClusterOwnerTeamService.js +9 -0
  123. package/build/dist/Server/Services/KubernetesClusterOwnerTeamService.js.map +1 -0
  124. package/build/dist/Server/Services/KubernetesClusterOwnerUserService.js +9 -0
  125. package/build/dist/Server/Services/KubernetesClusterOwnerUserService.js.map +1 -0
  126. package/build/dist/Server/Services/KubernetesResourceService.js +237 -0
  127. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -0
  128. package/build/dist/Server/Services/MetricPipelineRuleService.js +9 -0
  129. package/build/dist/Server/Services/MetricPipelineRuleService.js.map +1 -0
  130. package/build/dist/Server/Services/MetricRecordingRuleService.js +9 -0
  131. package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -0
  132. package/build/dist/Server/Services/TraceDropFilterService.js +9 -0
  133. package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -0
  134. package/build/dist/Server/Services/TracePipelineProcessorService.js +9 -0
  135. package/build/dist/Server/Services/TracePipelineProcessorService.js.map +1 -0
  136. package/build/dist/Server/Services/TracePipelineService.js +9 -0
  137. package/build/dist/Server/Services/TracePipelineService.js.map +1 -0
  138. package/build/dist/Server/Services/TraceRecordingRuleService.js +9 -0
  139. package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -0
  140. package/build/dist/Server/Services/TraceScrubRuleService.js +9 -0
  141. package/build/dist/Server/Services/TraceScrubRuleService.js.map +1 -0
  142. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +56 -9
  143. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  144. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +335 -53
  145. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  146. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +277 -5
  147. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  148. package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js +407 -0
  149. package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js.map +1 -0
  150. package/build/dist/Tests/Utils/MetricUnitUtil.test.js +159 -0
  151. package/build/dist/Tests/Utils/MetricUnitUtil.test.js.map +1 -0
  152. package/build/dist/Tests/Utils/Metrics/MetricFormulaEvaluator.test.js +224 -0
  153. package/build/dist/Tests/Utils/Metrics/MetricFormulaEvaluator.test.js.map +1 -0
  154. package/build/dist/Tests/Utils/Metrics/MetricResultUnitConverter.test.js +180 -0
  155. package/build/dist/Tests/Utils/Metrics/MetricResultUnitConverter.test.js.map +1 -0
  156. package/build/dist/Tests/Utils/RecordingRuleExpression.test.js +142 -0
  157. package/build/dist/Tests/Utils/RecordingRuleExpression.test.js.map +1 -0
  158. package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js +200 -0
  159. package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js.map +1 -0
  160. package/build/dist/Types/Kubernetes/KubernetesObjectParser.js +1205 -0
  161. package/build/dist/Types/Kubernetes/KubernetesObjectParser.js.map +1 -0
  162. package/build/dist/Types/Metrics/MetricDownsamplingRetentionDays.js +32 -0
  163. package/build/dist/Types/Metrics/MetricDownsamplingRetentionDays.js.map +1 -0
  164. package/build/dist/Types/Metrics/MetricPipelineRuleFilterCondition.js +103 -0
  165. package/build/dist/Types/Metrics/MetricPipelineRuleFilterCondition.js.map +1 -0
  166. package/build/dist/Types/Metrics/MetricPipelineRuleType.js +27 -0
  167. package/build/dist/Types/Metrics/MetricPipelineRuleType.js.map +1 -0
  168. package/build/dist/Types/Metrics/RecordingRuleDefinition.js +110 -0
  169. package/build/dist/Types/Metrics/RecordingRuleDefinition.js.map +1 -0
  170. package/build/dist/Types/Monitor/CriteriaFilter.js +22 -0
  171. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  172. package/build/dist/Types/Monitor/MetricMonitor/MetricCriteriaContext.js +2 -0
  173. package/build/dist/Types/Monitor/MetricMonitor/MetricCriteriaContext.js.map +1 -0
  174. package/build/dist/Types/Permission.js +454 -0
  175. package/build/dist/Types/Permission.js.map +1 -1
  176. package/build/dist/Types/Trace/TraceAggregationType.js +18 -0
  177. package/build/dist/Types/Trace/TraceAggregationType.js.map +1 -0
  178. package/build/dist/Types/Trace/TraceDropFilterAction.js +7 -0
  179. package/build/dist/Types/Trace/TraceDropFilterAction.js.map +1 -0
  180. package/build/dist/Types/Trace/TracePipelineProcessorType.js +10 -0
  181. package/build/dist/Types/Trace/TracePipelineProcessorType.js.map +1 -0
  182. package/build/dist/Types/Trace/TraceRecordingRuleDefinition.js +145 -0
  183. package/build/dist/Types/Trace/TraceRecordingRuleDefinition.js.map +1 -0
  184. package/build/dist/Types/Trace/TraceScrubAction.js +8 -0
  185. package/build/dist/Types/Trace/TraceScrubAction.js.map +1 -0
  186. package/build/dist/Types/Trace/TraceScrubField.js +9 -0
  187. package/build/dist/Types/Trace/TraceScrubField.js.map +1 -0
  188. package/build/dist/Types/Trace/TraceScrubPatternType.js +11 -0
  189. package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -0
  190. package/build/dist/UI/Components/CardSelect/CardSelect.js +3 -1
  191. package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
  192. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +6 -9
  193. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  194. package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
  195. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  196. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +30 -0
  197. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  198. package/build/dist/UI/Components/Page/Page.js +1 -0
  199. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  200. package/build/dist/Utils/MetricUnitUtil.js +232 -0
  201. package/build/dist/Utils/MetricUnitUtil.js.map +1 -0
  202. package/build/dist/Utils/Metrics/MetricFormulaEvaluator.js +453 -0
  203. package/build/dist/Utils/Metrics/MetricFormulaEvaluator.js.map +1 -0
  204. package/build/dist/Utils/Metrics/MetricResultUnitConverter.js +61 -0
  205. package/build/dist/Utils/Metrics/MetricResultUnitConverter.js.map +1 -0
  206. package/build/dist/Utils/Metrics/RecordingRuleExpression.js +298 -0
  207. package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -0
  208. package/build/dist/Utils/ValueFormatter.js +123 -13
  209. package/build/dist/Utils/ValueFormatter.js.map +1 -1
  210. package/package.json +1 -1
@@ -0,0 +1,232 @@
1
+ /*
2
+ * Unit conversion utilities for metric alert thresholds. Separate from
3
+ * ValueFormatter (which focuses on auto-scaled display formatting) because
4
+ * thresholds need deterministic, user-selected units — not whatever unit
5
+ * happens to read best for a given magnitude.
6
+ */
7
+ /*
8
+ * Within a family, `toCanonical` is the multiplier that converts a value
9
+ * expressed in this unit into the family's canonical base (bytes for data,
10
+ * seconds for time, % for percent).
11
+ */
12
+ const byteUnits = [
13
+ {
14
+ value: "B",
15
+ label: "Bytes (B)",
16
+ aliases: ["b", "byte", "bytes", "by"],
17
+ toCanonical: 1,
18
+ },
19
+ {
20
+ value: "KB",
21
+ label: "Kilobytes (KB)",
22
+ aliases: ["kb", "kilobyte", "kilobytes", "kby"],
23
+ toCanonical: 1e3,
24
+ },
25
+ {
26
+ value: "MB",
27
+ label: "Megabytes (MB)",
28
+ aliases: ["mb", "megabyte", "megabytes", "mby"],
29
+ toCanonical: 1e6,
30
+ },
31
+ {
32
+ value: "GB",
33
+ label: "Gigabytes (GB)",
34
+ aliases: ["gb", "gigabyte", "gigabytes", "gby"],
35
+ toCanonical: 1e9,
36
+ },
37
+ {
38
+ value: "TB",
39
+ label: "Terabytes (TB)",
40
+ aliases: ["tb", "terabyte", "terabytes", "tby"],
41
+ toCanonical: 1e12,
42
+ },
43
+ {
44
+ value: "PB",
45
+ label: "Petabytes (PB)",
46
+ aliases: ["pb", "petabyte", "petabytes", "pby"],
47
+ toCanonical: 1e15,
48
+ },
49
+ ];
50
+ const timeUnits = [
51
+ {
52
+ value: "ns",
53
+ label: "Nanoseconds (ns)",
54
+ aliases: ["ns", "nanosecond", "nanoseconds"],
55
+ toCanonical: 1e-9,
56
+ },
57
+ {
58
+ value: "µs",
59
+ label: "Microseconds (µs)",
60
+ aliases: ["µs", "us", "microsecond", "microseconds"],
61
+ toCanonical: 1e-6,
62
+ },
63
+ {
64
+ value: "ms",
65
+ label: "Milliseconds (ms)",
66
+ aliases: ["ms", "millisecond", "milliseconds"],
67
+ toCanonical: 1e-3,
68
+ },
69
+ {
70
+ value: "sec",
71
+ label: "Seconds (sec)",
72
+ aliases: ["s", "sec", "second", "seconds"],
73
+ toCanonical: 1,
74
+ },
75
+ {
76
+ value: "min",
77
+ label: "Minutes (min)",
78
+ aliases: ["min", "minute", "minutes"],
79
+ toCanonical: 60,
80
+ },
81
+ {
82
+ value: "hours",
83
+ label: "Hours",
84
+ aliases: ["h", "hr", "hour", "hours"],
85
+ toCanonical: 3600,
86
+ },
87
+ {
88
+ value: "days",
89
+ label: "Days",
90
+ aliases: ["d", "day", "days"],
91
+ toCanonical: 86400,
92
+ },
93
+ ];
94
+ const percentUnits = [
95
+ {
96
+ value: "%",
97
+ label: "Percent (%)",
98
+ aliases: ["%", "percent"],
99
+ toCanonical: 1,
100
+ },
101
+ ];
102
+ const bitUnits = [
103
+ {
104
+ value: "bit",
105
+ label: "Bits (bit)",
106
+ aliases: ["bit", "bits"],
107
+ toCanonical: 1,
108
+ },
109
+ {
110
+ value: "kbit",
111
+ label: "Kilobits (kbit)",
112
+ aliases: ["kbit", "kilobit", "kilobits"],
113
+ toCanonical: 1e3,
114
+ },
115
+ {
116
+ value: "mbit",
117
+ label: "Megabits (mbit)",
118
+ aliases: ["mbit", "megabit", "megabits"],
119
+ toCanonical: 1e6,
120
+ },
121
+ {
122
+ value: "gbit",
123
+ label: "Gigabits (gbit)",
124
+ aliases: ["gbit", "gigabit", "gigabits"],
125
+ toCanonical: 1e9,
126
+ },
127
+ ];
128
+ const allFamilies = [
129
+ byteUnits,
130
+ timeUnits,
131
+ percentUnits,
132
+ bitUnits,
133
+ ];
134
+ function normalize(unit) {
135
+ return unit.trim().toLowerCase();
136
+ }
137
+ function findDefinitionInFamily(unit, family) {
138
+ const normalized = normalize(unit);
139
+ return (family.find((u) => {
140
+ return u.aliases.includes(normalized);
141
+ }) || null);
142
+ }
143
+ function findFamily(unit) {
144
+ if (!unit || !unit.trim()) {
145
+ return null;
146
+ }
147
+ for (const family of allFamilies) {
148
+ if (findDefinitionInFamily(unit, family)) {
149
+ return family;
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+ export default class MetricUnitUtil {
155
+ /*
156
+ * Returns the dropdown options the UI should show next to the threshold
157
+ * input, given the metric's native unit. If the unit isn't in any known
158
+ * family, returns a single option matching the raw unit so users still
159
+ * see what they're working with.
160
+ */
161
+ static getCompatibleUnits(metricUnit) {
162
+ if (!metricUnit || !metricUnit.trim()) {
163
+ return [];
164
+ }
165
+ const family = findFamily(metricUnit);
166
+ if (!family) {
167
+ return [{ value: metricUnit, label: metricUnit }];
168
+ }
169
+ return family.map((u) => {
170
+ return { value: u.value, label: u.label };
171
+ });
172
+ }
173
+ /*
174
+ * Returns the canonical unit value for the metric's native unit, which
175
+ * is what the threshold dropdown should default to when nothing is
176
+ * selected yet. Returns the input unit unchanged if the family is
177
+ * unknown.
178
+ */
179
+ static getCanonicalUnitValue(metricUnit) {
180
+ if (!metricUnit || !metricUnit.trim()) {
181
+ return undefined;
182
+ }
183
+ const family = findFamily(metricUnit);
184
+ if (!family) {
185
+ return metricUnit;
186
+ }
187
+ const def = findDefinitionInFamily(metricUnit, family);
188
+ return (def === null || def === void 0 ? void 0 : def.value) || metricUnit;
189
+ }
190
+ /*
191
+ * Converts a threshold value from the user-selected unit into the
192
+ * metric's native unit so comparisons can happen against raw samples.
193
+ * Returns the unchanged value when either unit is unknown or they
194
+ * belong to different families — the evaluator shouldn't silently
195
+ * produce nonsense numbers.
196
+ */
197
+ static convertToMetricUnit(input) {
198
+ const { value, fromUnit, metricUnit } = input;
199
+ if (!fromUnit || !metricUnit) {
200
+ return value;
201
+ }
202
+ if (normalize(fromUnit) === normalize(metricUnit)) {
203
+ return value;
204
+ }
205
+ const family = findFamily(metricUnit);
206
+ if (!family) {
207
+ return value;
208
+ }
209
+ const fromDef = findDefinitionInFamily(fromUnit, family);
210
+ const metricDef = findDefinitionInFamily(metricUnit, family);
211
+ if (!fromDef || !metricDef) {
212
+ return value;
213
+ }
214
+ /*
215
+ * value_in_canonical = value * fromDef.toCanonical
216
+ * value_in_metric = value_in_canonical / metricDef.toCanonical
217
+ */
218
+ return (value * fromDef.toCanonical) / metricDef.toCanonical;
219
+ }
220
+ /*
221
+ * Convenience: does the metric unit belong to a family we can offer
222
+ * conversions for? When false, the UI should still render a dropdown,
223
+ * but with just the raw unit.
224
+ */
225
+ static hasCompatibleUnitFamily(metricUnit) {
226
+ if (!metricUnit || !metricUnit.trim()) {
227
+ return false;
228
+ }
229
+ return findFamily(metricUnit) !== null;
230
+ }
231
+ }
232
+ //# sourceMappingURL=MetricUnitUtil.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetricUnitUtil.js","sourceRoot":"","sources":["../../../Utils/MetricUnitUtil.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,453 @@
1
+ import BadDataException from "../../Types/Exception/BadDataException";
2
+ /**
3
+ * Shunting-yard based evaluator for metric formulas such as
4
+ * "$A + $B * 2" or "(a - b) / 100". Variables are matched by metric
5
+ * alias (case-insensitive, with or without a leading "$"). Evaluates
6
+ * the formula point-by-point against a time-aligned series from the
7
+ * referenced queries/formulas.
8
+ */
9
+ var TokenType;
10
+ (function (TokenType) {
11
+ TokenType["Number"] = "number";
12
+ TokenType["Variable"] = "variable";
13
+ TokenType["Operator"] = "operator";
14
+ TokenType["LeftParen"] = "lparen";
15
+ TokenType["RightParen"] = "rparen";
16
+ })(TokenType || (TokenType = {}));
17
+ const OPERATOR_PRECEDENCE = {
18
+ "u-": 4,
19
+ "u+": 4,
20
+ "^": 3,
21
+ "*": 2,
22
+ "/": 2,
23
+ "%": 2,
24
+ "+": 1,
25
+ "-": 1,
26
+ };
27
+ const RIGHT_ASSOCIATIVE = new Set(["^", "u-", "u+"]);
28
+ export default class MetricFormulaEvaluator {
29
+ /**
30
+ * Evaluate a formula against the provided query/formula results and
31
+ * return a synthetic AggregatedResult whose timestamps are the union
32
+ * of all referenced series.
33
+ */
34
+ static evaluateFormula(input) {
35
+ const trimmedFormula = (input.formula || "").trim();
36
+ if (!trimmedFormula) {
37
+ return { data: [] };
38
+ }
39
+ const rpn = MetricFormulaEvaluator.toRpn(trimmedFormula);
40
+ const variableResults = MetricFormulaEvaluator.buildVariableResultMap({
41
+ queryConfigs: input.queryConfigs,
42
+ formulaConfigs: input.formulaConfigs,
43
+ results: input.results,
44
+ });
45
+ /*
46
+ * Validate every variable referenced in the formula actually resolves
47
+ * to a result series. Failing loudly is better than silently returning
48
+ * NaN when a user typos an alias.
49
+ */
50
+ const referencedVariables = MetricFormulaEvaluator.collectVariableNames(rpn);
51
+ for (const variableName of referencedVariables) {
52
+ if (!variableResults[variableName]) {
53
+ throw new BadDataException(`Formula references unknown variable "$${variableName}". Define a metric query with that alias first.`);
54
+ }
55
+ }
56
+ const timestampIndex = MetricFormulaEvaluator.buildTimestampIndex(referencedVariables, variableResults);
57
+ const sortedTimestamps = Array.from(timestampIndex.keys()).sort();
58
+ const resultData = [];
59
+ for (const timestampString of sortedTimestamps) {
60
+ const values = timestampIndex.get(timestampString) || {};
61
+ /*
62
+ * Skip points where any referenced variable is missing a value. A
63
+ * partial join would produce misleading numbers (e.g. treating a
64
+ * gap as zero silently when using subtraction).
65
+ */
66
+ const hasAllValues = referencedVariables.every((variable) => {
67
+ return typeof values[variable] === "number";
68
+ });
69
+ if (!hasAllValues) {
70
+ continue;
71
+ }
72
+ let evaluated;
73
+ try {
74
+ evaluated = MetricFormulaEvaluator.evaluateRpn(rpn, values);
75
+ }
76
+ catch (_a) {
77
+ continue;
78
+ }
79
+ if (!Number.isFinite(evaluated)) {
80
+ continue;
81
+ }
82
+ resultData.push({
83
+ timestamp: new Date(timestampString),
84
+ value: evaluated,
85
+ });
86
+ }
87
+ return { data: resultData };
88
+ }
89
+ /**
90
+ * Return the set of variables referenced by a formula, preserving the
91
+ * order of first appearance. Variable names are lower-cased to match
92
+ * the evaluator's case-insensitive lookup. Invalid formulas return an
93
+ * empty list rather than throwing, so callers rendering UI don't have
94
+ * to add defensive try/catch.
95
+ */
96
+ static getReferencedVariables(formula) {
97
+ const trimmedFormula = (formula || "").trim();
98
+ if (!trimmedFormula) {
99
+ return [];
100
+ }
101
+ try {
102
+ const rpn = MetricFormulaEvaluator.toRpn(trimmedFormula);
103
+ return MetricFormulaEvaluator.collectVariableNames(rpn);
104
+ }
105
+ catch (_a) {
106
+ return [];
107
+ }
108
+ }
109
+ /**
110
+ * Validate a formula's syntax without evaluating it. Returns `null`
111
+ * when valid, otherwise a human-readable error message.
112
+ */
113
+ static validateFormula(input) {
114
+ const trimmedFormula = (input.formula || "").trim();
115
+ if (!trimmedFormula) {
116
+ return "Formula is required.";
117
+ }
118
+ let rpn;
119
+ try {
120
+ rpn = MetricFormulaEvaluator.toRpn(trimmedFormula);
121
+ }
122
+ catch (err) {
123
+ return err.message || "Invalid formula.";
124
+ }
125
+ const referenced = MetricFormulaEvaluator.collectVariableNames(rpn);
126
+ const available = new Set(input.availableVariables.map((v) => {
127
+ return v.toLowerCase();
128
+ }));
129
+ for (const variable of referenced) {
130
+ if (!available.has(variable.toLowerCase())) {
131
+ return `Formula references unknown variable "$${variable}".`;
132
+ }
133
+ }
134
+ return null;
135
+ }
136
+ static buildVariableResultMap(input) {
137
+ var _a, _b, _c, _d;
138
+ const variableMap = {};
139
+ const totalSeries = input.queryConfigs.length + input.formulaConfigs.length;
140
+ for (let index = 0; index < totalSeries; index++) {
141
+ const result = input.results[index];
142
+ if (!result) {
143
+ continue;
144
+ }
145
+ let alias;
146
+ if (index < input.queryConfigs.length) {
147
+ alias =
148
+ ((_b = (_a = input.queryConfigs[index]) === null || _a === void 0 ? void 0 : _a.metricAliasData) === null || _b === void 0 ? void 0 : _b.metricVariable) ||
149
+ undefined;
150
+ }
151
+ else {
152
+ const formulaIndex = index - input.queryConfigs.length;
153
+ alias =
154
+ ((_d = (_c = input.formulaConfigs[formulaIndex]) === null || _c === void 0 ? void 0 : _c.metricAliasData) === null || _d === void 0 ? void 0 : _d.metricVariable) ||
155
+ undefined;
156
+ }
157
+ if (!alias) {
158
+ continue;
159
+ }
160
+ variableMap[alias.toLowerCase()] = result;
161
+ }
162
+ return variableMap;
163
+ }
164
+ static buildTimestampIndex(variables, variableResults) {
165
+ const index = new Map();
166
+ for (const variable of variables) {
167
+ const series = variableResults[variable];
168
+ if (!series) {
169
+ continue;
170
+ }
171
+ for (const sample of series.data) {
172
+ const timestampKey = MetricFormulaEvaluator.normalizeTimestamp(sample.timestamp);
173
+ if (!index.has(timestampKey)) {
174
+ index.set(timestampKey, {});
175
+ }
176
+ const bucket = index.get(timestampKey) || {};
177
+ bucket[variable] = sample.value;
178
+ }
179
+ }
180
+ return index;
181
+ }
182
+ static normalizeTimestamp(timestamp) {
183
+ if (timestamp instanceof Date) {
184
+ return timestamp.toISOString();
185
+ }
186
+ /*
187
+ * ClickHouse sometimes returns timestamps at varying precisions
188
+ * ("2024-01-01T00:00:00" vs "2024-01-01T00:00:00.000Z"). Normalize
189
+ * by parsing through Date so differently formatted strings for the
190
+ * same instant align correctly.
191
+ */
192
+ const asDate = new Date(timestamp);
193
+ if (!isNaN(asDate.getTime())) {
194
+ return asDate.toISOString();
195
+ }
196
+ return String(timestamp);
197
+ }
198
+ static collectVariableNames(rpn) {
199
+ const seen = new Set();
200
+ const result = [];
201
+ for (const token of rpn) {
202
+ if (token.type === TokenType.Variable) {
203
+ const normalized = token.value.toLowerCase();
204
+ if (!seen.has(normalized)) {
205
+ seen.add(normalized);
206
+ result.push(normalized);
207
+ }
208
+ }
209
+ }
210
+ return result;
211
+ }
212
+ static tokenize(expression) {
213
+ const tokens = [];
214
+ let position = 0;
215
+ while (position < expression.length) {
216
+ const char = expression[position];
217
+ if (char === " " || char === "\t" || char === "\n") {
218
+ position++;
219
+ continue;
220
+ }
221
+ // Number literal (with optional decimal and exponent)
222
+ if (char >= "0" && char <= "9") {
223
+ let numberBuffer = "";
224
+ while (position < expression.length &&
225
+ MetricFormulaEvaluator.isNumberChar(expression[position], numberBuffer)) {
226
+ numberBuffer += expression[position];
227
+ position++;
228
+ }
229
+ tokens.push({ type: TokenType.Number, value: numberBuffer });
230
+ continue;
231
+ }
232
+ // Decimal starting without leading 0 (e.g. ".5")
233
+ if (char === "." &&
234
+ position + 1 < expression.length &&
235
+ expression[position + 1] >= "0" &&
236
+ expression[position + 1] <= "9") {
237
+ let numberBuffer = "";
238
+ while (position < expression.length &&
239
+ MetricFormulaEvaluator.isNumberChar(expression[position], numberBuffer)) {
240
+ numberBuffer += expression[position];
241
+ position++;
242
+ }
243
+ tokens.push({ type: TokenType.Number, value: numberBuffer });
244
+ continue;
245
+ }
246
+ // Variable — may be prefixed with "$" or bare ("a", "b1", etc.)
247
+ if (char === "$" || MetricFormulaEvaluator.isIdentifierStart(char)) {
248
+ if (char === "$") {
249
+ position++;
250
+ }
251
+ let identifier = "";
252
+ while (position < expression.length &&
253
+ MetricFormulaEvaluator.isIdentifierPart(expression[position])) {
254
+ identifier += expression[position];
255
+ position++;
256
+ }
257
+ if (!identifier) {
258
+ throw new BadDataException(`Unexpected character "$" without a variable name.`);
259
+ }
260
+ tokens.push({ type: TokenType.Variable, value: identifier });
261
+ continue;
262
+ }
263
+ if (char === "(") {
264
+ tokens.push({ type: TokenType.LeftParen, value: char });
265
+ position++;
266
+ continue;
267
+ }
268
+ if (char === ")") {
269
+ tokens.push({ type: TokenType.RightParen, value: char });
270
+ position++;
271
+ continue;
272
+ }
273
+ if (char === "+" ||
274
+ char === "-" ||
275
+ char === "*" ||
276
+ char === "/" ||
277
+ char === "%" ||
278
+ char === "^") {
279
+ tokens.push({ type: TokenType.Operator, value: char });
280
+ position++;
281
+ continue;
282
+ }
283
+ throw new BadDataException(`Unexpected character "${char}" at position ${position}.`);
284
+ }
285
+ return tokens;
286
+ }
287
+ static isNumberChar(char, currentBuffer) {
288
+ if (char >= "0" && char <= "9") {
289
+ return true;
290
+ }
291
+ if (char === "." && !currentBuffer.includes(".")) {
292
+ return true;
293
+ }
294
+ if ((char === "e" || char === "E") &&
295
+ !currentBuffer.toLowerCase().includes("e") &&
296
+ currentBuffer.length > 0) {
297
+ return true;
298
+ }
299
+ if ((char === "+" || char === "-") &&
300
+ currentBuffer.length > 0 &&
301
+ (currentBuffer[currentBuffer.length - 1] === "e" ||
302
+ currentBuffer[currentBuffer.length - 1] === "E")) {
303
+ return true;
304
+ }
305
+ return false;
306
+ }
307
+ static isIdentifierStart(char) {
308
+ return ((char >= "a" && char <= "z") ||
309
+ (char >= "A" && char <= "Z") ||
310
+ char === "_");
311
+ }
312
+ static isIdentifierPart(char) {
313
+ return (MetricFormulaEvaluator.isIdentifierStart(char) ||
314
+ (char >= "0" && char <= "9"));
315
+ }
316
+ static toRpn(expression) {
317
+ const tokens = MetricFormulaEvaluator.tokenize(expression);
318
+ const output = [];
319
+ const operatorStack = [];
320
+ let previousToken = null;
321
+ for (const token of tokens) {
322
+ if (token.type === TokenType.Number) {
323
+ output.push(token);
324
+ }
325
+ else if (token.type === TokenType.Variable) {
326
+ output.push(token);
327
+ }
328
+ else if (token.type === TokenType.Operator) {
329
+ let operatorValue = token.value;
330
+ const isUnary = (operatorValue === "+" || operatorValue === "-") &&
331
+ (previousToken === null ||
332
+ previousToken.type === TokenType.Operator ||
333
+ previousToken.type === TokenType.LeftParen);
334
+ if (isUnary) {
335
+ operatorValue = operatorValue === "-" ? "u-" : "u+";
336
+ }
337
+ while (operatorStack.length > 0) {
338
+ const top = operatorStack[operatorStack.length - 1];
339
+ if (top.type !== TokenType.Operator) {
340
+ break;
341
+ }
342
+ const topPrecedence = OPERATOR_PRECEDENCE[top.value];
343
+ const currentPrecedence = OPERATOR_PRECEDENCE[operatorValue];
344
+ const isRightAssociative = RIGHT_ASSOCIATIVE.has(operatorValue);
345
+ if (topPrecedence > currentPrecedence ||
346
+ (topPrecedence === currentPrecedence && !isRightAssociative)) {
347
+ output.push(operatorStack.pop());
348
+ }
349
+ else {
350
+ break;
351
+ }
352
+ }
353
+ operatorStack.push({ type: TokenType.Operator, value: operatorValue });
354
+ }
355
+ else if (token.type === TokenType.LeftParen) {
356
+ operatorStack.push(token);
357
+ }
358
+ else if (token.type === TokenType.RightParen) {
359
+ let foundLeftParen = false;
360
+ while (operatorStack.length > 0) {
361
+ const top = operatorStack.pop();
362
+ if (top.type === TokenType.LeftParen) {
363
+ foundLeftParen = true;
364
+ break;
365
+ }
366
+ output.push(top);
367
+ }
368
+ if (!foundLeftParen) {
369
+ throw new BadDataException("Mismatched parentheses in formula.");
370
+ }
371
+ }
372
+ previousToken = token;
373
+ }
374
+ while (operatorStack.length > 0) {
375
+ const top = operatorStack.pop();
376
+ if (top.type === TokenType.LeftParen ||
377
+ top.type === TokenType.RightParen) {
378
+ throw new BadDataException("Mismatched parentheses in formula.");
379
+ }
380
+ output.push(top);
381
+ }
382
+ return output;
383
+ }
384
+ static evaluateRpn(rpn, variableValues) {
385
+ const stack = [];
386
+ for (const token of rpn) {
387
+ if (token.type === TokenType.Number) {
388
+ stack.push(parseFloat(token.value));
389
+ continue;
390
+ }
391
+ if (token.type === TokenType.Variable) {
392
+ const variableName = token.value.toLowerCase();
393
+ const variableValue = variableValues[variableName];
394
+ if (typeof variableValue !== "number") {
395
+ throw new BadDataException(`Missing value for variable "${variableName}".`);
396
+ }
397
+ stack.push(variableValue);
398
+ continue;
399
+ }
400
+ if (token.type === TokenType.Operator) {
401
+ const operatorValue = token.value;
402
+ if (operatorValue === "u-" || operatorValue === "u+") {
403
+ const operand = stack.pop();
404
+ if (typeof operand !== "number") {
405
+ throw new BadDataException("Invalid formula: missing operand.");
406
+ }
407
+ stack.push(operatorValue === "u-" ? -operand : operand);
408
+ continue;
409
+ }
410
+ const right = stack.pop();
411
+ const left = stack.pop();
412
+ if (typeof right !== "number" || typeof left !== "number") {
413
+ throw new BadDataException("Invalid formula: missing operands.");
414
+ }
415
+ switch (operatorValue) {
416
+ case "+":
417
+ stack.push(left + right);
418
+ break;
419
+ case "-":
420
+ stack.push(left - right);
421
+ break;
422
+ case "*":
423
+ stack.push(left * right);
424
+ break;
425
+ case "/":
426
+ if (right === 0) {
427
+ stack.push(NaN);
428
+ }
429
+ else {
430
+ stack.push(left / right);
431
+ }
432
+ break;
433
+ case "%":
434
+ if (right === 0) {
435
+ stack.push(NaN);
436
+ }
437
+ else {
438
+ stack.push(left % right);
439
+ }
440
+ break;
441
+ case "^":
442
+ stack.push(Math.pow(left, right));
443
+ break;
444
+ }
445
+ }
446
+ }
447
+ if (stack.length !== 1) {
448
+ throw new BadDataException("Invalid formula expression.");
449
+ }
450
+ return stack[0];
451
+ }
452
+ }
453
+ //# sourceMappingURL=MetricFormulaEvaluator.js.map