@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,359 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Recording-rule expression DSL.
|
|
3
|
+
*
|
|
4
|
+
* Grammar (recursive descent):
|
|
5
|
+
*
|
|
6
|
+
* expr := term (('+' | '-') term)*
|
|
7
|
+
* term := factor (('*' | '/') factor)*
|
|
8
|
+
* factor := number | identifier | '(' expr ')' | '-' factor
|
|
9
|
+
* number := digits ('.' digits)?
|
|
10
|
+
* identifier := [A-Za-z_][A-Za-z0-9_]*
|
|
11
|
+
*
|
|
12
|
+
* Evaluation returns `null` when the result is not a finite real number —
|
|
13
|
+
* division by zero, missing binding, overflow, or NaN. Callers treat `null`
|
|
14
|
+
* as "skip this bucket".
|
|
15
|
+
*
|
|
16
|
+
* Intentionally small: no function calls, no power operator, no comparisons.
|
|
17
|
+
* A future version can grow this DSL without breaking stored rules because
|
|
18
|
+
* the whole expression is one string column on the model.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const MAX_DEPTH: number = 32;
|
|
22
|
+
|
|
23
|
+
export interface Token {
|
|
24
|
+
type: "number" | "ident" | "op" | "lparen" | "rparen";
|
|
25
|
+
value: string;
|
|
26
|
+
pos: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type Node =
|
|
30
|
+
| { type: "num"; value: number }
|
|
31
|
+
| { type: "ident"; name: string }
|
|
32
|
+
| { type: "unary"; op: "-"; operand: Node }
|
|
33
|
+
| { type: "binary"; op: "+" | "-" | "*" | "/"; left: Node; right: Node };
|
|
34
|
+
|
|
35
|
+
export interface ParseResult {
|
|
36
|
+
ok: true;
|
|
37
|
+
ast: Node;
|
|
38
|
+
identifiers: Array<string>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ParseError {
|
|
42
|
+
ok: false;
|
|
43
|
+
error: string;
|
|
44
|
+
position?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ValidateResult {
|
|
48
|
+
ok: boolean;
|
|
49
|
+
error?: string;
|
|
50
|
+
position?: number;
|
|
51
|
+
unknownIdentifiers?: Array<string>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function tokenize(input: string): Array<Token> | ParseError {
|
|
55
|
+
const tokens: Array<Token> = [];
|
|
56
|
+
let i: number = 0;
|
|
57
|
+
while (i < input.length) {
|
|
58
|
+
const ch: string = input[i] as string;
|
|
59
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r") {
|
|
60
|
+
i++;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (ch === "(") {
|
|
64
|
+
tokens.push({ type: "lparen", value: ch, pos: i });
|
|
65
|
+
i++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (ch === ")") {
|
|
69
|
+
tokens.push({ type: "rparen", value: ch, pos: i });
|
|
70
|
+
i++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (ch === "+" || ch === "-" || ch === "*" || ch === "/") {
|
|
74
|
+
tokens.push({ type: "op", value: ch, pos: i });
|
|
75
|
+
i++;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (ch >= "0" && ch <= "9") {
|
|
79
|
+
const start: number = i;
|
|
80
|
+
while (i < input.length && input[i]! >= "0" && input[i]! <= "9") {
|
|
81
|
+
i++;
|
|
82
|
+
}
|
|
83
|
+
if (input[i] === ".") {
|
|
84
|
+
i++;
|
|
85
|
+
while (i < input.length && input[i]! >= "0" && input[i]! <= "9") {
|
|
86
|
+
i++;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
tokens.push({ type: "number", value: input.slice(start, i), pos: start });
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (isIdentStart(ch)) {
|
|
93
|
+
const start: number = i;
|
|
94
|
+
i++;
|
|
95
|
+
while (i < input.length && isIdentPart(input[i] as string)) {
|
|
96
|
+
i++;
|
|
97
|
+
}
|
|
98
|
+
tokens.push({ type: "ident", value: input.slice(start, i), pos: start });
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
error: `Unexpected character "${ch}" at position ${i}`,
|
|
104
|
+
position: i,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return tokens;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isIdentStart(ch: string): boolean {
|
|
111
|
+
return (ch >= "A" && ch <= "Z") || (ch >= "a" && ch <= "z") || ch === "_";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isIdentPart(ch: string): boolean {
|
|
115
|
+
return isIdentStart(ch) || (ch >= "0" && ch <= "9");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Recursive-descent parser.
|
|
119
|
+
export function parse(input: string): ParseResult | ParseError {
|
|
120
|
+
const tokenized: Array<Token> | ParseError = tokenize(input);
|
|
121
|
+
if (!Array.isArray(tokenized)) {
|
|
122
|
+
return tokenized;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const tokens: Array<Token> = tokenized;
|
|
126
|
+
const state: { i: number; depth: number } = { i: 0, depth: 0 };
|
|
127
|
+
const identifiers: Set<string> = new Set();
|
|
128
|
+
|
|
129
|
+
function peek(): Token | undefined {
|
|
130
|
+
return tokens[state.i];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function advance(): Token | undefined {
|
|
134
|
+
return tokens[state.i++];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function parseExpr(): Node | ParseError {
|
|
138
|
+
if (state.depth > MAX_DEPTH) {
|
|
139
|
+
return { ok: false, error: "Expression nesting too deep" };
|
|
140
|
+
}
|
|
141
|
+
state.depth++;
|
|
142
|
+
let left: Node | ParseError = parseTerm();
|
|
143
|
+
if ("ok" in left && left.ok === false) {
|
|
144
|
+
state.depth--;
|
|
145
|
+
return left;
|
|
146
|
+
}
|
|
147
|
+
while (true) {
|
|
148
|
+
const next: Token | undefined = peek();
|
|
149
|
+
if (next?.type === "op" && (next.value === "+" || next.value === "-")) {
|
|
150
|
+
advance();
|
|
151
|
+
const right: Node | ParseError = parseTerm();
|
|
152
|
+
if ("ok" in right && right.ok === false) {
|
|
153
|
+
state.depth--;
|
|
154
|
+
return right;
|
|
155
|
+
}
|
|
156
|
+
left = {
|
|
157
|
+
type: "binary",
|
|
158
|
+
op: next.value as "+" | "-",
|
|
159
|
+
left: left as Node,
|
|
160
|
+
right: right as Node,
|
|
161
|
+
};
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
state.depth--;
|
|
167
|
+
return left as Node;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function parseTerm(): Node | ParseError {
|
|
171
|
+
let left: Node | ParseError = parseFactor();
|
|
172
|
+
if ("ok" in left && left.ok === false) {
|
|
173
|
+
return left;
|
|
174
|
+
}
|
|
175
|
+
while (true) {
|
|
176
|
+
const next: Token | undefined = peek();
|
|
177
|
+
if (next?.type === "op" && (next.value === "*" || next.value === "/")) {
|
|
178
|
+
advance();
|
|
179
|
+
const right: Node | ParseError = parseFactor();
|
|
180
|
+
if ("ok" in right && right.ok === false) {
|
|
181
|
+
return right;
|
|
182
|
+
}
|
|
183
|
+
left = {
|
|
184
|
+
type: "binary",
|
|
185
|
+
op: next.value as "*" | "/",
|
|
186
|
+
left: left as Node,
|
|
187
|
+
right: right as Node,
|
|
188
|
+
};
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
return left as Node;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function parseFactor(): Node | ParseError {
|
|
197
|
+
const tok: Token | undefined = peek();
|
|
198
|
+
if (!tok) {
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
error: "Unexpected end of expression",
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (tok.type === "op" && tok.value === "-") {
|
|
205
|
+
advance();
|
|
206
|
+
const operand: Node | ParseError = parseFactor();
|
|
207
|
+
if ("ok" in operand && operand.ok === false) {
|
|
208
|
+
return operand;
|
|
209
|
+
}
|
|
210
|
+
return { type: "unary", op: "-", operand: operand as Node };
|
|
211
|
+
}
|
|
212
|
+
if (tok.type === "number") {
|
|
213
|
+
advance();
|
|
214
|
+
const n: number = Number(tok.value);
|
|
215
|
+
if (!Number.isFinite(n)) {
|
|
216
|
+
return {
|
|
217
|
+
ok: false,
|
|
218
|
+
error: `Invalid number "${tok.value}"`,
|
|
219
|
+
position: tok.pos,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
return { type: "num", value: n };
|
|
223
|
+
}
|
|
224
|
+
if (tok.type === "ident") {
|
|
225
|
+
advance();
|
|
226
|
+
identifiers.add(tok.value);
|
|
227
|
+
return { type: "ident", name: tok.value };
|
|
228
|
+
}
|
|
229
|
+
if (tok.type === "lparen") {
|
|
230
|
+
advance();
|
|
231
|
+
const inner: Node | ParseError = parseExpr();
|
|
232
|
+
if ("ok" in inner && inner.ok === false) {
|
|
233
|
+
return inner;
|
|
234
|
+
}
|
|
235
|
+
const close: Token | undefined = advance();
|
|
236
|
+
if (!close || close.type !== "rparen") {
|
|
237
|
+
return {
|
|
238
|
+
ok: false,
|
|
239
|
+
error: "Expected ')'",
|
|
240
|
+
...(close ? { position: close.pos } : {}),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
return inner as Node;
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
ok: false,
|
|
247
|
+
error: `Unexpected token "${tok.value}"`,
|
|
248
|
+
position: tok.pos,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const ast: Node | ParseError = parseExpr();
|
|
253
|
+
if ("ok" in ast && ast.ok === false) {
|
|
254
|
+
return ast;
|
|
255
|
+
}
|
|
256
|
+
if (state.i !== tokens.length) {
|
|
257
|
+
const stray: Token | undefined = tokens[state.i];
|
|
258
|
+
return {
|
|
259
|
+
ok: false,
|
|
260
|
+
error: `Unexpected token "${stray?.value ?? ""}" at position ${stray?.pos ?? -1}`,
|
|
261
|
+
...(stray ? { position: stray.pos } : {}),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
ok: true,
|
|
266
|
+
ast: ast as Node,
|
|
267
|
+
identifiers: Array.from(identifiers),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/*
|
|
272
|
+
* Evaluate an AST against the given variable bindings. Returns null when
|
|
273
|
+
* the result is not a finite real number (division by zero, missing binding,
|
|
274
|
+
* NaN, Infinity). The caller skips any output row whose result is null.
|
|
275
|
+
*/
|
|
276
|
+
export function evaluate(
|
|
277
|
+
node: Node,
|
|
278
|
+
bindings: Record<string, number>,
|
|
279
|
+
): number | null {
|
|
280
|
+
switch (node.type) {
|
|
281
|
+
case "num":
|
|
282
|
+
return Number.isFinite(node.value) ? node.value : null;
|
|
283
|
+
case "ident": {
|
|
284
|
+
const v: number | undefined = bindings[node.name];
|
|
285
|
+
if (typeof v !== "number" || !Number.isFinite(v)) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
return v;
|
|
289
|
+
}
|
|
290
|
+
case "unary": {
|
|
291
|
+
const inner: number | null = evaluate(node.operand, bindings);
|
|
292
|
+
if (inner === null) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
return -inner;
|
|
296
|
+
}
|
|
297
|
+
case "binary": {
|
|
298
|
+
const l: number | null = evaluate(node.left, bindings);
|
|
299
|
+
if (l === null) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
const r: number | null = evaluate(node.right, bindings);
|
|
303
|
+
if (r === null) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
let out: number;
|
|
307
|
+
switch (node.op) {
|
|
308
|
+
case "+":
|
|
309
|
+
out = l + r;
|
|
310
|
+
break;
|
|
311
|
+
case "-":
|
|
312
|
+
out = l - r;
|
|
313
|
+
break;
|
|
314
|
+
case "*":
|
|
315
|
+
out = l * r;
|
|
316
|
+
break;
|
|
317
|
+
case "/":
|
|
318
|
+
if (r === 0) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
out = l / r;
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
return Number.isFinite(out) ? out : null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/*
|
|
330
|
+
* Parse + check that every identifier in the expression is in `allowed`.
|
|
331
|
+
* Used at rule-save time so the form can fail fast before the cron runs.
|
|
332
|
+
*/
|
|
333
|
+
export function validate(
|
|
334
|
+
expression: string,
|
|
335
|
+
allowed: Array<string>,
|
|
336
|
+
): ValidateResult {
|
|
337
|
+
const parsed: ParseResult | ParseError = parse(expression);
|
|
338
|
+
if (!parsed.ok) {
|
|
339
|
+
return {
|
|
340
|
+
ok: false,
|
|
341
|
+
error: parsed.error,
|
|
342
|
+
...(typeof parsed.position === "number"
|
|
343
|
+
? { position: parsed.position }
|
|
344
|
+
: {}),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
const allowedSet: Set<string> = new Set(allowed);
|
|
348
|
+
const unknown: Array<string> = parsed.identifiers.filter((id: string) => {
|
|
349
|
+
return !allowedSet.has(id);
|
|
350
|
+
});
|
|
351
|
+
if (unknown.length > 0) {
|
|
352
|
+
return {
|
|
353
|
+
ok: false,
|
|
354
|
+
error: `Unknown identifier(s): ${unknown.join(", ")}`,
|
|
355
|
+
unknownIdentifiers: unknown,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
return { ok: true };
|
|
359
|
+
}
|
package/Utils/ValueFormatter.ts
CHANGED
|
@@ -25,34 +25,34 @@ const byteUnits: Array<UnitThreshold> = [
|
|
|
25
25
|
];
|
|
26
26
|
|
|
27
27
|
const secondUnits: Array<UnitThreshold> = [
|
|
28
|
-
{ threshold: 86400, unit: "
|
|
29
|
-
{ threshold: 3600, unit: "
|
|
28
|
+
{ threshold: 86400, unit: "days", divisor: 86400 },
|
|
29
|
+
{ threshold: 3600, unit: "hours", divisor: 3600 },
|
|
30
30
|
{ threshold: 60, unit: "min", divisor: 60 },
|
|
31
|
-
{ threshold: 1, unit: "
|
|
31
|
+
{ threshold: 1, unit: "sec", divisor: 1 },
|
|
32
32
|
{ threshold: 0.001, unit: "ms", divisor: 0.001 },
|
|
33
33
|
{ threshold: 0.000001, unit: "µs", divisor: 0.000001 },
|
|
34
34
|
{ threshold: 0, unit: "ns", divisor: 0.000000001 },
|
|
35
35
|
];
|
|
36
36
|
|
|
37
37
|
const millisecondUnits: Array<UnitThreshold> = [
|
|
38
|
-
{ threshold: 86400000, unit: "
|
|
39
|
-
{ threshold: 3600000, unit: "
|
|
38
|
+
{ threshold: 86400000, unit: "days", divisor: 86400000 },
|
|
39
|
+
{ threshold: 3600000, unit: "hours", divisor: 3600000 },
|
|
40
40
|
{ threshold: 60000, unit: "min", divisor: 60000 },
|
|
41
|
-
{ threshold: 1000, unit: "
|
|
41
|
+
{ threshold: 1000, unit: "sec", divisor: 1000 },
|
|
42
42
|
{ threshold: 1, unit: "ms", divisor: 1 },
|
|
43
43
|
{ threshold: 0.001, unit: "µs", divisor: 0.001 },
|
|
44
44
|
{ threshold: 0, unit: "ns", divisor: 0.000001 },
|
|
45
45
|
];
|
|
46
46
|
|
|
47
47
|
const microsecondUnits: Array<UnitThreshold> = [
|
|
48
|
-
{ threshold: 1e6, unit: "
|
|
48
|
+
{ threshold: 1e6, unit: "sec", divisor: 1e6 },
|
|
49
49
|
{ threshold: 1e3, unit: "ms", divisor: 1e3 },
|
|
50
50
|
{ threshold: 1, unit: "µs", divisor: 1 },
|
|
51
51
|
{ threshold: 0, unit: "ns", divisor: 0.001 },
|
|
52
52
|
];
|
|
53
53
|
|
|
54
54
|
const nanosecondUnits: Array<UnitThreshold> = [
|
|
55
|
-
{ threshold: 1e9, unit: "
|
|
55
|
+
{ threshold: 1e9, unit: "sec", divisor: 1e9 },
|
|
56
56
|
{ threshold: 1e6, unit: "ms", divisor: 1e6 },
|
|
57
57
|
{ threshold: 1e3, unit: "µs", divisor: 1e3 },
|
|
58
58
|
{ threshold: 0, unit: "ns", divisor: 1 },
|
|
@@ -89,6 +89,100 @@ const unitTableMap: Record<string, Array<UnitThreshold>> = {
|
|
|
89
89
|
ns: nanosecondUnits,
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
+
/*
|
|
93
|
+
* Maps UCUM / OpenTelemetry unit codes to human-readable unit names.
|
|
94
|
+
* Used for labelling (e.g. badges) where the raw code "By" is hard to read.
|
|
95
|
+
*/
|
|
96
|
+
const readableUnitMap: Record<string, string> = {
|
|
97
|
+
// Dimensionless — OpenTelemetry uses "1" for unitless counts
|
|
98
|
+
"1": "",
|
|
99
|
+
|
|
100
|
+
// Bytes (decimal)
|
|
101
|
+
by: "Bytes",
|
|
102
|
+
byte: "Bytes",
|
|
103
|
+
bytes: "Bytes",
|
|
104
|
+
b: "Bytes",
|
|
105
|
+
kby: "Kilobytes",
|
|
106
|
+
mby: "Megabytes",
|
|
107
|
+
gby: "Gigabytes",
|
|
108
|
+
tby: "Terabytes",
|
|
109
|
+
pby: "Petabytes",
|
|
110
|
+
|
|
111
|
+
// Bytes (binary)
|
|
112
|
+
kiby: "Kibibytes",
|
|
113
|
+
miby: "Mebibytes",
|
|
114
|
+
giby: "Gibibytes",
|
|
115
|
+
tiby: "Tebibytes",
|
|
116
|
+
piby: "Pebibytes",
|
|
117
|
+
|
|
118
|
+
// Bits
|
|
119
|
+
bit: "Bits",
|
|
120
|
+
bits: "Bits",
|
|
121
|
+
kbit: "Kilobits",
|
|
122
|
+
mbit: "Megabits",
|
|
123
|
+
gbit: "Gigabits",
|
|
124
|
+
|
|
125
|
+
// Time
|
|
126
|
+
ns: "Nanoseconds",
|
|
127
|
+
nanosecond: "Nanoseconds",
|
|
128
|
+
nanoseconds: "Nanoseconds",
|
|
129
|
+
us: "Microseconds",
|
|
130
|
+
µs: "Microseconds",
|
|
131
|
+
microsecond: "Microseconds",
|
|
132
|
+
microseconds: "Microseconds",
|
|
133
|
+
ms: "Milliseconds",
|
|
134
|
+
millisecond: "Milliseconds",
|
|
135
|
+
milliseconds: "Milliseconds",
|
|
136
|
+
s: "Seconds",
|
|
137
|
+
sec: "Seconds",
|
|
138
|
+
second: "Seconds",
|
|
139
|
+
seconds: "Seconds",
|
|
140
|
+
min: "Minutes",
|
|
141
|
+
minute: "Minutes",
|
|
142
|
+
minutes: "Minutes",
|
|
143
|
+
h: "Hours",
|
|
144
|
+
hr: "Hours",
|
|
145
|
+
hour: "Hours",
|
|
146
|
+
hours: "Hours",
|
|
147
|
+
d: "Days",
|
|
148
|
+
day: "Days",
|
|
149
|
+
days: "Days",
|
|
150
|
+
|
|
151
|
+
// Percent
|
|
152
|
+
"%": "Percent",
|
|
153
|
+
percent: "Percent",
|
|
154
|
+
|
|
155
|
+
// Frequency
|
|
156
|
+
hz: "Hertz",
|
|
157
|
+
khz: "Kilohertz",
|
|
158
|
+
mhz: "Megahertz",
|
|
159
|
+
ghz: "Gigahertz",
|
|
160
|
+
|
|
161
|
+
// Temperature
|
|
162
|
+
cel: "Celsius",
|
|
163
|
+
"[degf]": "Fahrenheit",
|
|
164
|
+
k: "Kelvin",
|
|
165
|
+
|
|
166
|
+
// Electrical
|
|
167
|
+
v: "Volts",
|
|
168
|
+
a: "Amperes",
|
|
169
|
+
w: "Watts",
|
|
170
|
+
kw: "Kilowatts",
|
|
171
|
+
j: "Joules",
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
function normalizeUnit(unit: string): string {
|
|
175
|
+
return unit.trim().toLowerCase();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function getReadableUnitPart(unit: string): string {
|
|
179
|
+
const normalized: string = normalizeUnit(unit);
|
|
180
|
+
if (readableUnitMap[normalized] !== undefined) {
|
|
181
|
+
return readableUnitMap[normalized]!;
|
|
182
|
+
}
|
|
183
|
+
return unit;
|
|
184
|
+
}
|
|
185
|
+
|
|
92
186
|
function formatWithThresholds(
|
|
93
187
|
value: number,
|
|
94
188
|
thresholds: Array<UnitThreshold>,
|
|
@@ -144,11 +238,12 @@ export default class ValueFormatter {
|
|
|
144
238
|
* e.g. formatValue(42, "%") → "42 %" (passthrough for unknown units)
|
|
145
239
|
*/
|
|
146
240
|
public static formatValue(value: number, unit: string): string {
|
|
147
|
-
|
|
241
|
+
// OpenTelemetry uses "1" as the dimensionless marker — render as a bare number.
|
|
242
|
+
if (!unit || unit.trim() === "" || unit.trim() === "1") {
|
|
148
243
|
return formatNumber(value);
|
|
149
244
|
}
|
|
150
245
|
|
|
151
|
-
const normalizedUnit: string = unit
|
|
246
|
+
const normalizedUnit: string = normalizeUnit(unit);
|
|
152
247
|
const thresholds: Array<UnitThreshold> | undefined =
|
|
153
248
|
unitTableMap[normalizedUnit];
|
|
154
249
|
|
|
@@ -156,8 +251,8 @@ export default class ValueFormatter {
|
|
|
156
251
|
return formatWithThresholds(value, thresholds).formatted;
|
|
157
252
|
}
|
|
158
253
|
|
|
159
|
-
// Unknown unit —
|
|
160
|
-
return `${formatNumber(value)} ${unit}`;
|
|
254
|
+
// Unknown unit — format number and show the readable unit name when we have one.
|
|
255
|
+
return `${formatNumber(value)} ${ValueFormatter.getReadableUnit(unit)}`;
|
|
161
256
|
}
|
|
162
257
|
|
|
163
258
|
// Check if a unit is one we can auto-scale (bytes, seconds, etc.)
|
|
@@ -165,6 +260,35 @@ export default class ValueFormatter {
|
|
|
165
260
|
if (!unit || unit.trim() === "") {
|
|
166
261
|
return false;
|
|
167
262
|
}
|
|
168
|
-
return unitTableMap[unit
|
|
263
|
+
return unitTableMap[normalizeUnit(unit)] !== undefined;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/*
|
|
267
|
+
* Convert a UCUM / OpenTelemetry unit code into a human-readable name.
|
|
268
|
+
* e.g. "By" → "Bytes", "s" → "Seconds", "By/s" → "Bytes per Second",
|
|
269
|
+
* "1" → "" (dimensionless). Falls back to the original string when unknown.
|
|
270
|
+
*/
|
|
271
|
+
public static getReadableUnit(unit: string): string {
|
|
272
|
+
if (!unit || unit.trim() === "" || unit.trim() === "1") {
|
|
273
|
+
return "";
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Handle compound rate units like "By/s" → "Bytes per Second"
|
|
277
|
+
if (unit.includes("/")) {
|
|
278
|
+
const parts: Array<string> = unit.split("/");
|
|
279
|
+
const readableParts: Array<string> = parts.map((part: string): string => {
|
|
280
|
+
return getReadableUnitPart(part);
|
|
281
|
+
});
|
|
282
|
+
// Singularize the denominator ("Seconds" → "Second") for nicer rate reading.
|
|
283
|
+
const [numerator, ...denominators] = readableParts;
|
|
284
|
+
const denominator: string = denominators
|
|
285
|
+
.map((d: string): string => {
|
|
286
|
+
return d.endsWith("s") ? d.slice(0, -1) : d;
|
|
287
|
+
})
|
|
288
|
+
.join(" per ");
|
|
289
|
+
return `${numerator} per ${denominator}`;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return getReadableUnitPart(unit);
|
|
169
293
|
}
|
|
170
294
|
}
|