@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.
- package/Models/DatabaseModels/DockerHostOwnerTeam.ts +464 -0
- package/Models/DatabaseModels/DockerHostOwnerUser.ts +463 -0
- package/Models/DatabaseModels/Index.ts +24 -0
- package/Models/DatabaseModels/KubernetesClusterOwnerTeam.ts +464 -0
- package/Models/DatabaseModels/KubernetesClusterOwnerUser.ts +463 -0
- package/Models/DatabaseModels/KubernetesResource.ts +548 -0
- package/Models/DatabaseModels/MetricPipelineRule.ts +804 -0
- package/Models/DatabaseModels/MetricRecordingRule.ts +470 -0
- package/Models/DatabaseModels/Monitor.ts +2 -0
- package/Models/DatabaseModels/Project.ts +53 -0
- package/Models/DatabaseModels/Service.ts +79 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +508 -0
- package/Models/DatabaseModels/TracePipeline.ts +436 -0
- package/Models/DatabaseModels/TracePipelineProcessor.ts +454 -0
- package/Models/DatabaseModels/TraceRecordingRule.ts +470 -0
- package/Models/DatabaseModels/TraceScrubRule.ts +546 -0
- package/Server/API/KubernetesResourceAPI.ts +129 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.ts +399 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.ts +205 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.ts +335 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.ts +53 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -1
- package/Server/Services/DockerHostOwnerTeamService.ts +10 -0
- package/Server/Services/DockerHostOwnerUserService.ts +10 -0
- package/Server/Services/KubernetesClusterOwnerTeamService.ts +10 -0
- package/Server/Services/KubernetesClusterOwnerUserService.ts +10 -0
- package/Server/Services/KubernetesResourceService.ts +351 -0
- package/Server/Services/MetricPipelineRuleService.ts +10 -0
- package/Server/Services/MetricRecordingRuleService.ts +10 -0
- package/Server/Services/TraceDropFilterService.ts +10 -0
- package/Server/Services/TracePipelineProcessorService.ts +10 -0
- package/Server/Services/TracePipelineService.ts +10 -0
- package/Server/Services/TraceRecordingRuleService.ts +10 -0
- package/Server/Services/TraceScrubRuleService.ts +10 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +71 -9
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +483 -75
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +379 -6
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +502 -0
- package/Tests/Utils/MetricUnitUtil.test.ts +216 -0
- package/Tests/Utils/Metrics/MetricFormulaEvaluator.test.ts +269 -0
- package/Tests/Utils/Metrics/MetricResultUnitConverter.test.ts +231 -0
- package/Tests/Utils/RecordingRuleExpression.test.ts +177 -0
- package/Types/Kubernetes/KubernetesInventoryExtractor.ts +327 -0
- package/Types/Kubernetes/KubernetesObjectParser.ts +1949 -0
- package/Types/Metrics/MetricDownsamplingRetentionDays.ts +49 -0
- package/Types/Metrics/MetricFormulaConfigData.ts +4 -0
- package/Types/Metrics/MetricPipelineRuleFilterCondition.ts +136 -0
- package/Types/Metrics/MetricPipelineRuleType.ts +27 -0
- package/Types/Metrics/RecordingRuleDefinition.ts +180 -0
- package/Types/Monitor/CriteriaFilter.ts +43 -0
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +70 -0
- package/Types/Permission.ts +520 -0
- package/Types/Trace/TraceAggregationType.ts +17 -0
- package/Types/Trace/TraceDropFilterAction.ts +6 -0
- package/Types/Trace/TracePipelineProcessorType.ts +56 -0
- package/Types/Trace/TraceRecordingRuleDefinition.ts +218 -0
- package/Types/Trace/TraceScrubAction.ts +7 -0
- package/Types/Trace/TraceScrubField.ts +8 -0
- package/Types/Trace/TraceScrubPatternType.ts +10 -0
- package/UI/Components/CardSelect/CardSelect.tsx +9 -1
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +6 -10
- package/UI/Components/Forms/Fields/FormField.tsx +1 -0
- package/UI/Components/Forms/Types/Field.ts +1 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +57 -0
- package/UI/Components/Page/Page.tsx +6 -0
- package/Utils/MetricUnitUtil.ts +289 -0
- package/Utils/Metrics/MetricFormulaEvaluator.ts +610 -0
- package/Utils/Metrics/MetricResultUnitConverter.ts +91 -0
- package/Utils/Metrics/RecordingRuleExpression.ts +359 -0
- package/Utils/ValueFormatter.ts +137 -13
- package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js +480 -0
- package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js +479 -0
- package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +24 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js +480 -0
- package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js +479 -0
- package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesResource.js +590 -0
- package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MetricPipelineRule.js +836 -0
- package/build/dist/Models/DatabaseModels/MetricPipelineRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MetricRecordingRule.js +497 -0
- package/build/dist/Models/DatabaseModels/MetricRecordingRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Monitor.js +2 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +53 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +79 -0
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +536 -0
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -0
- package/build/dist/Models/DatabaseModels/TracePipeline.js +462 -0
- package/build/dist/Models/DatabaseModels/TracePipeline.js.map +1 -0
- package/build/dist/Models/DatabaseModels/TracePipelineProcessor.js +476 -0
- package/build/dist/Models/DatabaseModels/TracePipelineProcessor.js.map +1 -0
- package/build/dist/Models/DatabaseModels/TraceRecordingRule.js +497 -0
- package/build/dist/Models/DatabaseModels/TraceRecordingRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/TraceScrubRule.js +575 -0
- package/build/dist/Models/DatabaseModels/TraceScrubRule.js.map +1 -0
- package/build/dist/Server/API/KubernetesResourceAPI.js +98 -0
- package/build/dist/Server/API/KubernetesResourceAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.js +144 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.js +82 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.js +118 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/DockerHostOwnerTeamService.js +9 -0
- package/build/dist/Server/Services/DockerHostOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/DockerHostOwnerUserService.js +9 -0
- package/build/dist/Server/Services/DockerHostOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterOwnerTeamService.js +9 -0
- package/build/dist/Server/Services/KubernetesClusterOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterOwnerUserService.js +9 -0
- package/build/dist/Server/Services/KubernetesClusterOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesResourceService.js +237 -0
- package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -0
- package/build/dist/Server/Services/MetricPipelineRuleService.js +9 -0
- package/build/dist/Server/Services/MetricPipelineRuleService.js.map +1 -0
- package/build/dist/Server/Services/MetricRecordingRuleService.js +9 -0
- package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -0
- package/build/dist/Server/Services/TraceDropFilterService.js +9 -0
- package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -0
- package/build/dist/Server/Services/TracePipelineProcessorService.js +9 -0
- package/build/dist/Server/Services/TracePipelineProcessorService.js.map +1 -0
- package/build/dist/Server/Services/TracePipelineService.js +9 -0
- package/build/dist/Server/Services/TracePipelineService.js.map +1 -0
- package/build/dist/Server/Services/TraceRecordingRuleService.js +9 -0
- package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -0
- package/build/dist/Server/Services/TraceScrubRuleService.js +9 -0
- package/build/dist/Server/Services/TraceScrubRuleService.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +56 -9
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +335 -53
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +277 -5
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js +407 -0
- package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js.map +1 -0
- package/build/dist/Tests/Utils/MetricUnitUtil.test.js +159 -0
- package/build/dist/Tests/Utils/MetricUnitUtil.test.js.map +1 -0
- package/build/dist/Tests/Utils/Metrics/MetricFormulaEvaluator.test.js +224 -0
- package/build/dist/Tests/Utils/Metrics/MetricFormulaEvaluator.test.js.map +1 -0
- package/build/dist/Tests/Utils/Metrics/MetricResultUnitConverter.test.js +180 -0
- package/build/dist/Tests/Utils/Metrics/MetricResultUnitConverter.test.js.map +1 -0
- package/build/dist/Tests/Utils/RecordingRuleExpression.test.js +142 -0
- package/build/dist/Tests/Utils/RecordingRuleExpression.test.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js +200 -0
- package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesObjectParser.js +1205 -0
- package/build/dist/Types/Kubernetes/KubernetesObjectParser.js.map +1 -0
- package/build/dist/Types/Metrics/MetricDownsamplingRetentionDays.js +32 -0
- package/build/dist/Types/Metrics/MetricDownsamplingRetentionDays.js.map +1 -0
- package/build/dist/Types/Metrics/MetricPipelineRuleFilterCondition.js +103 -0
- package/build/dist/Types/Metrics/MetricPipelineRuleFilterCondition.js.map +1 -0
- package/build/dist/Types/Metrics/MetricPipelineRuleType.js +27 -0
- package/build/dist/Types/Metrics/MetricPipelineRuleType.js.map +1 -0
- package/build/dist/Types/Metrics/RecordingRuleDefinition.js +110 -0
- package/build/dist/Types/Metrics/RecordingRuleDefinition.js.map +1 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js +22 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/MetricMonitor/MetricCriteriaContext.js +2 -0
- package/build/dist/Types/Monitor/MetricMonitor/MetricCriteriaContext.js.map +1 -0
- package/build/dist/Types/Permission.js +454 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Trace/TraceAggregationType.js +18 -0
- package/build/dist/Types/Trace/TraceAggregationType.js.map +1 -0
- package/build/dist/Types/Trace/TraceDropFilterAction.js +7 -0
- package/build/dist/Types/Trace/TraceDropFilterAction.js.map +1 -0
- package/build/dist/Types/Trace/TracePipelineProcessorType.js +10 -0
- package/build/dist/Types/Trace/TracePipelineProcessorType.js.map +1 -0
- package/build/dist/Types/Trace/TraceRecordingRuleDefinition.js +145 -0
- package/build/dist/Types/Trace/TraceRecordingRuleDefinition.js.map +1 -0
- package/build/dist/Types/Trace/TraceScrubAction.js +8 -0
- package/build/dist/Types/Trace/TraceScrubAction.js.map +1 -0
- package/build/dist/Types/Trace/TraceScrubField.js +9 -0
- package/build/dist/Types/Trace/TraceScrubField.js.map +1 -0
- package/build/dist/Types/Trace/TraceScrubPatternType.js +11 -0
- package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -0
- package/build/dist/UI/Components/CardSelect/CardSelect.js +3 -1
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +6 -9
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +30 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Page/Page.js +1 -0
- package/build/dist/UI/Components/Page/Page.js.map +1 -1
- package/build/dist/Utils/MetricUnitUtil.js +232 -0
- package/build/dist/Utils/MetricUnitUtil.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricFormulaEvaluator.js +453 -0
- package/build/dist/Utils/Metrics/MetricFormulaEvaluator.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricResultUnitConverter.js +61 -0
- package/build/dist/Utils/Metrics/MetricResultUnitConverter.js.map +1 -0
- package/build/dist/Utils/Metrics/RecordingRuleExpression.js +298 -0
- package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -0
- package/build/dist/Utils/ValueFormatter.js +123 -13
- package/build/dist/Utils/ValueFormatter.js.map +1 -1
- 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
|