@miragon/pulse-analytics-client 0.0.0-bootstrap.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +83 -3
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/metric-names.d.ts +43 -0
- package/dist/metric-names.d.ts.map +1 -0
- package/dist/metric-names.js +53 -0
- package/dist/metric-names.js.map +1 -0
- package/dist/metrics-contract-labels.test.d.ts +2 -0
- package/dist/metrics-contract-labels.test.d.ts.map +1 -0
- package/dist/metrics-contract-labels.test.js +169 -0
- package/dist/metrics-contract-labels.test.js.map +1 -0
- package/dist/metrics-contract.test.d.ts +2 -0
- package/dist/metrics-contract.test.d.ts.map +1 -0
- package/dist/metrics-contract.test.js +247 -0
- package/dist/metrics-contract.test.js.map +1 -0
- package/dist/prometheus-client.test.d.ts +2 -0
- package/dist/prometheus-client.test.d.ts.map +1 -0
- package/dist/prometheus-client.test.js +265 -0
- package/dist/prometheus-client.test.js.map +1 -0
- package/dist/prometheus.d.ts +100 -0
- package/dist/prometheus.d.ts.map +1 -0
- package/dist/prometheus.js +295 -0
- package/dist/prometheus.js.map +1 -0
- package/dist/prometheus.test.d.ts +2 -0
- package/dist/prometheus.test.d.ts.map +1 -0
- package/dist/prometheus.test.js +54 -0
- package/dist/prometheus.test.js.map +1 -0
- package/dist/promql-labels.test-support.d.ts +53 -0
- package/dist/promql-labels.test-support.d.ts.map +1 -0
- package/dist/promql-labels.test-support.js +237 -0
- package/dist/promql-labels.test-support.js.map +1 -0
- package/dist/promql-labels.test.d.ts +2 -0
- package/dist/promql-labels.test.d.ts.map +1 -0
- package/dist/promql-labels.test.js +190 -0
- package/dist/promql-labels.test.js.map +1 -0
- package/dist/promql-parse.test-support.d.ts +57 -0
- package/dist/promql-parse.test-support.d.ts.map +1 -0
- package/dist/promql-parse.test-support.js +284 -0
- package/dist/promql-parse.test-support.js.map +1 -0
- package/dist/promql-reading-rules.test-support.d.ts +29 -0
- package/dist/promql-reading-rules.test-support.d.ts.map +1 -0
- package/dist/promql-reading-rules.test-support.js +79 -0
- package/dist/promql-reading-rules.test-support.js.map +1 -0
- package/dist/promql-reading-rules.test.d.ts +2 -0
- package/dist/promql-reading-rules.test.d.ts.map +1 -0
- package/dist/promql-reading-rules.test.js +61 -0
- package/dist/promql-reading-rules.test.js.map +1 -0
- package/dist/promql-sources.test-support.d.ts +29 -0
- package/dist/promql-sources.test-support.d.ts.map +1 -0
- package/dist/promql-sources.test-support.js +112 -0
- package/dist/promql-sources.test-support.js.map +1 -0
- package/dist/promql-sources.test.d.ts +2 -0
- package/dist/promql-sources.test.d.ts.map +1 -0
- package/dist/promql-sources.test.js +93 -0
- package/dist/promql-sources.test.js.map +1 -0
- package/dist/queries/as-of.test.d.ts +2 -0
- package/dist/queries/as-of.test.d.ts.map +1 -0
- package/dist/queries/as-of.test.js +80 -0
- package/dist/queries/as-of.test.js.map +1 -0
- package/dist/queries/cluster-compare.d.ts +62 -0
- package/dist/queries/cluster-compare.d.ts.map +1 -0
- package/dist/queries/cluster-compare.js +70 -0
- package/dist/queries/cluster-compare.js.map +1 -0
- package/dist/queries/cluster-compare.test.d.ts +2 -0
- package/dist/queries/cluster-compare.test.d.ts.map +1 -0
- package/dist/queries/cluster-compare.test.js +81 -0
- package/dist/queries/cluster-compare.test.js.map +1 -0
- package/dist/queries/dashboard.d.ts +60 -0
- package/dist/queries/dashboard.d.ts.map +1 -0
- package/dist/queries/dashboard.js +292 -0
- package/dist/queries/dashboard.js.map +1 -0
- package/dist/queries/dashboard.test.d.ts +2 -0
- package/dist/queries/dashboard.test.d.ts.map +1 -0
- package/dist/queries/dashboard.test.js +423 -0
- package/dist/queries/dashboard.test.js.map +1 -0
- package/dist/queries/element.d.ts +68 -0
- package/dist/queries/element.d.ts.map +1 -0
- package/dist/queries/element.js +98 -0
- package/dist/queries/element.js.map +1 -0
- package/dist/queries/element.test.d.ts +2 -0
- package/dist/queries/element.test.d.ts.map +1 -0
- package/dist/queries/element.test.js +105 -0
- package/dist/queries/element.test.js.map +1 -0
- package/dist/queries/engine-compare.d.ts +52 -0
- package/dist/queries/engine-compare.d.ts.map +1 -0
- package/dist/queries/engine-compare.js +57 -0
- package/dist/queries/engine-compare.js.map +1 -0
- package/dist/queries/engine-compare.test.d.ts +2 -0
- package/dist/queries/engine-compare.test.d.ts.map +1 -0
- package/dist/queries/engine-compare.test.js +159 -0
- package/dist/queries/engine-compare.test.js.map +1 -0
- package/dist/queries/engine-landscape.d.ts +92 -0
- package/dist/queries/engine-landscape.d.ts.map +1 -0
- package/dist/queries/engine-landscape.js +183 -0
- package/dist/queries/engine-landscape.js.map +1 -0
- package/dist/queries/engine-landscape.test.d.ts +2 -0
- package/dist/queries/engine-landscape.test.d.ts.map +1 -0
- package/dist/queries/engine-landscape.test.js +171 -0
- package/dist/queries/engine-landscape.test.js.map +1 -0
- package/dist/queries/failures.d.ts +33 -0
- package/dist/queries/failures.d.ts.map +1 -0
- package/dist/queries/failures.js +31 -0
- package/dist/queries/failures.js.map +1 -0
- package/dist/queries/health.d.ts +77 -0
- package/dist/queries/health.d.ts.map +1 -0
- package/dist/queries/health.js +126 -0
- package/dist/queries/health.js.map +1 -0
- package/dist/queries/health.test.d.ts +2 -0
- package/dist/queries/health.test.d.ts.map +1 -0
- package/dist/queries/health.test.js +187 -0
- package/dist/queries/health.test.js.map +1 -0
- package/dist/queries/helpers.d.ts +184 -0
- package/dist/queries/helpers.d.ts.map +1 -0
- package/dist/queries/helpers.js +219 -0
- package/dist/queries/helpers.js.map +1 -0
- package/dist/queries/helpers.test.d.ts +2 -0
- package/dist/queries/helpers.test.d.ts.map +1 -0
- package/dist/queries/helpers.test.js +53 -0
- package/dist/queries/helpers.test.js.map +1 -0
- package/dist/queries/index.d.ts +19 -0
- package/dist/queries/index.d.ts.map +1 -0
- package/dist/queries/index.js +10 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/performance.d.ts +103 -0
- package/dist/queries/performance.d.ts.map +1 -0
- package/dist/queries/performance.js +173 -0
- package/dist/queries/performance.js.map +1 -0
- package/dist/queries/performance.test.d.ts +2 -0
- package/dist/queries/performance.test.d.ts.map +1 -0
- package/dist/queries/performance.test.js +265 -0
- package/dist/queries/performance.test.js.map +1 -0
- package/dist/queries/version-compare.d.ts +41 -0
- package/dist/queries/version-compare.d.ts.map +1 -0
- package/dist/queries/version-compare.js +44 -0
- package/dist/queries/version-compare.js.map +1 -0
- package/dist/queries/version-compare.test.d.ts +2 -0
- package/dist/queries/version-compare.test.d.ts.map +1 -0
- package/dist/queries/version-compare.test.js +162 -0
- package/dist/queries/version-compare.test.js.map +1 -0
- package/dist/queries/windows.d.ts +40 -0
- package/dist/queries/windows.d.ts.map +1 -0
- package/dist/queries/windows.js +56 -0
- package/dist/queries/windows.js.map +1 -0
- package/dist/query-honesty.test.d.ts +2 -0
- package/dist/query-honesty.test.d.ts.map +1 -0
- package/dist/query-honesty.test.js +294 -0
- package/dist/query-honesty.test.js.map +1 -0
- package/dist/query-scenarios.test-support.d.ts +49 -0
- package/dist/query-scenarios.test-support.d.ts.map +1 -0
- package/dist/query-scenarios.test-support.js +146 -0
- package/dist/query-scenarios.test-support.js.map +1 -0
- package/dist/schemas/cluster-compare.d.ts +11 -0
- package/dist/schemas/cluster-compare.d.ts.map +1 -0
- package/dist/schemas/cluster-compare.js +35 -0
- package/dist/schemas/cluster-compare.js.map +1 -0
- package/dist/schemas/engine-compare.d.ts +10 -0
- package/dist/schemas/engine-compare.d.ts.map +1 -0
- package/dist/schemas/engine-compare.js +27 -0
- package/dist/schemas/engine-compare.js.map +1 -0
- package/dist/schemas/engine-landscape.d.ts +5 -0
- package/dist/schemas/engine-landscape.d.ts.map +1 -0
- package/dist/schemas/engine-landscape.js +8 -0
- package/dist/schemas/engine-landscape.js.map +1 -0
- package/dist/schemas/failures.d.ts +8 -0
- package/dist/schemas/failures.d.ts.map +1 -0
- package/dist/schemas/failures.js +9 -0
- package/dist/schemas/failures.js.map +1 -0
- package/dist/schemas/health.d.ts +5 -0
- package/dist/schemas/health.d.ts.map +1 -0
- package/dist/schemas/health.js +6 -0
- package/dist/schemas/health.js.map +1 -0
- package/dist/schemas/index.d.ts +10 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +10 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/path.d.ts +15 -0
- package/dist/schemas/path.d.ts.map +1 -0
- package/dist/schemas/path.js +21 -0
- package/dist/schemas/path.js.map +1 -0
- package/dist/schemas/performance.d.ts +23 -0
- package/dist/schemas/performance.d.ts.map +1 -0
- package/dist/schemas/performance.js +21 -0
- package/dist/schemas/performance.js.map +1 -0
- package/dist/schemas/shared.d.ts +34 -0
- package/dist/schemas/shared.d.ts.map +1 -0
- package/dist/schemas/shared.js +34 -0
- package/dist/schemas/shared.js.map +1 -0
- package/dist/schemas/version-compare.d.ts +10 -0
- package/dist/schemas/version-compare.d.ts.map +1 -0
- package/dist/schemas/version-compare.js +25 -0
- package/dist/schemas/version-compare.js.map +1 -0
- package/dist/widgets.d.ts +138 -0
- package/dist/widgets.d.ts.map +1 -0
- package/dist/widgets.js +2 -0
- package/dist/widgets.js.map +1 -0
- package/metrics-contract.json +174 -0
- package/package.json +72 -5
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test support for the metrics-contract guards — never imported by runtime
|
|
3
|
+
* code. A parser for the PromQL subset the analytics consumers write (TS
|
|
4
|
+
* queries, alert rules, Grafana dashboards) into the small AST the label
|
|
5
|
+
* checker (`promql-labels.test-support.ts`) walks.
|
|
6
|
+
*
|
|
7
|
+
* Anything outside the supported subset throws instead of parsing to
|
|
8
|
+
* something approximate — extend the parser when a new query shape needs it.
|
|
9
|
+
*/
|
|
10
|
+
const LEXEMES = [
|
|
11
|
+
// A Grafana interval variable is a duration too (`offset $__range`).
|
|
12
|
+
[
|
|
13
|
+
"duration",
|
|
14
|
+
/(?:\d+(?:ms|[smhdwy]))+(?![A-Za-z0-9_])|\$__(?:rate_interval|interval|range)(?![A-Za-z0-9_])/y,
|
|
15
|
+
],
|
|
16
|
+
["number", /(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/y],
|
|
17
|
+
["ident", /[A-Za-z_:][A-Za-z0-9_:]*/y],
|
|
18
|
+
["string", /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`[^`]*`/y],
|
|
19
|
+
// Range / subquery brackets stay raw: their content (`5m`, `$__range`,
|
|
20
|
+
// `86400s`, `5m:1m`) never names a label.
|
|
21
|
+
["range", /\[[^\]]*\]/y],
|
|
22
|
+
["punct", /=~|!~|!=|==|>=|<=|[(){},=<>+\-*/%^@]/y],
|
|
23
|
+
];
|
|
24
|
+
const WHITESPACE = /\s+/y;
|
|
25
|
+
function tokenize(src) {
|
|
26
|
+
const tokens = [];
|
|
27
|
+
let pos = 0;
|
|
28
|
+
while (pos < src.length) {
|
|
29
|
+
WHITESPACE.lastIndex = pos;
|
|
30
|
+
if (WHITESPACE.test(src)) {
|
|
31
|
+
pos = WHITESPACE.lastIndex;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const token = lexemeAt(src, pos);
|
|
35
|
+
tokens.push(token);
|
|
36
|
+
pos += token.text.length;
|
|
37
|
+
}
|
|
38
|
+
tokens.push({ kind: "eof", text: "", pos });
|
|
39
|
+
return tokens;
|
|
40
|
+
}
|
|
41
|
+
function lexemeAt(src, pos) {
|
|
42
|
+
for (const [kind, pattern] of LEXEMES) {
|
|
43
|
+
pattern.lastIndex = pos;
|
|
44
|
+
const match = pattern.exec(src);
|
|
45
|
+
if (match)
|
|
46
|
+
return { kind, text: match[0], pos };
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`PromQL: unexpected "${src[pos]}" at ${pos} in: ${src}`);
|
|
49
|
+
}
|
|
50
|
+
/** String literal → value (Go-style escapes reduced to the escaped character). */
|
|
51
|
+
function unquote(text) {
|
|
52
|
+
const body = text.slice(1, -1);
|
|
53
|
+
return text.startsWith("`") ? body : body.replace(/\\(.)/g, "$1");
|
|
54
|
+
}
|
|
55
|
+
// ── Parser ─────────────────────────────────────────────────────────────────
|
|
56
|
+
const AGGREGATORS = new Set("sum min max avg group stddev stdvar count count_values bottomk topk quantile limitk limit_ratio".split(" "));
|
|
57
|
+
const BINARY_PRECEDENCE = {
|
|
58
|
+
or: 1,
|
|
59
|
+
and: 2,
|
|
60
|
+
unless: 2,
|
|
61
|
+
"==": 3,
|
|
62
|
+
"!=": 3,
|
|
63
|
+
"<=": 3,
|
|
64
|
+
"<": 3,
|
|
65
|
+
">=": 3,
|
|
66
|
+
">": 3,
|
|
67
|
+
"+": 4,
|
|
68
|
+
"-": 4,
|
|
69
|
+
"*": 5,
|
|
70
|
+
"/": 5,
|
|
71
|
+
"%": 5,
|
|
72
|
+
atan2: 5,
|
|
73
|
+
"^": 6,
|
|
74
|
+
};
|
|
75
|
+
const MATCH_OPS = new Set(["=", "!=", "=~", "!~"]);
|
|
76
|
+
class Parser {
|
|
77
|
+
tokens;
|
|
78
|
+
src;
|
|
79
|
+
i = 0;
|
|
80
|
+
constructor(tokens, src) {
|
|
81
|
+
this.tokens = tokens;
|
|
82
|
+
this.src = src;
|
|
83
|
+
}
|
|
84
|
+
parse() {
|
|
85
|
+
const node = this.parseExpr(1);
|
|
86
|
+
this.expectKind("eof");
|
|
87
|
+
return node;
|
|
88
|
+
}
|
|
89
|
+
peek() {
|
|
90
|
+
return this.tokens[this.i];
|
|
91
|
+
}
|
|
92
|
+
next() {
|
|
93
|
+
const token = this.tokens[this.i];
|
|
94
|
+
if (token.kind !== "eof")
|
|
95
|
+
this.i++;
|
|
96
|
+
return token;
|
|
97
|
+
}
|
|
98
|
+
peekIs(text) {
|
|
99
|
+
const t = this.peek();
|
|
100
|
+
return (t.kind === "ident" || t.kind === "punct") && t.text === text;
|
|
101
|
+
}
|
|
102
|
+
accept(text) {
|
|
103
|
+
if (!this.peekIs(text))
|
|
104
|
+
return false;
|
|
105
|
+
this.i++;
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
expect(text) {
|
|
109
|
+
if (!this.accept(text))
|
|
110
|
+
throw this.error(`"${text}"`);
|
|
111
|
+
}
|
|
112
|
+
expectKind(kind) {
|
|
113
|
+
if (this.peek().kind !== kind)
|
|
114
|
+
throw this.error(kind);
|
|
115
|
+
return this.next();
|
|
116
|
+
}
|
|
117
|
+
error(expected) {
|
|
118
|
+
const t = this.peek();
|
|
119
|
+
return new Error(`PromQL: expected ${expected} at ${t.pos}, got "${t.text || "<end>"}" in: ${this.src}`);
|
|
120
|
+
}
|
|
121
|
+
binaryPrecedence() {
|
|
122
|
+
const t = this.peek();
|
|
123
|
+
if (t.kind !== "punct" && t.kind !== "ident")
|
|
124
|
+
return undefined;
|
|
125
|
+
return Object.hasOwn(BINARY_PRECEDENCE, t.text) ? BINARY_PRECEDENCE[t.text] : undefined;
|
|
126
|
+
}
|
|
127
|
+
parseExpr(minPrec) {
|
|
128
|
+
let lhs = this.parseUnary();
|
|
129
|
+
for (;;) {
|
|
130
|
+
const prec = this.binaryPrecedence();
|
|
131
|
+
if (prec === undefined || prec < minPrec)
|
|
132
|
+
return lhs;
|
|
133
|
+
const op = this.next().text;
|
|
134
|
+
const matching = this.parseVectorMatching();
|
|
135
|
+
// `^` is right-associative; every other operator binds left.
|
|
136
|
+
const rhs = this.parseExpr(op === "^" ? prec : prec + 1);
|
|
137
|
+
lhs = { type: "binary", op, lhs, rhs, matching };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
parseVectorMatching() {
|
|
141
|
+
this.accept("bool");
|
|
142
|
+
let mode;
|
|
143
|
+
let labels = [];
|
|
144
|
+
if (this.peekIs("on") || this.peekIs("ignoring")) {
|
|
145
|
+
mode = this.next().text;
|
|
146
|
+
labels = this.parseLabelList();
|
|
147
|
+
}
|
|
148
|
+
let group;
|
|
149
|
+
let include = [];
|
|
150
|
+
if (this.peekIs("group_left") || this.peekIs("group_right")) {
|
|
151
|
+
group = this.next().text;
|
|
152
|
+
if (this.peekIs("("))
|
|
153
|
+
include = this.parseLabelList();
|
|
154
|
+
}
|
|
155
|
+
return mode || group ? { mode, labels, group, include } : undefined;
|
|
156
|
+
}
|
|
157
|
+
parseUnary() {
|
|
158
|
+
if (this.accept("-") || this.accept("+"))
|
|
159
|
+
return this.parseUnary();
|
|
160
|
+
return this.parsePostfix(this.parsePrimary());
|
|
161
|
+
}
|
|
162
|
+
/** Range / subquery brackets, `offset <duration>`, `@ <time>` — label-neutral. */
|
|
163
|
+
parsePostfix(node) {
|
|
164
|
+
for (;;) {
|
|
165
|
+
if (this.peek().kind === "range") {
|
|
166
|
+
this.next();
|
|
167
|
+
}
|
|
168
|
+
else if (this.accept("offset")) {
|
|
169
|
+
this.accept("-");
|
|
170
|
+
this.expectKind("duration");
|
|
171
|
+
}
|
|
172
|
+
else if (this.accept("@")) {
|
|
173
|
+
this.parseAtModifier();
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
return node;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
parseAtModifier() {
|
|
181
|
+
if (this.peek().kind === "number") {
|
|
182
|
+
this.next();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (!this.accept("start") && !this.accept("end"))
|
|
186
|
+
throw this.error("@ timestamp");
|
|
187
|
+
this.expect("(");
|
|
188
|
+
this.expect(")");
|
|
189
|
+
}
|
|
190
|
+
parsePrimary() {
|
|
191
|
+
const t = this.peek();
|
|
192
|
+
if (t.kind === "number") {
|
|
193
|
+
this.next();
|
|
194
|
+
return { type: "number" };
|
|
195
|
+
}
|
|
196
|
+
if (t.kind === "string") {
|
|
197
|
+
this.next();
|
|
198
|
+
return { type: "string", value: unquote(t.text) };
|
|
199
|
+
}
|
|
200
|
+
if (t.kind === "ident")
|
|
201
|
+
return this.parseIdentExpr(this.next().text);
|
|
202
|
+
if (this.accept("{"))
|
|
203
|
+
return this.parseSelectorBody(undefined);
|
|
204
|
+
if (!this.accept("("))
|
|
205
|
+
throw this.error("expression");
|
|
206
|
+
const inner = this.parseExpr(1);
|
|
207
|
+
this.expect(")");
|
|
208
|
+
return inner;
|
|
209
|
+
}
|
|
210
|
+
parseIdentExpr(name) {
|
|
211
|
+
const isAggregation = AGGREGATORS.has(name) && (this.peekIs("(") || this.peekIs("by") || this.peekIs("without"));
|
|
212
|
+
if (isAggregation)
|
|
213
|
+
return this.parseAggregate(name);
|
|
214
|
+
if (this.peekIs("("))
|
|
215
|
+
return { type: "call", func: name, args: this.parseArgs() };
|
|
216
|
+
if (this.accept("{"))
|
|
217
|
+
return this.parseSelectorBody(name);
|
|
218
|
+
return { type: "selector", name, matchers: [] };
|
|
219
|
+
}
|
|
220
|
+
parseAggregate(op) {
|
|
221
|
+
const before = this.parseGrouping();
|
|
222
|
+
const args = this.parseArgs();
|
|
223
|
+
const grouping = before ?? this.parseGrouping();
|
|
224
|
+
if (args.length < 1 || args.length > 2)
|
|
225
|
+
throw this.error(`1-2 arguments to ${op}`);
|
|
226
|
+
const expr = args[args.length - 1];
|
|
227
|
+
const param = args.length === 2 ? args[0] : undefined;
|
|
228
|
+
return { type: "aggregate", op, grouping, param, expr };
|
|
229
|
+
}
|
|
230
|
+
parseGrouping() {
|
|
231
|
+
if (!this.peekIs("by") && !this.peekIs("without"))
|
|
232
|
+
return undefined;
|
|
233
|
+
const mode = this.next().text;
|
|
234
|
+
return { mode, labels: this.parseLabelList() };
|
|
235
|
+
}
|
|
236
|
+
parseLabelList() {
|
|
237
|
+
this.expect("(");
|
|
238
|
+
const labels = [];
|
|
239
|
+
while (!this.accept(")")) {
|
|
240
|
+
labels.push(this.expectKind("ident").text);
|
|
241
|
+
if (!this.accept(",")) {
|
|
242
|
+
this.expect(")");
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return labels;
|
|
247
|
+
}
|
|
248
|
+
parseArgs() {
|
|
249
|
+
this.expect("(");
|
|
250
|
+
const args = [];
|
|
251
|
+
if (this.accept(")"))
|
|
252
|
+
return args;
|
|
253
|
+
do {
|
|
254
|
+
args.push(this.parseExpr(1));
|
|
255
|
+
} while (this.accept(","));
|
|
256
|
+
this.expect(")");
|
|
257
|
+
return args;
|
|
258
|
+
}
|
|
259
|
+
/** After `{`: `label op "value"` pairs up to the closing `}`. */
|
|
260
|
+
parseSelectorBody(name) {
|
|
261
|
+
const matchers = [];
|
|
262
|
+
while (!this.accept("}")) {
|
|
263
|
+
const label = this.expectKind("ident").text;
|
|
264
|
+
const op = this.peek().text;
|
|
265
|
+
if (this.peek().kind !== "punct" || !MATCH_OPS.has(op)) {
|
|
266
|
+
throw this.error("label matcher operator");
|
|
267
|
+
}
|
|
268
|
+
this.next();
|
|
269
|
+
const value = unquote(this.expectKind("string").text);
|
|
270
|
+
matchers.push({ label, op: op, value });
|
|
271
|
+
if (!this.accept(",")) {
|
|
272
|
+
this.expect("}");
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const byName = matchers.find((m) => m.label === "__name__" && m.op === "=")?.value;
|
|
277
|
+
return { type: "selector", name: name ?? byName, matchers };
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/** Parse one PromQL expression; throws on anything outside the supported subset. */
|
|
281
|
+
export function parsePromql(src) {
|
|
282
|
+
return new Parser(tokenize(src), src).parse();
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=promql-parse.test-support.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promql-parse.test-support.js","sourceRoot":"","sources":["../src/promql-parse.test-support.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test support for the metrics contract's reading rules — never imported by
|
|
3
|
+
* runtime code. The label checker (`promql-labels.test-support.ts`) asks which
|
|
4
|
+
* labels a query names; this asks HOW it reads a series:
|
|
5
|
+
*
|
|
6
|
+
* - A counter or histogram series is read only inside the born-series count
|
|
7
|
+
* `increaseIn` builds (contract rule 2): `increase()` alone never counts a
|
|
8
|
+
* series' first sample, `rate()` neither. The one exception is a plain
|
|
9
|
+
* `increase()` over a window of an hour or less at the retention edge,
|
|
10
|
+
* which the caller allows through `plainIncreaseAllowed`.
|
|
11
|
+
* - A gauge is read only node-folded (contract rule 3): directly inside
|
|
12
|
+
* `max by (<exactly its contract labels>)(…)`.
|
|
13
|
+
*
|
|
14
|
+
* Text-level on purpose: the shapes are fixed strings the helpers emit and the
|
|
15
|
+
* alert rules and dashboards copy, so any other spelling is a violation.
|
|
16
|
+
*/
|
|
17
|
+
/** The subset of a `metrics-contract.json` entry the rules need. */
|
|
18
|
+
export interface ReadingRuleMetric {
|
|
19
|
+
promName: string;
|
|
20
|
+
type: string;
|
|
21
|
+
labels: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
export interface ReadingRuleOptions {
|
|
24
|
+
/** Whether a plain `increase(<range>[ @ <at>])` is allowed; never, when absent. */
|
|
25
|
+
plainIncreaseAllowed?: (range: string, at: number | undefined) => boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Every violation of the reading rules in one PromQL string. */
|
|
28
|
+
export declare function readingRuleViolations(promql: string, metrics: readonly ReadingRuleMetric[], options?: ReadingRuleOptions): string[];
|
|
29
|
+
//# sourceMappingURL=promql-reading-rules.test-support.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promql-reading-rules.test-support.d.ts","sourceRoot":"","sources":["../src/promql-reading-rules.test-support.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,mFAAmF;IACnF,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAA;CAC1E;AAsBD,iEAAiE;AACjE,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,SAAS,iBAAiB,EAAE,EACrC,OAAO,GAAE,kBAAuB,GAC/B,MAAM,EAAE,CAmBV"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test support for the metrics contract's reading rules — never imported by
|
|
3
|
+
* runtime code. The label checker (`promql-labels.test-support.ts`) asks which
|
|
4
|
+
* labels a query names; this asks HOW it reads a series:
|
|
5
|
+
*
|
|
6
|
+
* - A counter or histogram series is read only inside the born-series count
|
|
7
|
+
* `increaseIn` builds (contract rule 2): `increase()` alone never counts a
|
|
8
|
+
* series' first sample, `rate()` neither. The one exception is a plain
|
|
9
|
+
* `increase()` over a window of an hour or less at the retention edge,
|
|
10
|
+
* which the caller allows through `plainIncreaseAllowed`.
|
|
11
|
+
* - A gauge is read only node-folded (contract rule 3): directly inside
|
|
12
|
+
* `max by (<exactly its contract labels>)(…)`.
|
|
13
|
+
*
|
|
14
|
+
* Text-level on purpose: the shapes are fixed strings the helpers emit and the
|
|
15
|
+
* alert rules and dashboards copy, so any other spelling is a violation.
|
|
16
|
+
*/
|
|
17
|
+
const SERIES = String.raw `camunda_[A-Za-z0-9_]+(?:\{[^}]*\})?`;
|
|
18
|
+
const RANGE = String.raw `[^\]\s]+`;
|
|
19
|
+
const OFFSET = String.raw `[^)\s]+`;
|
|
20
|
+
/**
|
|
21
|
+
* `((increase(S[R]@) and present_over_time(S[1h]@ offset O)) or
|
|
22
|
+
* (max_over_time(S[R]@) unless present_over_time(S[1h]@ offset O)))` — O is
|
|
23
|
+
* the window's range, or the end of its first hour at the retention edge.
|
|
24
|
+
*/
|
|
25
|
+
const BORN_READ = new RegExp(String.raw `\(\(increase\((${SERIES})\[(${RANGE})\]((?: @ \d+)?)\) and present_over_time\(\1\[1h\]\3 offset (${OFFSET})\)\)` +
|
|
26
|
+
String.raw ` or \(max_over_time\(\1\[\2\]\3\) unless present_over_time\(\1\[1h\]\3 offset \4\)\)\)`, "g");
|
|
27
|
+
const PLAIN_INCREASE = new RegExp(String.raw `increase\((${SERIES})\[(${RANGE})\](?: @ (\d+))?\)`, "g");
|
|
28
|
+
/** Every violation of the reading rules in one PromQL string. */
|
|
29
|
+
export function readingRuleViolations(promql, metrics, options = {}) {
|
|
30
|
+
const violations = [];
|
|
31
|
+
const covered = coveredSpans(promql, options);
|
|
32
|
+
for (const metric of metrics) {
|
|
33
|
+
for (const index of occurrences(promql, metric)) {
|
|
34
|
+
if (metric.type === "gauge") {
|
|
35
|
+
if (!nodeFolded(promql, index, metric.labels)) {
|
|
36
|
+
violations.push(`${metric.promName} is not read as max by (${metric.labels.join(", ")})(…) — fold a clustered engine's nodes first`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else if (!covered.some(([from, to]) => index >= from && index < to)) {
|
|
40
|
+
violations.push(`${metric.promName} is read outside the born-series count (increaseIn) — increase()/rate() miss a series' first sample`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return violations;
|
|
45
|
+
}
|
|
46
|
+
/** The spans counters may appear in: born-series reads, plus the allowed plain increases. */
|
|
47
|
+
function coveredSpans(promql, options) {
|
|
48
|
+
const spans = [];
|
|
49
|
+
for (const m of promql.matchAll(BORN_READ))
|
|
50
|
+
spans.push([m.index, m.index + m[0].length]);
|
|
51
|
+
for (const m of promql.matchAll(PLAIN_INCREASE)) {
|
|
52
|
+
const inBorn = spans.some(([from, to]) => m.index >= from && m.index < to);
|
|
53
|
+
const at = m[3] === undefined ? undefined : Number(m[3]);
|
|
54
|
+
if (!inBorn && options.plainIncreaseAllowed?.(m[2], at)) {
|
|
55
|
+
spans.push([m.index, m.index + m[0].length]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return spans;
|
|
59
|
+
}
|
|
60
|
+
/** Start offsets of the metric's series names (histograms: `_sum`/`_count`/`_bucket`). */
|
|
61
|
+
function occurrences(promql, metric) {
|
|
62
|
+
const names = metric.type === "histogram"
|
|
63
|
+
? ["_sum", "_count", "_bucket"].map((suffix) => `${metric.promName}${suffix}`)
|
|
64
|
+
: [metric.promName];
|
|
65
|
+
const pattern = new RegExp(String.raw `\b(?:${names.join("|")})(?![A-Za-z0-9_])`, "g");
|
|
66
|
+
return [...promql.matchAll(pattern)].map((m) => m.index);
|
|
67
|
+
}
|
|
68
|
+
/** Whether the gauge at `index` sits directly inside `max by (<labels>)(`. */
|
|
69
|
+
function nodeFolded(promql, index, labels) {
|
|
70
|
+
const before = /max\s+by\s*\(([^)]*)\)\s*\(\s*$/.exec(promql.slice(0, index));
|
|
71
|
+
if (!before)
|
|
72
|
+
return false;
|
|
73
|
+
const folded = before[1]
|
|
74
|
+
.split(",")
|
|
75
|
+
.map((label) => label.trim())
|
|
76
|
+
.filter((label) => label !== "");
|
|
77
|
+
return folded.length === labels.length && labels.every((label) => folded.includes(label));
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=promql-reading-rules.test-support.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promql-reading-rules.test-support.js","sourceRoot":"","sources":["../src/promql-reading-rules.test-support.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promql-reading-rules.test.d.ts","sourceRoot":"","sources":["../src/promql-reading-rules.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { readingRuleViolations, } from "./promql-reading-rules.test-support.js";
|
|
3
|
+
import { engineState, increaseIn } from "./queries/helpers.js";
|
|
4
|
+
const METRICS = [
|
|
5
|
+
{ promName: "camunda_inc_total", type: "counter", labels: ["engine_id"] },
|
|
6
|
+
{ promName: "camunda_dur_milliseconds", type: "histogram", labels: ["engine_id"] },
|
|
7
|
+
{ promName: "camunda_open", type: "gauge", labels: ["process_definition_key", "engine_id"] },
|
|
8
|
+
];
|
|
9
|
+
const violations = (promql, plainIncreaseAllowed) => readingRuleViolations(promql, METRICS, { plainIncreaseAllowed });
|
|
10
|
+
describe("readingRuleViolations — counters and histograms", () => {
|
|
11
|
+
it("accepts the born-series count, rolling and pinned, with a selector", () => {
|
|
12
|
+
const born = (series, range, at) => `((increase(${series}[${range}]${at === undefined ? "" : ` @ ${at}`}) and present_over_time(${series}[1h]${at === undefined ? "" : ` @ ${at}`} offset ${range})) or (max_over_time(${series}[${range}]${at === undefined ? "" : ` @ ${at}`}) unless present_over_time(${series}[1h]${at === undefined ? "" : ` @ ${at}`} offset ${range})))`;
|
|
13
|
+
expect(violations(`sum(${born('camunda_inc_total{engine_id="a"}', "7d")})`)).toEqual([]);
|
|
14
|
+
expect(violations(`histogram_quantile(0.95, sum by (le)(${born("camunda_dur_milliseconds_bucket", "86400s", 1700000000)}))`)).toEqual([]);
|
|
15
|
+
expect(violations(`sum(${born("camunda_inc_total", "$__range")})`)).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
it("rejects a plain increase(), rate() or a raw counter", () => {
|
|
18
|
+
expect(violations("sum(increase(camunda_inc_total[7d]))")).toHaveLength(1);
|
|
19
|
+
expect(violations("sum(rate(camunda_inc_total[5m])) * 60")).toHaveLength(1);
|
|
20
|
+
expect(violations("sum(camunda_dur_milliseconds_sum)")).toHaveLength(1);
|
|
21
|
+
});
|
|
22
|
+
it("rejects a born-series count whose parts disagree", () => {
|
|
23
|
+
// The two silence checks must look at the same hour …
|
|
24
|
+
const offsets = "((increase(camunda_inc_total[7d]) and present_over_time(camunda_inc_total[1h] offset 7d)) or (max_over_time(camunda_inc_total[7d]) unless present_over_time(camunda_inc_total[1h] offset 1d)))";
|
|
25
|
+
expect(violations(offsets).length).toBeGreaterThan(0);
|
|
26
|
+
// … and both reads span the same window.
|
|
27
|
+
const ranges = "((increase(camunda_inc_total[7d]) and present_over_time(camunda_inc_total[1h] offset 7d)) or (max_over_time(camunda_inc_total[1d]) unless present_over_time(camunda_inc_total[1h] offset 7d)))";
|
|
28
|
+
expect(violations(ranges).length).toBeGreaterThan(0);
|
|
29
|
+
});
|
|
30
|
+
it("accepts the window's own first hour as the silence check at the retention edge", () => {
|
|
31
|
+
const edge = "((increase(camunda_inc_total[30d]) and present_over_time(camunda_inc_total[1h] offset 2588400s)) or (max_over_time(camunda_inc_total[30d]) unless present_over_time(camunda_inc_total[1h] offset 2588400s)))";
|
|
32
|
+
expect(violations(`sum(${edge})`)).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
it("lets a plain increase() through only where the caller allows its window", () => {
|
|
35
|
+
const allowed = (range) => range === "30d";
|
|
36
|
+
expect(violations("sum(increase(camunda_inc_total[30d]))", allowed)).toEqual([]);
|
|
37
|
+
expect(violations("sum(increase(camunda_inc_total[7d]))", allowed)).toHaveLength(1);
|
|
38
|
+
});
|
|
39
|
+
it("accepts exactly what increaseIn emits", () => {
|
|
40
|
+
expect(violations(`sum(${increaseIn('camunda_inc_total{engine_id="a"}', { range: "7d" })})`)).toEqual([]);
|
|
41
|
+
expect(violations(`sum(${increaseIn("camunda_dur_milliseconds_sum", { range: "3600s", at: Math.floor(Date.now() / 1000) })})`)).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe("readingRuleViolations — gauges", () => {
|
|
45
|
+
it("accepts a gauge folded over exactly its contract labels, in any order", () => {
|
|
46
|
+
expect(violations('sum(max by (engine_id, process_definition_key)(camunda_open{engine_id="a"}))')).toEqual([]);
|
|
47
|
+
expect(violations("max by (process_definition_key, engine_id) (camunda_open)")).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
it("rejects a summed, partly folded or over-folded gauge", () => {
|
|
50
|
+
expect(violations("sum(camunda_open)")).toHaveLength(1);
|
|
51
|
+
expect(violations("sum by (engine_id)(max by (engine_id)(camunda_open))")).toHaveLength(1);
|
|
52
|
+
expect(violations("max by (engine_id, process_definition_key, instance)(camunda_open)")).toHaveLength(1);
|
|
53
|
+
expect(violations("max(camunda_open)")).toHaveLength(1);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe("engineState", () => {
|
|
57
|
+
it("folds a gauge over its contract labels", () => {
|
|
58
|
+
expect(engineState("camunda_incidents_open", '{engine_id="a"}')).toBe('max by (process_definition_key, incident_type, engine_id)(camunda_incidents_open{engine_id="a"})');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=promql-reading-rules.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promql-reading-rules.test.js","sourceRoot":"","sources":["../src/promql-reading-rules.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test support for the metrics-contract guards — never imported by runtime
|
|
3
|
+
* code. Extracts the PromQL the non-TypeScript consumers send: the alert
|
|
4
|
+
* rules' `expr:` values (alerts.yml) and the Grafana dashboards' panel `expr`s
|
|
5
|
+
* and templating queries, for the label checker
|
|
6
|
+
* (`promql-labels.test-support.ts`).
|
|
7
|
+
*
|
|
8
|
+
* Both extractors fail closed. A source shape they do not understand throws
|
|
9
|
+
* instead of being skipped or passed through raw: a skipped expression is one
|
|
10
|
+
* the label guard never checks, and a still-quoted one parses as a single
|
|
11
|
+
* PromQL string literal that checks zero labels — either way the guard would
|
|
12
|
+
* pass vacuously.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The `expr:` values of the alert rules, as the YAML scalars they denote:
|
|
16
|
+
* plain, single- or double-quoted, or a `|`/`>` block scalar, each possibly
|
|
17
|
+
* running over continuation lines (joined with a space — PromQL does not care
|
|
18
|
+
* about line breaks).
|
|
19
|
+
*/
|
|
20
|
+
export declare function alertExpressions(yaml: string): string[];
|
|
21
|
+
/**
|
|
22
|
+
* Every panel `expr` of a Grafana dashboard, plus its templating variable
|
|
23
|
+
* queries: `label_values(series, label)` becomes the equivalent
|
|
24
|
+
* `count by (label)(series)`, so the label is checked like any grouping, and
|
|
25
|
+
* `query_result(expr)` contributes its expression. Other `query` strings
|
|
26
|
+
* (custom variable lists, `label_names()`, `metrics()`) name no label.
|
|
27
|
+
*/
|
|
28
|
+
export declare function dashboardQueries(node: unknown): string[];
|
|
29
|
+
//# sourceMappingURL=promql-sources.test-support.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promql-sources.test-support.d.ts","sourceRoot":"","sources":["../src/promql-sources.test-support.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAyBvD;AAwBD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,CAUxD"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test support for the metrics-contract guards — never imported by runtime
|
|
3
|
+
* code. Extracts the PromQL the non-TypeScript consumers send: the alert
|
|
4
|
+
* rules' `expr:` values (alerts.yml) and the Grafana dashboards' panel `expr`s
|
|
5
|
+
* and templating queries, for the label checker
|
|
6
|
+
* (`promql-labels.test-support.ts`).
|
|
7
|
+
*
|
|
8
|
+
* Both extractors fail closed. A source shape they do not understand throws
|
|
9
|
+
* instead of being skipped or passed through raw: a skipped expression is one
|
|
10
|
+
* the label guard never checks, and a still-quoted one parses as a single
|
|
11
|
+
* PromQL string literal that checks zero labels — either way the guard would
|
|
12
|
+
* pass vacuously.
|
|
13
|
+
*/
|
|
14
|
+
/** `|`/`>` block-scalar headers, with optional chomping and indentation indicators. */
|
|
15
|
+
const BLOCK_SCALAR = /^[|>](?:[1-9]?[-+]?|[-+][1-9])$/;
|
|
16
|
+
/**
|
|
17
|
+
* The `expr:` values of the alert rules, as the YAML scalars they denote:
|
|
18
|
+
* plain, single- or double-quoted, or a `|`/`>` block scalar, each possibly
|
|
19
|
+
* running over continuation lines (joined with a space — PromQL does not care
|
|
20
|
+
* about line breaks).
|
|
21
|
+
*/
|
|
22
|
+
export function alertExpressions(yaml) {
|
|
23
|
+
const lines = yaml.split("\n");
|
|
24
|
+
const out = [];
|
|
25
|
+
for (let i = 0; i < lines.length; i++) {
|
|
26
|
+
// The key may open a list item (`- expr: …`); the dash counts as indentation.
|
|
27
|
+
const match = /^(\s*(?:-\s+)?)expr:(.*)$/.exec(lines[i]);
|
|
28
|
+
if (!match)
|
|
29
|
+
continue;
|
|
30
|
+
const [, keyIndent, rest] = match;
|
|
31
|
+
const line = i + 1;
|
|
32
|
+
// The value runs until the first non-blank line indented no deeper than the key.
|
|
33
|
+
const continuation = [];
|
|
34
|
+
for (; i + 1 < lines.length; i++) {
|
|
35
|
+
const next = lines[i + 1];
|
|
36
|
+
if (next.trim() === "")
|
|
37
|
+
continue;
|
|
38
|
+
if (next.length - next.trimStart().length <= keyIndent.length)
|
|
39
|
+
break;
|
|
40
|
+
continuation.push(next.trim());
|
|
41
|
+
}
|
|
42
|
+
const inline = rest.trim();
|
|
43
|
+
const value = BLOCK_SCALAR.test(inline)
|
|
44
|
+
? continuation.join(" ")
|
|
45
|
+
: yamlFlowScalar([inline, ...continuation].join(" ").trim());
|
|
46
|
+
if (value === "")
|
|
47
|
+
throw new Error(`alerts.yml: empty expr at line ${line}`);
|
|
48
|
+
out.push(value);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The string a plain or quoted YAML flow scalar denotes. Single quotes escape
|
|
54
|
+
* only `''`; double quotes use backslash escapes, parsed as their JSON subset —
|
|
55
|
+
* anything beyond it (or a trailing comment after the closing quote) throws.
|
|
56
|
+
*/
|
|
57
|
+
function yamlFlowScalar(text) {
|
|
58
|
+
if (text.startsWith("'")) {
|
|
59
|
+
if (!/^'(?:[^']|'')*'$/.test(text)) {
|
|
60
|
+
throw new Error(`alerts.yml: unsupported single-quoted expr: ${text}`);
|
|
61
|
+
}
|
|
62
|
+
return text.slice(1, -1).replaceAll("''", "'");
|
|
63
|
+
}
|
|
64
|
+
if (text.startsWith('"')) {
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(text);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
throw new Error(`alerts.yml: unsupported double-quoted expr: ${text}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return text;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Every panel `expr` of a Grafana dashboard, plus its templating variable
|
|
76
|
+
* queries: `label_values(series, label)` becomes the equivalent
|
|
77
|
+
* `count by (label)(series)`, so the label is checked like any grouping, and
|
|
78
|
+
* `query_result(expr)` contributes its expression. Other `query` strings
|
|
79
|
+
* (custom variable lists, `label_names()`, `metrics()`) name no label.
|
|
80
|
+
*/
|
|
81
|
+
export function dashboardQueries(node) {
|
|
82
|
+
if (Array.isArray(node))
|
|
83
|
+
return node.flatMap(dashboardQueries);
|
|
84
|
+
if (node === null || typeof node !== "object")
|
|
85
|
+
return [];
|
|
86
|
+
const out = [];
|
|
87
|
+
for (const [key, value] of Object.entries(node)) {
|
|
88
|
+
if (typeof value !== "string")
|
|
89
|
+
out.push(...dashboardQueries(value));
|
|
90
|
+
else if (key === "expr")
|
|
91
|
+
out.push(value);
|
|
92
|
+
else if (key === "query")
|
|
93
|
+
out.push(...templatingQuery(value));
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
/** The PromQL behind one templating `query`; throws on a label-naming call it cannot read. */
|
|
98
|
+
function templatingQuery(query) {
|
|
99
|
+
const fn = /^\s*(label_values|query_result)\s*\(/.exec(query)?.[1];
|
|
100
|
+
if (!fn)
|
|
101
|
+
return [];
|
|
102
|
+
const args = new RegExp(`^\\s*${fn}\\s*\\(([\\s\\S]*)\\)\\s*$`).exec(query)?.[1].trim();
|
|
103
|
+
if (fn === "query_result" && args)
|
|
104
|
+
return [args];
|
|
105
|
+
// label_values(<series selector>, <label>): the label is the LAST argument —
|
|
106
|
+
// the selector itself may contain commas between its matchers.
|
|
107
|
+
const labelValues = args && /^([\s\S]+),\s*([A-Za-z_][A-Za-z0-9_]*)$/.exec(args);
|
|
108
|
+
if (labelValues)
|
|
109
|
+
return [`count by (${labelValues[2]})(${labelValues[1].trim()})`];
|
|
110
|
+
throw new Error(`unsupported Grafana templating query (extend dashboardQueries for it): ${query}`);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=promql-sources.test-support.js.map
|