@parca/profile 0.16.0 → 0.16.22
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/CHANGELOG.md +40 -0
- package/dist/Callgraph/Edge/index.d.ts +22 -0
- package/dist/Callgraph/Edge/index.js +30 -0
- package/dist/Callgraph/Node/index.d.ts +19 -0
- package/dist/Callgraph/Node/index.js +37 -0
- package/dist/Callgraph/index.d.ts +8 -0
- package/dist/Callgraph/index.js +137 -0
- package/dist/Callgraph/mockData/index.d.ts +148 -0
- package/dist/Callgraph/mockData/index.js +577 -0
- package/dist/Callgraph/utils.d.ts +19 -0
- package/dist/Callgraph/utils.js +82 -0
- package/dist/GraphTooltip/index.d.ts +19 -0
- package/dist/GraphTooltip/index.js +119 -0
- package/dist/IcicleGraph.d.ts +35 -0
- package/dist/IcicleGraph.js +139 -0
- package/dist/MatchersInput/index.d.ts +23 -0
- package/dist/MatchersInput/index.js +479 -0
- package/dist/MetricsCircle/index.d.ts +7 -0
- package/dist/MetricsCircle/index.js +18 -0
- package/dist/MetricsGraph/index.d.ts +35 -0
- package/dist/MetricsGraph/index.js +349 -0
- package/dist/MetricsSeries/index.d.ts +11 -0
- package/dist/MetricsSeries/index.js +21 -0
- package/dist/ProfileExplorer/ProfileExplorerCompare.d.ts +19 -0
- package/dist/ProfileExplorer/ProfileExplorerCompare.js +38 -0
- package/dist/ProfileExplorer/ProfileExplorerSingle.d.ts +15 -0
- package/dist/ProfileExplorer/ProfileExplorerSingle.js +19 -0
- package/dist/ProfileExplorer/index.d.ts +9 -0
- package/dist/ProfileExplorer/index.js +203 -0
- package/dist/ProfileIcicleGraph.d.ts +10 -0
- package/dist/ProfileIcicleGraph.js +28 -0
- package/dist/ProfileMetricsGraph/index.d.ts +22 -0
- package/dist/ProfileMetricsGraph/index.js +127 -0
- package/dist/ProfileSVG.module.css +3 -0
- package/dist/ProfileSelector/CompareButton.d.ts +5 -0
- package/dist/ProfileSelector/CompareButton.js +41 -0
- package/dist/ProfileSelector/MergeButton.d.ts +5 -0
- package/dist/ProfileSelector/MergeButton.js +41 -0
- package/dist/ProfileSelector/index.d.ts +29 -0
- package/dist/ProfileSelector/index.js +133 -0
- package/dist/ProfileSource.d.ts +88 -0
- package/dist/ProfileSource.js +239 -0
- package/dist/ProfileTypeSelector/index.d.ts +20 -0
- package/dist/ProfileTypeSelector/index.js +138 -0
- package/dist/ProfileView.d.ts +39 -0
- package/dist/ProfileView.js +111 -0
- package/dist/ProfileView.styles.css +3 -0
- package/dist/ProfileViewWithData.d.ts +11 -0
- package/dist/ProfileViewWithData.js +116 -0
- package/dist/TopTable.d.ts +9 -0
- package/dist/TopTable.js +140 -0
- package/dist/TopTable.styles.css +7 -0
- package/dist/components/DiffLegend.d.ts +2 -0
- package/dist/components/DiffLegend.js +62 -0
- package/dist/components/ProfileShareButton/ResultBox.d.ts +6 -0
- package/dist/components/ProfileShareButton/ResultBox.js +46 -0
- package/dist/components/ProfileShareButton/index.d.ts +7 -0
- package/dist/components/ProfileShareButton/index.js +119 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +64 -0
- package/dist/styles.css +1 -0
- package/dist/useDelayedLoader.d.ts +5 -0
- package/dist/useDelayedLoader.js +33 -0
- package/dist/useQuery.d.ts +13 -0
- package/dist/useQuery.js +41 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +83 -0
- package/package.json +12 -8
- package/src/Callgraph/Edge/index.tsx +59 -0
- package/src/Callgraph/Node/index.tsx +66 -0
- package/src/Callgraph/index.tsx +169 -0
- package/src/Callgraph/mockData/index.ts +605 -0
- package/src/Callgraph/utils.ts +116 -0
- package/src/GraphTooltip/index.tsx +245 -0
- package/src/IcicleGraph.tsx +3 -3
- package/src/MatchersInput/index.tsx +698 -0
- package/src/MetricsCircle/index.tsx +28 -0
- package/src/MetricsGraph/index.tsx +614 -0
- package/src/MetricsSeries/index.tsx +38 -0
- package/src/ProfileExplorer/ProfileExplorerCompare.tsx +109 -0
- package/src/ProfileExplorer/ProfileExplorerSingle.tsx +72 -0
- package/src/ProfileExplorer/index.tsx +377 -0
- package/src/ProfileMetricsGraph/index.tsx +143 -0
- package/src/ProfileSelector/CompareButton.tsx +72 -0
- package/src/ProfileSelector/MergeButton.tsx +72 -0
- package/src/ProfileSelector/index.tsx +270 -0
- package/src/ProfileTypeSelector/index.tsx +180 -0
- package/src/ProfileView.tsx +2 -7
- package/src/index.tsx +11 -0
- package/src/useQuery.tsx +1 -0
- package/tailwind.config.js +8 -0
- package/tsconfig.json +7 -3
- package/typings.d.ts +14 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
13
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
14
|
+
if (ar || !(i in from)) {
|
|
15
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
16
|
+
ar[i] = from[i];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
20
|
+
};
|
|
21
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
22
|
+
// Copyright 2022 The Parca Authors
|
|
23
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
24
|
+
// you may not use this file except in compliance with the License.
|
|
25
|
+
// You may obtain a copy of the License at
|
|
26
|
+
//
|
|
27
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
28
|
+
//
|
|
29
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
30
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
31
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
32
|
+
// See the License for the specific language governing permissions and
|
|
33
|
+
// limitations under the License.
|
|
34
|
+
import { Fragment, useState, useEffect } from 'react';
|
|
35
|
+
import { Transition } from '@headlessui/react';
|
|
36
|
+
import { Query } from '@parca/parser';
|
|
37
|
+
import { usePopper } from 'react-popper';
|
|
38
|
+
import cx from 'classnames';
|
|
39
|
+
import { useParcaTheme, useGrpcMetadata } from '@parca/components';
|
|
40
|
+
var Labels;
|
|
41
|
+
(function (Labels) {
|
|
42
|
+
Labels["labelName"] = "labelName";
|
|
43
|
+
Labels["labelValue"] = "labelValue";
|
|
44
|
+
Labels["literal"] = "literal";
|
|
45
|
+
})(Labels || (Labels = {}));
|
|
46
|
+
// eslint-disable-next-line no-useless-escape
|
|
47
|
+
var labelNameValueRe = /(^([a-z])\w+)(=~|=|!=|!~)(\")[a-zA-Z0-9_.-:]+(\")$/g; // labelNameValueRe matches the following: labelName=~"labelValue"
|
|
48
|
+
var labelNameValueWithoutQuotesRe = /(^([a-z])\w+)(=~|=|!=|!~)[a-zA-Z0-9_.-:]+$/g; // labelNameValueWithoutQuotesRe matches the following: labelName=~labelValue
|
|
49
|
+
var labelNameLiteralRe = /(^([a-z])\w+)(=~|=|!=|!~)/; // labelNameLiteralRe matches the following: labelName=~, labelName!=~, labelName=, labelName!=
|
|
50
|
+
var literalRe = /(=~|=|!=|!~)/; // literalRe matches the following: =~, =, !=, !~
|
|
51
|
+
var addQuoteMarks = function (labelValue) {
|
|
52
|
+
// eslint-disable-next-line no-useless-escape
|
|
53
|
+
return "\"".concat(labelValue, "\"");
|
|
54
|
+
};
|
|
55
|
+
export var useLabelNames = function (client) {
|
|
56
|
+
var _a = useState(true), loading = _a[0], setLoading = _a[1];
|
|
57
|
+
var _b = useState({}), result = _b[0], setResult = _b[1];
|
|
58
|
+
var metadata = useGrpcMetadata();
|
|
59
|
+
useEffect(function () {
|
|
60
|
+
var call = client.labels({ match: [] }, { meta: metadata });
|
|
61
|
+
setLoading(true);
|
|
62
|
+
call.response
|
|
63
|
+
.then(function (response) { return setResult({ response: response }); })
|
|
64
|
+
.catch(function (error) { return setResult({ error: error }); })
|
|
65
|
+
.finally(function () { return setLoading(false); });
|
|
66
|
+
}, [client, metadata]);
|
|
67
|
+
return { result: result, loading: loading };
|
|
68
|
+
};
|
|
69
|
+
var Suggestion = /** @class */ (function () {
|
|
70
|
+
function Suggestion(type, typeahead, value) {
|
|
71
|
+
this.type = type;
|
|
72
|
+
this.typeahead = typeahead;
|
|
73
|
+
this.value = value;
|
|
74
|
+
}
|
|
75
|
+
return Suggestion;
|
|
76
|
+
}());
|
|
77
|
+
var Suggestions = /** @class */ (function () {
|
|
78
|
+
function Suggestions() {
|
|
79
|
+
this.literals = [];
|
|
80
|
+
this.labelNames = [];
|
|
81
|
+
this.labelValues = [];
|
|
82
|
+
}
|
|
83
|
+
return Suggestions;
|
|
84
|
+
}());
|
|
85
|
+
var MatchersInput = function (_a) {
|
|
86
|
+
var queryClient = _a.queryClient, setMatchersString = _a.setMatchersString, runQuery = _a.runQuery, currentQuery = _a.currentQuery;
|
|
87
|
+
var _b = useState(null), divInputRef = _b[0], setDivInputRef = _b[1];
|
|
88
|
+
var _c = useState(null), popperElement = _c[0], setPopperElement = _c[1];
|
|
89
|
+
var _d = useState(null), localMatchers = _d[0], setLocalMatchers = _d[1];
|
|
90
|
+
var _e = useState(false), focusedInput = _e[0], setFocusedInput = _e[1];
|
|
91
|
+
var _f = useState(true), showSuggest = _f[0], setShowSuggest = _f[1];
|
|
92
|
+
var _g = useState(false), labelValuesLoading = _g[0], setLabelValuesLoading = _g[1];
|
|
93
|
+
var _h = useState(-1), highlightedSuggestionIndex = _h[0], setHighlightedSuggestionIndex = _h[1];
|
|
94
|
+
var _j = useState(new Suggestion('', '', '')), lastCompleted = _j[0], setLastCompleted = _j[1];
|
|
95
|
+
var suggestionSections = useState(new Suggestions())[0];
|
|
96
|
+
var _k = useState(''), inputRef = _k[0], setInputRef = _k[1];
|
|
97
|
+
var _l = useState(null), labelValuesResponse = _l[0], setLabelValuesResponse = _l[1];
|
|
98
|
+
var _m = useState(null), currentLabelsCollection = _m[0], setCurrentLabelsCollection = _m[1]; // This is an array that contains query expressions that have been matched i.e. they have been completed and have the blue badge around them in the UI.
|
|
99
|
+
var _o = usePopper(divInputRef, popperElement, {
|
|
100
|
+
placement: 'bottom-start',
|
|
101
|
+
}), styles = _o.styles, attributes = _o.attributes;
|
|
102
|
+
var metadata = useGrpcMetadata();
|
|
103
|
+
var Spinner = useParcaTheme().loader;
|
|
104
|
+
var _p = useLabelNames(queryClient), labelNamesLoading = _p.loading, result = _p.result;
|
|
105
|
+
var labelNamesResponse = result.response, labelNamesError = result.error;
|
|
106
|
+
var LoadingSpinner = function () {
|
|
107
|
+
return _jsx("div", __assign({ className: "pt-2 pb-4" }, { children: Spinner }));
|
|
108
|
+
};
|
|
109
|
+
var getLabelNameValues = function (labelName) {
|
|
110
|
+
var call = queryClient.values({ labelName: labelName, match: [] }, { meta: metadata });
|
|
111
|
+
setLabelValuesLoading(true);
|
|
112
|
+
call.response
|
|
113
|
+
.then(function (response) {
|
|
114
|
+
setLabelValuesResponse(response.labelValues);
|
|
115
|
+
})
|
|
116
|
+
.catch(function () { return setLabelValuesResponse(null); })
|
|
117
|
+
.finally(function () { return setLabelValuesLoading(false); });
|
|
118
|
+
};
|
|
119
|
+
var labelNames = (labelNamesError === undefined || labelNamesError == null) &&
|
|
120
|
+
labelNamesResponse !== undefined &&
|
|
121
|
+
labelNamesResponse != null
|
|
122
|
+
? labelNamesResponse.labelNames.filter(function (e) { return e !== '__name__'; })
|
|
123
|
+
: [];
|
|
124
|
+
var labelValues = labelValuesResponse !== undefined && labelValuesResponse != null ? labelValuesResponse : [];
|
|
125
|
+
var value = currentQuery.matchersString();
|
|
126
|
+
Query.suggest("{".concat(value)).forEach(function (s) {
|
|
127
|
+
// Skip suggestions that we just completed. This really only works,
|
|
128
|
+
// because we know the language is not repetitive. For a language that
|
|
129
|
+
// has a repeating word, this would not work.
|
|
130
|
+
if (lastCompleted !== null && lastCompleted.type === s.type) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// Need to figure out if any literal suggestions make sense, but a
|
|
134
|
+
// closing bracket doesn't in the guided query experience because all
|
|
135
|
+
// we have the user do is type the matchers.
|
|
136
|
+
if (s.type === Labels.literal && s.value !== '}') {
|
|
137
|
+
if (suggestionSections.literals.find(function (e) { return e.value === s.value; }) != null) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
suggestionSections.literals.push({
|
|
141
|
+
type: s.type,
|
|
142
|
+
typeahead: '',
|
|
143
|
+
value: s.value,
|
|
144
|
+
});
|
|
145
|
+
suggestionSections.labelNames = [];
|
|
146
|
+
suggestionSections.labelValues = [];
|
|
147
|
+
}
|
|
148
|
+
if (s.type === Labels.labelName) {
|
|
149
|
+
var inputValue_1 = s.typeahead.trim().toLowerCase();
|
|
150
|
+
var inputLength_1 = inputValue_1.length;
|
|
151
|
+
var matches = labelNames.filter(function (label) {
|
|
152
|
+
return label.toLowerCase().slice(0, inputLength_1) === inputValue_1;
|
|
153
|
+
});
|
|
154
|
+
matches.forEach(function (m) {
|
|
155
|
+
if (suggestionSections.labelNames.find(function (e) { return e.value === m; }) != null) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
suggestionSections.labelNames.push({
|
|
159
|
+
type: s.type,
|
|
160
|
+
typeahead: s.typeahead,
|
|
161
|
+
value: m,
|
|
162
|
+
});
|
|
163
|
+
suggestionSections.literals = [];
|
|
164
|
+
suggestionSections.labelValues = [];
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (s.type === Labels.labelValue) {
|
|
168
|
+
var inputValue_2 = s.typeahead.trim().toLowerCase();
|
|
169
|
+
var inputLength_2 = inputValue_2.length;
|
|
170
|
+
var matches = labelValues.filter(function (label) {
|
|
171
|
+
return label.toLowerCase().slice(0, inputLength_2) === inputValue_2;
|
|
172
|
+
});
|
|
173
|
+
matches.forEach(function (m) {
|
|
174
|
+
if (suggestionSections.labelValues.find(function (e) { return e.value === m; }) != null) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
suggestionSections.labelValues.push({
|
|
178
|
+
type: s.type,
|
|
179
|
+
typeahead: s.typeahead,
|
|
180
|
+
value: m,
|
|
181
|
+
});
|
|
182
|
+
suggestionSections.labelNames = [];
|
|
183
|
+
suggestionSections.literals = [];
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
var suggestionsLength = suggestionSections.literals.length +
|
|
188
|
+
suggestionSections.labelNames.length +
|
|
189
|
+
suggestionSections.labelValues.length;
|
|
190
|
+
var getLabelsFromMatchers = function (matchers) {
|
|
191
|
+
return matchers
|
|
192
|
+
.filter(function (matcher) { return matcher.key !== '__name__'; })
|
|
193
|
+
.map(function (matcher) { return "".concat(matcher.key).concat(matcher.matcherType).concat(addQuoteMarks(matcher.value)); });
|
|
194
|
+
};
|
|
195
|
+
useEffect(function () {
|
|
196
|
+
var matchers = currentQuery.matchers.filter(function (matcher) { return matcher.key !== '__name__'; });
|
|
197
|
+
if (matchers.length > 0) {
|
|
198
|
+
setCurrentLabelsCollection(getLabelsFromMatchers(matchers));
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
if (localMatchers !== null)
|
|
202
|
+
setCurrentLabelsCollection(getLabelsFromMatchers(localMatchers));
|
|
203
|
+
}
|
|
204
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
205
|
+
}, [currentQuery.matchers]);
|
|
206
|
+
var resetHighlight = function () { return setHighlightedSuggestionIndex(-1); };
|
|
207
|
+
var resetLastCompleted = function () { return setLastCompleted(new Suggestion('', '', '')); };
|
|
208
|
+
var onChange = function (e) {
|
|
209
|
+
var newValue = e.target.value;
|
|
210
|
+
// filter out the labelname list and move to the top the labelname that is most similar to what the user is typing.
|
|
211
|
+
if (suggestionSections.labelNames.length > 0) {
|
|
212
|
+
suggestionSections.labelNames = suggestionSections.labelNames.filter(function (suggestion) {
|
|
213
|
+
return suggestion.value.toLowerCase().includes(newValue.toLowerCase());
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
// this checks if the user has typed a label name and a literal (=/!=,=~,!~) i.e labelName=~, labelName!=~, labelName=, labelName!=
|
|
217
|
+
// and is about to type the label value, then it it will filter out the labelvalue list and move to the top
|
|
218
|
+
// the labelvalue that is most similar to what the user is typing.
|
|
219
|
+
if (suggestionSections.labelValues.length > 0 && labelNameLiteralRe.test(newValue)) {
|
|
220
|
+
var labelValueSearch_1 = newValue.split(literalRe)[2];
|
|
221
|
+
suggestionSections.labelValues = suggestionSections.labelValues.filter(function (suggestion) {
|
|
222
|
+
return suggestion.value.toLowerCase().includes(labelValueSearch_1.toLowerCase());
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
setInputRef(newValue);
|
|
226
|
+
resetLastCompleted();
|
|
227
|
+
resetHighlight();
|
|
228
|
+
};
|
|
229
|
+
var complete = function (suggestion) {
|
|
230
|
+
return value.slice(0, value.length - suggestion.typeahead.length) + suggestion.value;
|
|
231
|
+
};
|
|
232
|
+
var getSuggestion = function (index) {
|
|
233
|
+
if (suggestionSections.labelValues.length > 0) {
|
|
234
|
+
if (index < suggestionSections.labelValues.length) {
|
|
235
|
+
return suggestionSections.labelValues[index];
|
|
236
|
+
}
|
|
237
|
+
return suggestionSections.literals[index - suggestionSections.labelValues.length];
|
|
238
|
+
}
|
|
239
|
+
if (index < suggestionSections.labelNames.length) {
|
|
240
|
+
return suggestionSections.labelNames[index];
|
|
241
|
+
}
|
|
242
|
+
return suggestionSections.literals[index - suggestionSections.labelNames.length];
|
|
243
|
+
};
|
|
244
|
+
var highlightNext = function () {
|
|
245
|
+
var nextIndex = highlightedSuggestionIndex + 1;
|
|
246
|
+
if (nextIndex === suggestionsLength) {
|
|
247
|
+
resetHighlight();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
setHighlightedSuggestionIndex(nextIndex);
|
|
251
|
+
};
|
|
252
|
+
var highlightPrevious = function () {
|
|
253
|
+
if (highlightedSuggestionIndex === -1) {
|
|
254
|
+
// Didn't select anything, so starting at the bottom.
|
|
255
|
+
setHighlightedSuggestionIndex(suggestionsLength - 1);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
setHighlightedSuggestionIndex(highlightedSuggestionIndex - 1);
|
|
259
|
+
};
|
|
260
|
+
var applySuggestion = function (suggestionIndex) {
|
|
261
|
+
var suggestion = getSuggestion(suggestionIndex);
|
|
262
|
+
if (suggestion.type === Labels.labelValue) {
|
|
263
|
+
suggestion.value = addQuoteMarks(suggestion.value);
|
|
264
|
+
}
|
|
265
|
+
var newValue = complete(suggestion);
|
|
266
|
+
resetHighlight();
|
|
267
|
+
if (suggestion.type === Labels.labelName) {
|
|
268
|
+
getLabelNameValues(suggestion.value);
|
|
269
|
+
}
|
|
270
|
+
setLastCompleted(suggestion);
|
|
271
|
+
setMatchersString(newValue);
|
|
272
|
+
if (suggestion.type === Labels.labelValue) {
|
|
273
|
+
var values_1 = newValue.split(',');
|
|
274
|
+
if (currentLabelsCollection == null || (currentLabelsCollection === null || currentLabelsCollection === void 0 ? void 0 : currentLabelsCollection.length) === 0) {
|
|
275
|
+
setCurrentLabelsCollection(values_1);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
setCurrentLabelsCollection(function (oldValues) { return __spreadArray(__spreadArray([], (oldValues !== null && oldValues !== void 0 ? oldValues : []), true), [
|
|
279
|
+
values_1[values_1.length - 1],
|
|
280
|
+
], false); });
|
|
281
|
+
}
|
|
282
|
+
setInputRef('');
|
|
283
|
+
focus();
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (lastCompleted.type === Labels.labelValue && suggestion.type === Labels.literal) {
|
|
287
|
+
setInputRef('');
|
|
288
|
+
focus();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (currentLabelsCollection !== null) {
|
|
292
|
+
setInputRef(newValue.substring(newValue.lastIndexOf(',') + 1));
|
|
293
|
+
focus();
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
setInputRef(newValue);
|
|
297
|
+
focus();
|
|
298
|
+
};
|
|
299
|
+
var applyHighlightedSuggestion = function () {
|
|
300
|
+
applySuggestion(highlightedSuggestionIndex);
|
|
301
|
+
};
|
|
302
|
+
// This function adds quotes to the query expression if the user has typed it in manually, i.e. did not use the arrow up / down keys + Enter
|
|
303
|
+
// to choose the label name and value. Therefore, labelName=value becomes labelName="value".
|
|
304
|
+
var addQuotesToInputRefLabelValue = function (inputRef) {
|
|
305
|
+
var labelValue = inputRef.split(literalRe)[2].replaceAll(',', '');
|
|
306
|
+
var labelValueWithQuotes = addQuoteMarks(labelValue);
|
|
307
|
+
return inputRef.replace(labelValue, labelValueWithQuotes);
|
|
308
|
+
};
|
|
309
|
+
var handleKeyUp = function (event) {
|
|
310
|
+
var values = inputRef.replaceAll(',', '');
|
|
311
|
+
if (labelNameValueRe.test(inputRef)) {
|
|
312
|
+
if (currentLabelsCollection === null) {
|
|
313
|
+
setMatchersString(inputRef);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
setMatchersString((currentLabelsCollection === null || currentLabelsCollection === void 0 ? void 0 : currentLabelsCollection.join(',')) + ',' + values);
|
|
317
|
+
}
|
|
318
|
+
setInputRef('');
|
|
319
|
+
}
|
|
320
|
+
if (event.key === ',') {
|
|
321
|
+
if (inputRef.length === 0)
|
|
322
|
+
event.preventDefault();
|
|
323
|
+
// If the current typed query expression matches the labelNameValueWithoutQuotesRe regex (i.e. the labelvalue is not quoted), then add quotes to the labelvalue.
|
|
324
|
+
// if not, just use the current inputRef value.
|
|
325
|
+
var inputValues_1 = labelNameValueWithoutQuotesRe.test(inputRef)
|
|
326
|
+
? inputRef.replaceAll(',', '')
|
|
327
|
+
: addQuotesToInputRefLabelValue(inputRef).replaceAll(',', '');
|
|
328
|
+
// if the currentLabelsCollection array is null, we don't need to concat the current inputRef value with the currentLabelsCollection array, so we just push to it.
|
|
329
|
+
if (currentLabelsCollection === null) {
|
|
330
|
+
setCurrentLabelsCollection([inputValues_1]);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
setCurrentLabelsCollection(function (oldValues) {
|
|
334
|
+
// Don't add the current inputRef value to the currentLabelsCollection array if it doesn't match the regex because that will cause an API error.
|
|
335
|
+
if (!labelNameValueRe.test(inputRef))
|
|
336
|
+
return oldValues;
|
|
337
|
+
return __spreadArray(__spreadArray([], (oldValues !== null && oldValues !== void 0 ? oldValues : []), true), [inputValues_1], false);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
// update the currentQuery expression with the currentLabelsCollection array if it's not null, otherwise use the current inputRef value.
|
|
341
|
+
setMatchersString(currentLabelsCollection !== null
|
|
342
|
+
? "".concat(currentLabelsCollection === null || currentLabelsCollection === void 0 ? void 0 : currentLabelsCollection.join(','), ",").concat(inputValues_1)
|
|
343
|
+
: "".concat(inputValues_1, ","));
|
|
344
|
+
setInputRef('');
|
|
345
|
+
}
|
|
346
|
+
// We suggest the appropriate label names and label values when a user is typing, depending on what the user has typed.
|
|
347
|
+
// For example, if the user types "labelName=", we suggest the label values next.
|
|
348
|
+
// This bit of code is used for the opposite of the above bit of code, when a user is deleting characters by pressing del/backspace
|
|
349
|
+
// We update the currentQuery expression with what's in the inputRef value so that the suggestions are updated accordingly.
|
|
350
|
+
if (event.key === 'Backspace' && inputRef.length > 0) {
|
|
351
|
+
// if the currentLabelsCollection array is not empty i.e has already previously completed expressions, then we first need to turn the array into a string
|
|
352
|
+
// so it can be concatenated with the current inputRef value. that becomes something like "labelName="value",newLabelName="val
|
|
353
|
+
if (currentLabelsCollection != null && currentLabelsCollection.length > 0) {
|
|
354
|
+
setMatchersString("".concat(currentLabelsCollection === null || currentLabelsCollection === void 0 ? void 0 : currentLabelsCollection.join(','), ",").concat(inputRef, "}"));
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
// if not, we jsut update the currentQuery expression with the current inputRef value.
|
|
358
|
+
setMatchersString(inputRef);
|
|
359
|
+
}
|
|
360
|
+
if (currentLabelsCollection === null && inputRef.length === 0) {
|
|
361
|
+
setMatchersString('');
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
var handleKeyPress = function (event) {
|
|
366
|
+
// If there is a highlighted suggestion and enter is hit, we complete
|
|
367
|
+
// with the highlighted suggestion.
|
|
368
|
+
if (highlightedSuggestionIndex >= 0 && event.key === 'Enter') {
|
|
369
|
+
applyHighlightedSuggestion();
|
|
370
|
+
if (lastCompleted.type === Labels.labelValue)
|
|
371
|
+
setLabelValuesResponse(null);
|
|
372
|
+
var matchers_1 = currentQuery.matchers.filter(function (matcher) { return matcher.key !== '__name__'; });
|
|
373
|
+
setLocalMatchers(function (prevState) {
|
|
374
|
+
if (inputRef.length > 0)
|
|
375
|
+
return prevState;
|
|
376
|
+
if (matchers_1.length === 0)
|
|
377
|
+
return prevState;
|
|
378
|
+
return matchers_1;
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
// If a user has manually typed in a label name that actually exists in the list of label name (and did not use the
|
|
382
|
+
// highlight + arrow keys up/down + Enter/Mouse click method to complete it), and has also typed a literal value, i.e. labelName=,
|
|
383
|
+
// then we can apply a suggestion using the typed label name. This will be as if the user had highlighted the label name and hit enter.
|
|
384
|
+
if (event.key === '!' || event.key === '~' || event.key === '=') {
|
|
385
|
+
var labelName_1 = inputRef.split(literalRe)[0];
|
|
386
|
+
if (suggestionSections.labelNames.length > 0) {
|
|
387
|
+
// Find the label name in the suggestion list and get the index
|
|
388
|
+
var suggestion = suggestionSections.labelNames.find(function (suggestion) { return suggestion.value === labelName_1; });
|
|
389
|
+
// If the typed label name exists, we can apply it using the applySuggestion function
|
|
390
|
+
if (suggestion != null) {
|
|
391
|
+
applySuggestion(suggestionSections.labelNames.indexOf(suggestion));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// Same as above, If a user has typed in a label name and literal (and did not use the suggestion box to complete it),
|
|
396
|
+
// we can manually show the next set of suggestions, which are the label values, by applying a literal suggestion.
|
|
397
|
+
if (labelNameLiteralRe.test(inputRef)) {
|
|
398
|
+
var literal_1 = inputRef.split(literalRe)[1];
|
|
399
|
+
if (suggestionSections.literals.length > 0) {
|
|
400
|
+
// Find the literal in the suggestion list and get the index
|
|
401
|
+
var suggestion = suggestionSections.literals.find(function (suggestion) { return suggestion.value === literal_1; });
|
|
402
|
+
// If the typed literal exists, we can apply it using the applySuggestion function
|
|
403
|
+
if (suggestion != null) {
|
|
404
|
+
applySuggestion(suggestionSections.literals.indexOf(suggestion));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// If no suggestions is highlighted and we hit enter, we run the query,
|
|
409
|
+
// and hide suggestions until another actions enables them again.
|
|
410
|
+
if (highlightedSuggestionIndex === -1 && event.key === 'Enter') {
|
|
411
|
+
if (lastCompleted.type === 'labelValue')
|
|
412
|
+
setLabelValuesResponse(null);
|
|
413
|
+
setShowSuggest(false);
|
|
414
|
+
runQuery();
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
setShowSuggest(true);
|
|
418
|
+
};
|
|
419
|
+
var handleKeyDown = function (event) {
|
|
420
|
+
if (event.key === 'Backspace' && inputRef === '') {
|
|
421
|
+
if (currentLabelsCollection === null)
|
|
422
|
+
return;
|
|
423
|
+
removeLabel(currentLabelsCollection.length - 1);
|
|
424
|
+
removeLocalMatcher();
|
|
425
|
+
}
|
|
426
|
+
// Don't need to handle any key interactions if no suggestions there.
|
|
427
|
+
if (suggestionsLength === 0) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
// Handle tabbing through suggestions.
|
|
431
|
+
if (event.key === 'Tab' && suggestionsLength > 0) {
|
|
432
|
+
event.preventDefault();
|
|
433
|
+
if (event.shiftKey) {
|
|
434
|
+
// Shift + tab goes up.
|
|
435
|
+
highlightPrevious();
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
// Just tab goes down.
|
|
439
|
+
highlightNext();
|
|
440
|
+
}
|
|
441
|
+
// Up arrow highlights previous suggestions.
|
|
442
|
+
if (event.key === 'ArrowUp') {
|
|
443
|
+
highlightPrevious();
|
|
444
|
+
}
|
|
445
|
+
// Down arrow highlights next suggestions.
|
|
446
|
+
if (event.key === 'ArrowDown') {
|
|
447
|
+
highlightNext();
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
var focus = function () {
|
|
451
|
+
setFocusedInput(true);
|
|
452
|
+
};
|
|
453
|
+
var unfocus = function () {
|
|
454
|
+
setFocusedInput(false);
|
|
455
|
+
resetHighlight();
|
|
456
|
+
};
|
|
457
|
+
var removeLabel = function (label) {
|
|
458
|
+
if (currentLabelsCollection === null)
|
|
459
|
+
return;
|
|
460
|
+
var newLabels = __spreadArray([], currentLabelsCollection, true);
|
|
461
|
+
newLabels.splice(label, 1);
|
|
462
|
+
setCurrentLabelsCollection(newLabels);
|
|
463
|
+
var newLabelsAsAString = newLabels.join(',');
|
|
464
|
+
setMatchersString(newLabelsAsAString);
|
|
465
|
+
};
|
|
466
|
+
var removeLocalMatcher = function () {
|
|
467
|
+
if (localMatchers === null)
|
|
468
|
+
return;
|
|
469
|
+
var newMatchers = __spreadArray([], localMatchers, true);
|
|
470
|
+
newMatchers.splice(localMatchers.length - 1, 1);
|
|
471
|
+
setLocalMatchers(newMatchers);
|
|
472
|
+
};
|
|
473
|
+
var profileSelected = currentQuery.profType.profileName === '';
|
|
474
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", __assign({ ref: setDivInputRef, className: "w-full flex items-center text-sm border-gray-300 dark:border-gray-600 border-b" }, { children: [_jsx("ul", __assign({ className: "flex space-x-2" }, { children: currentLabelsCollection === null || currentLabelsCollection === void 0 ? void 0 : currentLabelsCollection.map(function (value, i) { return (_jsx("li", __assign({ className: "bg-indigo-600 w-fit py-1 px-2 text-gray-100 dark-gray-900 rounded-md" }, { children: value }), i)); }) })), _jsx("input", { type: "text", className: cx('bg-transparent focus:ring-indigo-800 flex-1 block w-full px-2 py-2 text-sm outline-none', profileSelected && 'cursor-not-allowed'), placeholder: profileSelected ? 'Select a profile first to query profiles...' : 'query profiles...', onChange: onChange, value: inputRef, onBlur: unfocus, onFocus: focus, onKeyPress: handleKeyPress, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, disabled: profileSelected, title: profileSelected ? 'Select a profile first to query profiles...' : 'query profiles...' })] })), _jsx("div", __assign({ ref: setPopperElement, style: __assign(__assign({}, styles.popper), { marginLeft: 0 }) }, attributes.popper, { className: "z-50" }, { children: _jsx(Transition, __assign({ show: focusedInput && showSuggest, as: Fragment, leave: "transition ease-in duration-100", leaveFrom: "opacity-100", leaveTo: "opacity-0" }, { children: _jsxs("div", __assign({ style: { width: divInputRef === null || divInputRef === void 0 ? void 0 : divInputRef.offsetWidth }, className: "absolute z-10 max-h-[400px] mt-1 bg-gray-50 dark:bg-gray-900 shadow-lg rounded-md text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" }, { children: [labelNamesLoading ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestionSections.labelNames.map(function (l, i) { return (_jsx("div", __assign({ className: cx(highlightedSuggestionIndex === i && 'text-white bg-indigo-600', 'cursor-default select-none relative py-2 pl-3 pr-9'), onMouseOver: function () { return setHighlightedSuggestionIndex(i); }, onClick: function () { return applySuggestion(i); }, onMouseOut: function () { return resetHighlight(); } }, { children: l.value }), i)); }) })), suggestionSections.literals.map(function (l, i) { return (_jsx("div", __assign({ className: cx(highlightedSuggestionIndex === i + suggestionSections.labelNames.length &&
|
|
475
|
+
'text-white bg-indigo-600', 'cursor-default select-none relative py-2 pl-3 pr-9'), onMouseOver: function () {
|
|
476
|
+
return setHighlightedSuggestionIndex(i + suggestionSections.labelNames.length);
|
|
477
|
+
}, onClick: function () { return applySuggestion(i + suggestionSections.labelNames.length); }, onMouseOut: function () { return resetHighlight(); } }, { children: l.value }), i)); }), labelValuesLoading && lastCompleted.type === 'literal' ? (_jsx(LoadingSpinner, {})) : (_jsx(_Fragment, { children: suggestionSections.labelValues.map(function (l, i) { return (_jsx("div", __assign({ className: cx(highlightedSuggestionIndex === i && 'text-white bg-indigo-600', 'cursor-default select-none relative py-2 pl-3 pr-9'), onMouseOver: function () { return setHighlightedSuggestionIndex(i); }, onClick: function () { return applySuggestion(i); }, onMouseOut: function () { return resetHighlight(); } }, { children: l.value }), i)); }) }))] })) })) }))] }));
|
|
478
|
+
};
|
|
479
|
+
export default MatchersInput;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
var defaultRadius = 3;
|
|
14
|
+
var MetricsCircle = function (_a) {
|
|
15
|
+
var cx = _a.cx, cy = _a.cy, radius = _a.radius;
|
|
16
|
+
return (_jsx("g", __assign({ className: "circle" }, { children: _jsx("circle", { cx: cx, cy: cy, r: radius !== undefined ? radius : defaultRadius }) })));
|
|
17
|
+
};
|
|
18
|
+
export default MetricsCircle;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SingleProfileSelection } from '..';
|
|
2
|
+
import { MetricsSeries as MetricsSeriesPb, Label } from '@parca/client';
|
|
3
|
+
import { DateTimeRange } from '@parca/components';
|
|
4
|
+
interface RawMetricsGraphProps {
|
|
5
|
+
data: MetricsSeriesPb[];
|
|
6
|
+
from: number;
|
|
7
|
+
to: number;
|
|
8
|
+
profile: SingleProfileSelection | null;
|
|
9
|
+
onSampleClick: (timestamp: number, value: number, labels: Label[]) => void;
|
|
10
|
+
onLabelClick: (labelName: string, labelValue: string) => void;
|
|
11
|
+
setTimeRange: (range: DateTimeRange) => void;
|
|
12
|
+
sampleUnit: string;
|
|
13
|
+
width?: number;
|
|
14
|
+
}
|
|
15
|
+
interface HighlightedSeries {
|
|
16
|
+
seriesIndex: number;
|
|
17
|
+
labels: Label[];
|
|
18
|
+
timestamp: number;
|
|
19
|
+
value: number;
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
}
|
|
23
|
+
declare const MetricsGraph: ({ data, from, to, profile, onSampleClick, onLabelClick, setTimeRange, sampleUnit, }: RawMetricsGraphProps) => JSX.Element;
|
|
24
|
+
export default MetricsGraph;
|
|
25
|
+
export declare const parseValue: (value: string) => number | null;
|
|
26
|
+
interface MetricsTooltipProps {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
highlighted: HighlightedSeries;
|
|
30
|
+
onLabelClick: (labelName: string, labelValue: string) => void;
|
|
31
|
+
contextElement: Element | null;
|
|
32
|
+
sampleUnit: string;
|
|
33
|
+
}
|
|
34
|
+
export declare const MetricsTooltip: ({ x, y, highlighted, onLabelClick, contextElement, sampleUnit, }: MetricsTooltipProps) => JSX.Element;
|
|
35
|
+
export declare const RawMetricsGraph: ({ data, from, to, profile, onSampleClick, onLabelClick, setTimeRange, width, sampleUnit, }: RawMetricsGraphProps) => JSX.Element;
|