@parca/profile 0.19.81 → 0.19.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/MatchersInput/index.d.ts +2 -34
  3. package/dist/MatchersInput/index.d.ts.map +1 -1
  4. package/dist/MatchersInput/index.js +14 -91
  5. package/dist/MetricsGraph/index.d.ts.map +1 -1
  6. package/dist/MetricsGraph/index.js +13 -1
  7. package/dist/ProfileMetricsGraph/index.d.ts.map +1 -1
  8. package/dist/ProfileMetricsGraph/index.js +6 -29
  9. package/dist/ProfileSelector/MetricsGraphSection.d.ts +2 -9
  10. package/dist/ProfileSelector/MetricsGraphSection.d.ts.map +1 -1
  11. package/dist/ProfileSelector/MetricsGraphSection.js +3 -38
  12. package/dist/ProfileSelector/index.d.ts +1 -29
  13. package/dist/ProfileSelector/index.d.ts.map +1 -1
  14. package/dist/ProfileSelector/index.js +12 -9
  15. package/dist/QueryControls/index.d.ts +42 -0
  16. package/dist/QueryControls/index.d.ts.map +1 -0
  17. package/dist/{ProfileSelector/QueryControls.js → QueryControls/index.js} +16 -13
  18. package/dist/SimpleMatchers/Select.js +1 -1
  19. package/dist/SimpleMatchers/index.d.ts +2 -11
  20. package/dist/SimpleMatchers/index.d.ts.map +1 -1
  21. package/dist/SimpleMatchers/index.js +34 -45
  22. package/dist/ViewMatchers/index.d.ts +0 -9
  23. package/dist/ViewMatchers/index.d.ts.map +1 -1
  24. package/dist/ViewMatchers/index.js +20 -12
  25. package/dist/contexts/LabelsQueryProvider.d.ts +35 -0
  26. package/dist/contexts/LabelsQueryProvider.d.ts.map +1 -0
  27. package/dist/contexts/LabelsQueryProvider.js +70 -0
  28. package/dist/contexts/UnifiedLabelsContext.d.ts +37 -0
  29. package/dist/contexts/UnifiedLabelsContext.d.ts.map +1 -0
  30. package/dist/contexts/UnifiedLabelsContext.js +88 -0
  31. package/dist/contexts/utils.d.ts +10 -0
  32. package/dist/contexts/utils.d.ts.map +1 -0
  33. package/dist/contexts/utils.js +31 -0
  34. package/dist/hooks/useLabels.d.ts +23 -0
  35. package/dist/hooks/useLabels.d.ts.map +1 -0
  36. package/dist/hooks/useLabels.js +75 -0
  37. package/dist/hooks/useQueryState.d.ts +2 -0
  38. package/dist/hooks/useQueryState.d.ts.map +1 -1
  39. package/dist/hooks/useQueryState.js +18 -0
  40. package/dist/index.d.ts +9 -3
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +9 -3
  43. package/dist/styles.css +1 -1
  44. package/dist/useSumBy.js +1 -1
  45. package/package.json +2 -2
  46. package/src/MatchersInput/index.tsx +17 -163
  47. package/src/MetricsGraph/index.tsx +17 -1
  48. package/src/ProfileMetricsGraph/index.tsx +17 -98
  49. package/src/ProfileSelector/MetricsGraphSection.tsx +14 -115
  50. package/src/ProfileSelector/index.tsx +106 -109
  51. package/src/{ProfileSelector/QueryControls.tsx → QueryControls/index.tsx} +66 -84
  52. package/src/SimpleMatchers/Select.tsx +1 -1
  53. package/src/SimpleMatchers/index.tsx +46 -85
  54. package/src/ViewMatchers/index.tsx +22 -30
  55. package/src/contexts/LabelsQueryProvider.tsx +142 -0
  56. package/src/contexts/UnifiedLabelsContext.tsx +155 -0
  57. package/src/contexts/utils.ts +43 -0
  58. package/src/hooks/useLabels.ts +121 -0
  59. package/src/hooks/useQueryState.ts +25 -0
  60. package/src/index.tsx +29 -3
  61. package/src/useSumBy.ts +1 -1
  62. package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts +0 -29
  63. package/dist/MetricsGraph/UtilizationMetrics/Throughput.d.ts.map +0 -1
  64. package/dist/MetricsGraph/UtilizationMetrics/Throughput.js +0 -175
  65. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts +0 -28
  66. package/dist/MetricsGraph/UtilizationMetrics/index.d.ts.map +0 -1
  67. package/dist/MetricsGraph/UtilizationMetrics/index.js +0 -186
  68. package/dist/ProfileSelector/QueryControls.d.ts +0 -43
  69. package/dist/ProfileSelector/QueryControls.d.ts.map +0 -1
  70. package/dist/contexts/MatchersInputLabelsContext.d.ts +0 -29
  71. package/dist/contexts/MatchersInputLabelsContext.d.ts.map +0 -1
  72. package/dist/contexts/MatchersInputLabelsContext.js +0 -79
  73. package/dist/contexts/SimpleMatchersLabelContext.d.ts +0 -25
  74. package/dist/contexts/SimpleMatchersLabelContext.d.ts.map +0 -1
  75. package/dist/contexts/SimpleMatchersLabelContext.js +0 -115
  76. package/dist/contexts/UtilizationLabelsContext.d.ts +0 -15
  77. package/dist/contexts/UtilizationLabelsContext.d.ts.map +0 -1
  78. package/dist/contexts/UtilizationLabelsContext.js +0 -25
  79. package/src/MetricsGraph/UtilizationMetrics/Throughput.tsx +0 -405
  80. package/src/MetricsGraph/UtilizationMetrics/index.tsx +0 -426
  81. package/src/contexts/MatchersInputLabelsContext.tsx +0 -141
  82. package/src/contexts/SimpleMatchersLabelContext.tsx +0 -189
  83. package/src/contexts/UtilizationLabelsContext.tsx +0 -45
@@ -11,25 +11,29 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- import { useState } from 'react';
14
+ import { useRef, useState } from 'react';
15
15
  import { Switch } from '@headlessui/react';
16
16
  import { Button, DateTimeRangePicker, useParcaContext } from '@parca/components';
17
17
  import { TEST_IDS, testId } from '@parca/test-utils';
18
18
  import MatchersInput from '../MatchersInput';
19
19
  import ProfileTypeSelector from '../ProfileTypeSelector';
20
- import SelectWithRefresh from '../SelectWithRefresh';
21
- import SimpleMatchers from '../SimpleMatchers';
20
+ import { SelectWithRefresh } from '../SelectWithRefresh';
21
+ import SimpleMatchers from '../SimpleMatchers/';
22
22
  import ViewMatchers from '../ViewMatchers';
23
- export function QueryControls({ showProfileTypeSelector, profileTypesData, profileTypesLoading, selectedProfileName, setProfileName, viewComponent, setQueryBrowserMode, advancedModeForQueryBrowser, setAdvancedModeForQueryBrowser, setMatchersString, setQueryExpression, query, queryBrowserRef, timeRangeSelection, setTimeRangeSelection, searchDisabled, queryClient, labels, sumBySelection, sumBySelectionLoading, setUserSumBySelection, sumByRef, profileType, showSumBySelector, profileTypesError, refreshLabelNames, }) {
23
+ import { useLabelNames } from '../hooks/useLabels';
24
+ export function QueryControls({ profileType, timeRangeSelection, setTimeRangeSelection, setQueryExpression, searchDisabled, showProfileTypeSelector = false, showSumBySelector = false, showAdvancedMode = true, profileTypesData, profileTypesLoading = false, selectedProfileName, setProfileName, profileTypesError, viewComponent, setQueryBrowserMode, advancedModeForQueryBrowser = false, setAdvancedModeForQueryBrowser, queryBrowserRef, labels = [], sumBySelection = [], sumBySelectionLoading = false, setUserSumBySelection, sumByRef, queryClient, }) {
24
25
  const { timezone } = useParcaContext();
26
+ const defaultQueryBrowserRef = useRef(null);
27
+ const actualQueryBrowserRef = queryBrowserRef ?? defaultQueryBrowserRef;
25
28
  const [searchExecutedTimestamp, setSearchExecutedTimestamp] = useState(0);
26
- return (_jsxs("div", { className: "flex w-full flex-wrap items-start gap-2", ...testId(TEST_IDS.QUERY_CONTROLS_CONTAINER), children: [showProfileTypeSelector && (_jsxs("div", { children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.PROFILE_TYPE_LABEL), children: "Profile type" }), _jsx(ProfileTypeSelector, { profileTypesData: profileTypesData, loading: profileTypesLoading, selectedKey: selectedProfileName, onSelection: setProfileName, error: profileTypesError, disabled: viewComponent?.disableProfileTypesDropdown })] })), _jsxs("div", { className: "w-full flex-1 flex flex-col gap-1 mt-auto", ref: queryBrowserRef, ...testId(TEST_IDS.QUERY_BROWSER_CONTAINER), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.QUERY_LABEL), children: "Query" }), viewComponent?.disableExplorativeQuerying !== true && (_jsxs(_Fragment, { children: [_jsxs(Switch, { checked: advancedModeForQueryBrowser, onChange: () => {
27
- setAdvancedModeForQueryBrowser(!advancedModeForQueryBrowser);
28
- setQueryBrowserMode(advancedModeForQueryBrowser ? 'simple' : 'advanced');
29
+ const { refetch: refetchLabelNames } = useLabelNames(queryClient, profileType, timeRangeSelection.getFromMs(), timeRangeSelection.getToMs());
30
+ return (_jsxs("div", { className: "flex w-full flex-wrap items-start gap-2", ...testId(TEST_IDS.QUERY_CONTROLS_CONTAINER), children: [showProfileTypeSelector && (_jsxs("div", { children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.PROFILE_TYPE_LABEL), children: "Profile type" }), _jsx(ProfileTypeSelector, { profileTypesData: profileTypesData, loading: profileTypesLoading, selectedKey: selectedProfileName, onSelection: setProfileName ?? (() => { }), error: profileTypesError, disabled: viewComponent?.disableProfileTypesDropdown })] })), _jsxs("div", { className: "w-full flex-1 flex flex-col gap-1 mt-auto", ref: actualQueryBrowserRef, ...testId(TEST_IDS.QUERY_BROWSER_CONTAINER), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.QUERY_LABEL), children: "Query" }), showAdvancedMode && viewComponent?.disableExplorativeQuerying !== true && (_jsxs(_Fragment, { children: [_jsxs(Switch, { checked: advancedModeForQueryBrowser, onChange: () => {
31
+ setAdvancedModeForQueryBrowser?.(!advancedModeForQueryBrowser);
32
+ setQueryBrowserMode?.(advancedModeForQueryBrowser ? 'simple' : 'advanced');
29
33
  }, className: `${advancedModeForQueryBrowser ? 'bg-indigo-600' : 'bg-gray-400 dark:bg-gray-800'} relative inline-flex h-[20px] w-[44px] shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75`, ...testId(TEST_IDS.ADVANCED_MODE_SWITCH), children: [_jsx("span", { className: "sr-only", children: "Use setting" }), _jsx("span", { "aria-hidden": "true", className: `${advancedModeForQueryBrowser ? 'translate-x-6' : 'translate-x-0'} pointer-events-none inline-block h-[16px] w-[16px] transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out` })] }), _jsx("label", { className: "text-xs", ...testId(TEST_IDS.QUERY_MODE_LABEL), children: "Advanced Mode" })] }))] }), viewComponent?.createViewComponent] }), viewComponent?.disableExplorativeQuerying === true &&
30
34
  viewComponent?.labelnames !== undefined &&
31
- viewComponent?.labelnames.length >= 1 ? (_jsx(ViewMatchers, { labelNames: viewComponent.labelnames, setMatchersString: setMatchersString, profileType: selectedProfileName, runQuery: setQueryExpression, currentQuery: query, queryClient: queryClient, start: timeRangeSelection.getFromMs(), end: timeRangeSelection.getToMs() })) : advancedModeForQueryBrowser ? (_jsx(MatchersInput, { setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName, queryClient: queryClient, start: timeRangeSelection.getFromMs(), end: timeRangeSelection.getToMs() })) : (_jsx(SimpleMatchers, { setMatchersString: setMatchersString, runQuery: setQueryExpression, currentQuery: query, profileType: selectedProfileName, queryBrowserRef: queryBrowserRef, queryClient: queryClient, start: timeRangeSelection.getFromMs(), end: timeRangeSelection.getToMs(), searchExecutedTimestamp: searchExecutedTimestamp }))] }), showSumBySelector && (_jsxs("div", { ...testId(TEST_IDS.SUM_BY_CONTAINER), children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", ...testId(TEST_IDS.SUM_BY_LABEL), children: "Sum by" }) }), _jsx(SelectWithRefresh, { id: "h-sum-by-selector", "data-testid": testId(TEST_IDS.SUM_BY_SELECT)['data-testid'], defaultValue: [], isMulti: true, isClearable: false, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-full max-w-80", classNamePrefix: "parca-select", value: (sumBySelection ?? []).map(sumBy => ({ label: sumBy, value: sumBy })), onChange: newValue => {
32
- setUserSumBySelection(newValue.map(option => option.value));
35
+ viewComponent?.labelnames.length >= 1 ? (_jsx(ViewMatchers, { labelNames: viewComponent.labelnames })) : showAdvancedMode && advancedModeForQueryBrowser ? (_jsx(MatchersInput, {})) : (_jsx(SimpleMatchers, { queryBrowserRef: actualQueryBrowserRef, searchExecutedTimestamp: searchExecutedTimestamp }))] }), showSumBySelector && (_jsxs("div", { ...testId(TEST_IDS.SUM_BY_CONTAINER), children: [_jsx("div", { className: "mb-0.5 mt-1.5 flex items-center justify-between", children: _jsx("label", { className: "text-xs", ...testId(TEST_IDS.SUM_BY_LABEL), children: "Sum by" }) }), _jsx(SelectWithRefresh, { id: "h-sum-by-selector", "data-testid": testId(TEST_IDS.SUM_BY_SELECT)['data-testid'], defaultValue: [], isMulti: true, isClearable: false, name: "colors", options: labels.map(label => ({ label, value: label })), className: "parca-select-container text-sm w-full max-w-80", classNamePrefix: "parca-select", value: sumBySelection.map(sumBy => ({ label: sumBy, value: sumBy })), onChange: newValue => {
36
+ setUserSumBySelection?.(newValue.map(option => option.value));
33
37
  }, placeholder: "Labels...", styles: {
34
38
  indicatorSeparator: () => ({ display: 'none' }),
35
39
  menu: provided => ({
@@ -41,11 +45,10 @@ export function QueryControls({ showProfileTypeSelector, profileTypesData, profi
41
45
  minWidth: '320px',
42
46
  position: 'absolute',
43
47
  }),
44
- // menu: provided => ({...provided, width: 'max-content', zIndex: 50}), // Setting the same zIndex as drop down menus
45
- }, isLoading: sumBySelectionLoading, isDisabled: !profileType.delta,
48
+ }, isLoading: sumBySelectionLoading, isDisabled: !profileType?.delta,
46
49
  // @ts-expect-error
47
50
  ref: sumByRef, onKeyDown: e => {
48
- const currentRef = sumByRef.current;
51
+ const currentRef = sumByRef?.current;
49
52
  if (currentRef == null) {
50
53
  return;
51
54
  }
@@ -60,7 +63,7 @@ export function QueryControls({ showProfileTypeSelector, profileTypesData, profi
60
63
  setQueryExpression(true);
61
64
  currentRef.blur();
62
65
  }
63
- }, onRefresh: refreshLabelNames, refreshTitle: "Refresh label names", refreshTestId: "sum-by-refresh-button", menuTestId: TEST_IDS.SUM_BY_SELECT_FLYOUT })] })), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection, timezone: timezone, ...testId(TEST_IDS.DATE_TIME_RANGE_PICKER) }), _jsxs("div", { children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.SEARCH_BUTTON_LABEL), children: "\u00A0" }), _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
66
+ }, onRefresh: refetchLabelNames, refreshTitle: "Refresh label names", refreshTestId: "sum-by-refresh-button", menuTestId: TEST_IDS.SUM_BY_SELECT_FLYOUT })] })), _jsx(DateTimeRangePicker, { onRangeSelection: setTimeRangeSelection, range: timeRangeSelection, timezone: timezone, ...testId(TEST_IDS.DATE_TIME_RANGE_PICKER) }), _jsxs("div", { children: [_jsx("label", { className: "text-xs", ...testId(TEST_IDS.SEARCH_BUTTON_LABEL), children: "\u00A0" }), _jsx(Button, { disabled: searchDisabled, onClick: (e) => {
64
67
  e.preventDefault();
65
68
  setSearchExecutedTimestamp(Date.now());
66
69
  setQueryExpression(true);
@@ -179,7 +179,7 @@ const CustomSelect = ({ items: itemsProp, selectedKey, onSelection, placeholder
179
179
  setIsOpen(false);
180
180
  }, children: "Add" }) }))] })) : (_jsx("input", { ref: searchInputRef, type: "text", className: "w-full px-4 h-[45px] text-sm border-none rounded-none ring-0 outline-none bg-gray-50 dark:bg-gray-800 dark:text-white", placeholder: "Search...", value: searchTerm, onChange: e => setSearchTerm(e.target.value) })) }) })), _jsx("div", { className: "flex-1 min-h-0", children: loading === true ? (_jsx("div", { className: "w-[270px]", children: loader })) : groupedFilteredItems.length === 0 ? (_jsx("div", { className: "px-4 py-3 text-sm text-gray-500 dark:text-gray-400 text-center", ...testId(TEST_IDS.LABEL_VALUE_NO_RESULTS), children: "No values found" })) : (groupedFilteredItems.map(group => (_jsxs(_Fragment, { children: [groupedFilteredItems.length > 1 &&
181
181
  groupedFilteredItems.every(g => g.type !== '') &&
182
- group.type !== '' ? (_jsx("div", { className: "pl-2", children: _jsx(DividerWithLabel, { label: group.type }) })) : null, group.values.map((item, index) => (_jsx(OptionItem, { item: item, index: index, optionRefs: optionRefs, focusedIndex: focusedIndex, selectedKey: selectedKey, handleSelection: handleSelection }, item.key)))] })))) }), refetchValues !== undefined && loading !== true && (_jsx(RefreshButton, { onClick: () => void handleRefetch(), disabled: isRefetching, title: "Refresh label values", testId: TEST_IDS.LABEL_VALUE_REFRESH_BUTTON, sticky: true, loading: isRefetching }))] }) }))] }));
182
+ group.type !== '' ? (_jsx("div", { className: "pl-2", children: _jsx(DividerWithLabel, { label: group.type }) })) : null, group.values.map((item, index) => (_jsx(OptionItem, { item: item, index: index, optionRefs: optionRefs, focusedIndex: focusedIndex, selectedKey: selectedKey, handleSelection: handleSelection }, item.key)))] })))) }), refetchValues !== undefined && loading !== true && (_jsx(RefreshButton, { onClick: () => void handleRefetch(), disabled: isRefetching, title: "Refresh results", testId: TEST_IDS.LABEL_VALUE_REFRESH_BUTTON, sticky: true, loading: isRefetching }))] }) }))] }));
183
183
  };
184
184
  const OptionItem = ({ item, optionRefs, index, focusedIndex, selectedKey, handleSelection, }) => {
185
185
  return (_jsxs("div", { ref: el => {
@@ -1,18 +1,9 @@
1
- import { QueryServiceClient } from '@parca/client';
2
- import { Query } from '@parca/parser';
3
1
  import { type SelectItem } from './Select';
4
2
  interface Props {
5
- queryClient: QueryServiceClient;
6
- setMatchersString: (arg: string) => void;
7
- runQuery: () => void;
8
- currentQuery: Query;
9
- profileType: string;
10
3
  queryBrowserRef: React.RefObject<HTMLDivElement>;
11
- start?: number;
12
- end?: number;
13
4
  searchExecutedTimestamp?: number;
14
5
  }
15
6
  export declare const transformLabelsForSelect: (labelNames: string[]) => SelectItem[];
16
- export default function SimpleMathersWithProvider(props: Props): JSX.Element;
17
- export {};
7
+ declare const SimpleMatchers: ({ queryBrowserRef, searchExecutedTimestamp, }: Props) => JSX.Element;
8
+ export default SimpleMatchers;
18
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/index.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAMpC,OAAe,EAAC,KAAK,UAAU,EAAC,MAAM,UAAU,CAAC;AAEjD,UAAU,KAAK;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAoBD,eAAO,MAAM,wBAAwB,GAAI,YAAY,MAAM,EAAE,KAAG,UAAU,EAQzE,CAAC;AAidF,MAAM,CAAC,OAAO,UAAU,yBAAyB,CAAC,KAAK,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAoB3E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/index.tsx"],"names":[],"mappings":"AA0BA,OAAe,EAAC,KAAK,UAAU,EAAC,MAAM,UAAU,CAAC;AAEjD,UAAU,KAAK;IACb,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACjD,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAUD,eAAO,MAAM,wBAAwB,GAAI,YAAY,MAAM,EAAE,KAAG,UAAU,EAQzE,CAAC;AAiDF,QAAA,MAAM,cAAc,GAAI,+CAIrB,KAAK,KAAG,GAAG,CAAC,OAyZd,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -11,31 +11,23 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- import { useCallback, useEffect, useMemo, useState } from 'react';
14
+ import { useCallback, useEffect, useState } from 'react';
15
15
  import { Icon } from '@iconify/react';
16
16
  import { useQueryClient } from '@tanstack/react-query';
17
17
  import cx from 'classnames';
18
- import { useGrpcMetadata } from '@parca/components';
18
+ import { useGrpcMetadata, useParcaContext } from '@parca/components';
19
19
  import { TEST_IDS, testId } from '@parca/test-utils';
20
20
  import { millisToProtoTimestamp, sanitizeLabelValue } from '@parca/utilities';
21
- import { LabelProvider, useLabels } from '../contexts/SimpleMatchersLabelContext';
22
- import { useUtilizationLabels } from '../contexts/UtilizationLabelsContext';
21
+ import { useUnifiedLabels } from '../contexts/UnifiedLabelsContext';
22
+ import { transformLabelName } from '../contexts/utils';
23
+ import { useQueryState } from '../hooks/useQueryState';
23
24
  import Select from './Select';
24
- const trimOtelPrefix = (labelName) => {
25
- if (labelName.startsWith('attributes_resource.')) {
26
- return labelName.replace('attributes_resource.', '');
27
- }
28
- if (labelName.startsWith('attributes.')) {
29
- return labelName.replace('attributes.', '');
30
- }
31
- return labelName;
32
- };
33
25
  export const transformLabelsForSelect = (labelNames) => {
34
26
  return labelNames.map(labelName => ({
35
27
  key: labelName,
36
28
  element: {
37
- active: _jsx(_Fragment, { children: trimOtelPrefix(labelName) }),
38
- expanded: _jsx(_Fragment, { children: trimOtelPrefix(labelName) }),
29
+ active: _jsx(_Fragment, { children: transformLabelName(labelName) }),
30
+ expanded: _jsx(_Fragment, { children: transformLabelName(labelName) }),
39
31
  },
40
32
  }));
41
33
  };
@@ -69,15 +61,19 @@ const operatorOptions = [
69
61
  },
70
62
  },
71
63
  ];
72
- const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileType, queryBrowserRef, start, end, searchExecutedTimestamp, }) => {
73
- const utilizationLabels = useUtilizationLabels();
64
+ const SimpleMatchers = ({ queryBrowserRef, searchExecutedTimestamp, }) => {
74
65
  const [queryRows, setQueryRows] = useState([
75
66
  { labelName: '', operator: '=', labelValue: '', labelValues: [], isLoading: false },
76
67
  ]);
77
68
  const reactQueryClient = useQueryClient();
78
69
  const metadata = useGrpcMetadata();
70
+ const { queryServiceClient: parcaQueryClient } = useParcaContext();
79
71
  const [showAll, setShowAll] = useState(false);
80
72
  const [isActivelyEditing, setIsActivelyEditing] = useState(false);
73
+ const { labelNameMappingsForSimpleMatchers: labelNameOptions, isLabelNamesLoading: labelNamesLoading, refetchLabelNames, suffix, } = useUnifiedLabels();
74
+ const { draftSelection, setDraftMatchers, draftParsedQuery } = useQueryState({
75
+ suffix,
76
+ });
81
77
  // Reset editing mode when search is executed
82
78
  useEffect(() => {
83
79
  if (searchExecutedTimestamp !== undefined && searchExecutedTimestamp > 0) {
@@ -88,14 +84,20 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
88
84
  const visibleRows = showAll || isActivelyEditing ? queryRows : queryRows.slice(0, 3);
89
85
  const hiddenRowsCount = queryRows.length - 3;
90
86
  const maxWidthInPixels = `max-w-[${queryBrowserRef.current?.offsetWidth.toString()}px]`;
91
- const currentMatchers = currentQuery.matchersString();
87
+ const currentMatchers = draftParsedQuery?.matchersString();
88
+ const profileType = draftParsedQuery?.profileType().toString();
89
+ const start = draftSelection.from;
90
+ const end = draftSelection.to;
92
91
  const fetchLabelValues = useCallback(async (labelName) => {
93
- if (labelName == null || labelName === '' || profileType == null || profileType === '') {
92
+ if (labelName == null || labelName === '') {
93
+ return [];
94
+ }
95
+ if (profileType == null || profileType === '') {
94
96
  return [];
95
97
  }
96
98
  try {
97
99
  const values = await reactQueryClient.fetchQuery([labelName, profileType, start, end], async () => {
98
- const response = await queryClient.values({
100
+ const response = await parcaQueryClient.values({
99
101
  labelName,
100
102
  match: [],
101
103
  profileType,
@@ -116,18 +118,14 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
116
118
  console.error('Error fetching label values:', error);
117
119
  return [];
118
120
  }
119
- }, [queryClient, metadata, profileType, reactQueryClient, start, end]);
120
- const fetchLabelValuesUtilization = useCallback(async (labelName) => {
121
- return (await utilizationLabels?.utilizationFetchLabelValues?.(labelName)) ?? [];
122
- }, [utilizationLabels]);
121
+ }, [parcaQueryClient, metadata, profileType, reactQueryClient, start, end]);
123
122
  const updateMatchersString = useCallback((rows) => {
124
123
  const matcherString = rows
125
124
  .filter(row => row.labelName.length > 0 && row.labelValue)
126
125
  .map(row => `${row.labelName}${row.operator}"${row.labelValue}"`)
127
126
  .join(',');
128
- setMatchersString(matcherString);
129
- }, [setMatchersString]);
130
- const { labelNameOptions, isLoading: labelNamesLoading, refetchLabelValues, refetchLabelNames, } = useLabels();
127
+ setDraftMatchers(matcherString);
128
+ }, [setDraftMatchers]);
131
129
  // Helper to ensure selected label name is in the options (for page load before API returns)
132
130
  const getLabelNameOptionsWithSelected = useCallback((selectedLabelName) => {
133
131
  if (selectedLabelName === '')
@@ -184,14 +182,10 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
184
182
  }
185
183
  }, [labelNameOptions]);
186
184
  const fetchLabelValuesUnified = useCallback(async (labelName) => {
187
- const labelType = labelNameOptions.find(option => option.values.some(e => e.key === labelName))?.type;
188
- const labelValues = labelType === 'gpu'
189
- ? await fetchLabelValuesUtilization(labelName)
190
- : await fetchLabelValues(labelName);
191
- return labelValues;
192
- }, [fetchLabelValues, fetchLabelValuesUtilization, labelNameOptions]);
185
+ return await fetchLabelValues(labelName);
186
+ }, [fetchLabelValues]);
193
187
  useEffect(() => {
194
- if (currentMatchers === '') {
188
+ if (currentMatchers === '' || currentMatchers === undefined) {
195
189
  const defaultRow = {
196
190
  labelName: '',
197
191
  operator: '=',
@@ -205,7 +199,7 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
205
199
  let isCancelled = false;
206
200
  const fetchAndSetQueryRows = async () => {
207
201
  const newRows = await Promise.all(currentMatchers.split(',').map(async (matcher) => {
208
- const match = matcher.match(/([^=!~]+)([=!~]{1,2})(.+)/);
202
+ const match = matcher.match(/^([^=!~]+)([=!~]{1,2})(.+)$/);
209
203
  if (match === null)
210
204
  return null;
211
205
  const [, labelName, operator, labelValue] = match;
@@ -314,11 +308,14 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
314
308
  };
315
309
  }, [queryRows, fetchLabelValuesUnified]);
316
310
  const isRowRegex = (row) => row.operator === '=~' || row.operator === '!~';
311
+ const handleRefetchForLabelValues = useCallback(async (labelName) => {
312
+ await fetchLabelValuesUnified(labelName);
313
+ }, [fetchLabelValuesUnified]);
317
314
  return (_jsxs("div", { className: `flex items-center gap-3 ${maxWidthInPixels} w-full flex-wrap`, id: "simple-matchers", ...testId(TEST_IDS.SIMPLE_MATCHERS_CONTAINER), children: [visibleRows.map((row, index) => (_jsxs("div", { className: "flex items-center", ...testId(TEST_IDS.SIMPLE_MATCHER_ROW), children: [_jsx(Select, { items: getLabelNameOptionsWithSelected(row.labelName), onSelection: value => handleUpdateRow(index, 'labelName', value), placeholder: "Select label name", selectedKey: row.labelName, className: "rounded-tr-none rounded-br-none ring-0 focus:ring-0 outline-none", loading: labelNamesLoading, searchable: true, ...testId(TEST_IDS.LABEL_NAME_SELECT), refetchValues: refetchLabelNames }), _jsx(Select, { items: operatorOptions, onSelection: value => handleUpdateRow(index, 'operator', value), selectedKey: row.operator, className: "rounded-none ring-0 focus:ring-0 outline-none", ...testId(TEST_IDS.OPERATOR_SELECT) }), _jsx(Select, { items: transformLabelsForSelect(row.labelValue !== '' && !row.labelValues.includes(row.labelValue)
318
315
  ? [...row.labelValues, row.labelValue]
319
316
  : row.labelValues), onSelection: value => handleUpdateRow(index, 'labelValue', value), placeholder: "Select label value", selectedKey: row.labelValue, className: "rounded-none ring-0 focus:ring-0 outline-none max-w-48", optionsClassname: cx('max-w-[600px]', {
320
317
  'w-[300px]': isRowRegex(row),
321
- }), searchable: true, disabled: row.labelName === '', loading: row.isLoading, onButtonClick: () => handleLabelValueClick(index), editable: isRowRegex(row), ...testId(TEST_IDS.LABEL_VALUE_SELECT), refetchValues: async () => await refetchLabelValues(row.labelName), showLoadingInButton: true }), _jsx("button", { onClick: () => removeRow(index), className: cx('p-2 border-gray-200 border rounded rounded-tl-none rounded-bl-none focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900'), ...testId(TEST_IDS.REMOVE_MATCHER_BUTTON), children: _jsx(Icon, { icon: "carbon:close", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) })] }, index))), queryRows.length > 3 && !isActivelyEditing && (_jsx("button", { onClick: () => {
318
+ }), searchable: true, disabled: row.labelName === '', loading: row.isLoading, onButtonClick: () => handleLabelValueClick(index), editable: isRowRegex(row), ...testId(TEST_IDS.LABEL_VALUE_SELECT), refetchValues: async () => await handleRefetchForLabelValues(row.labelName), showLoadingInButton: true }), _jsx("button", { onClick: () => removeRow(index), className: cx('p-2 border-gray-200 border rounded rounded-tl-none rounded-bl-none focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-900'), ...testId(TEST_IDS.REMOVE_MATCHER_BUTTON), children: _jsx(Icon, { icon: "carbon:close", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) })] }, index))), queryRows.length > 3 && !isActivelyEditing && (_jsx("button", { onClick: () => {
322
319
  if (showAll) {
323
320
  // Clicking "Show less" - collapse and stop editing mode
324
321
  setShowAll(false);
@@ -330,12 +327,4 @@ const SimpleMatchers = ({ queryClient, setMatchersString, currentQuery, profileT
330
327
  }
331
328
  }, className: "mr-2 px-3 py-1 text-sm font-medium text-gray-700 dark:text-gray-200 bg-gray-100 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:bg-gray-900", ...testId(showAll ? TEST_IDS.SHOW_LESS_BUTTON : TEST_IDS.SHOW_MORE_BUTTON), children: showAll ? 'Show less' : `Show ${hiddenRowsCount} more` })), _jsx("button", { onClick: addNewRow, className: "p-2 border-gray-200 dark:bg-gray-900 dark:border-gray-600 border rounded focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", ...testId(TEST_IDS.ADD_MATCHER_BUTTON), children: _jsx(Icon, { icon: "material-symbols:add", className: "h-5 w-5 text-gray-400", "aria-hidden": "true" }) })] }));
332
329
  };
333
- export default function SimpleMathersWithProvider(props) {
334
- const labelNameFromMatchers = useMemo(() => {
335
- if (props.currentQuery === undefined)
336
- return [];
337
- const matchers = props.currentQuery.matchers;
338
- return matchers.map(matcher => matcher.key);
339
- }, [props.currentQuery]);
340
- return (_jsx(LabelProvider, { queryClient: props.queryClient, profileType: props.profileType, labelNameFromMatchers: labelNameFromMatchers, start: props.start, end: props.end, children: _jsx(SimpleMatchers, { ...props }) }));
341
- }
330
+ export default SimpleMatchers;
@@ -1,15 +1,6 @@
1
1
  import React from 'react';
2
- import { QueryServiceClient } from '@parca/client';
3
- import { Query } from '@parca/parser';
4
2
  interface Props {
5
3
  labelNames: string[];
6
- profileType: string;
7
- runQuery: () => void;
8
- currentQuery: Query;
9
- queryClient: QueryServiceClient;
10
- setMatchersString: (arg: string) => void;
11
- start?: number;
12
- end?: number;
13
4
  }
14
5
  declare const ViewMatchers: React.FC<Props>;
15
6
  export default ViewMatchers;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ViewMatchers/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAKtE,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAMpC,UAAU,KAAK;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAyKjC,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ViewMatchers/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAatE,UAAU,KAAK;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CAwKjC,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -14,15 +14,23 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
14
14
  import { useCallback, useEffect, useRef, useState } from 'react';
15
15
  import { Icon } from '@iconify/react';
16
16
  import cx from 'classnames';
17
- import { useGrpcMetadata } from '@parca/components';
17
+ import { useGrpcMetadata, useParcaContext } from '@parca/components';
18
18
  import { TEST_IDS, testId } from '@parca/test-utils';
19
19
  import { millisToProtoTimestamp, sanitizeLabelValue } from '@parca/utilities';
20
20
  import CustomSelect from '../SimpleMatchers/Select';
21
- const ViewMatchers = ({ labelNames, profileType, queryClient, runQuery, setMatchersString, start, end, currentQuery, }) => {
21
+ import { useUnifiedLabels } from '../contexts/UnifiedLabelsContext';
22
+ import { useQueryState } from '../hooks/useQueryState';
23
+ const ViewMatchers = ({ labelNames }) => {
22
24
  const [labelValuesMap, setLabelValuesMap] = useState({});
23
25
  const [isLoading, setIsLoading] = useState({});
24
26
  const metadata = useGrpcMetadata();
25
- const currentMatchers = currentQuery.matchersString();
27
+ const { queryServiceClient: parcaQueryClient } = useParcaContext();
28
+ const { suffix } = useUnifiedLabels();
29
+ const { draftSelection, setDraftMatchers, commitDraft, draftParsedQuery } = useQueryState({ suffix });
30
+ const currentMatchers = draftParsedQuery?.matchersString();
31
+ const profileType = draftParsedQuery?.profileType().toString();
32
+ const start = draftSelection.from;
33
+ const end = draftSelection.to;
26
34
  const parseCurrentMatchers = useCallback((matchersString) => {
27
35
  const matches = matchersString.match(/(\w+)="([^"]+)"/g);
28
36
  if (matches === null)
@@ -37,19 +45,19 @@ const ViewMatchers = ({ labelNames, profileType, queryClient, runQuery, setMatch
37
45
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38
46
  {});
39
47
  }, []);
40
- const initialSelections = parseCurrentMatchers(currentMatchers);
48
+ const initialSelections = parseCurrentMatchers(currentMatchers ?? '');
41
49
  const selectionsRef = useRef(initialSelections);
42
- const runQueryRef = useRef(runQuery);
50
+ const commitDraftRef = useRef(commitDraft);
43
51
  const timeoutRef = useRef(null);
44
52
  useEffect(() => {
45
- runQueryRef.current = runQuery;
46
- }, [runQuery]);
53
+ commitDraftRef.current = commitDraft;
54
+ }, [commitDraft]);
47
55
  useEffect(() => {
48
56
  selectionsRef.current = initialSelections;
49
57
  }, [initialSelections]);
50
58
  const fetchLabelValues = useCallback(async (labelName) => {
51
59
  try {
52
- const response = await queryClient.values({
60
+ const response = await parcaQueryClient.values({
53
61
  labelName,
54
62
  match: [],
55
63
  profileType,
@@ -66,7 +74,7 @@ const ViewMatchers = ({ labelNames, profileType, queryClient, runQuery, setMatch
66
74
  console.error('Error fetching label values:', error);
67
75
  return [];
68
76
  }
69
- }, [queryClient, metadata, profileType, start, end]);
77
+ }, [parcaQueryClient, metadata, profileType, start, end]);
70
78
  const fetchAllLabelValues = useCallback(async () => {
71
79
  const newLabelValuesMap = {};
72
80
  const newIsLoading = {};
@@ -88,14 +96,14 @@ const ViewMatchers = ({ labelNames, profileType, queryClient, runQuery, setMatch
88
96
  .filter(([_, v]) => v !== null && v !== '')
89
97
  .map(([ln, v]) => `${ln}="${v}"`);
90
98
  const matcherString = matcherParts.join(',');
91
- setMatchersString(matcherString);
99
+ setDraftMatchers(matcherString);
92
100
  if (timeoutRef.current !== null) {
93
101
  clearTimeout(timeoutRef.current);
94
102
  }
95
103
  timeoutRef.current = setTimeout(() => {
96
- runQueryRef.current();
104
+ commitDraftRef.current();
97
105
  }, 300);
98
- }, [setMatchersString]);
106
+ }, [setDraftMatchers]);
99
107
  const handleSelection = useCallback((labelName, value) => {
100
108
  selectionsRef.current = {
101
109
  ...selectionsRef.current,
@@ -0,0 +1,35 @@
1
+ import { QueryServiceClient } from '@parca/client';
2
+ import { Query } from '@parca/parser';
3
+ export interface LabelsQueryProviderContextType {
4
+ isLabelNamesLoading: boolean;
5
+ isLabelValuesLoading: boolean;
6
+ currentLabelName: string | null;
7
+ setCurrentLabelName: (name: string | null) => void;
8
+ refetchLabelValues: () => Promise<void>;
9
+ refetchLabelNames: () => Promise<void>;
10
+ labelNames: string[];
11
+ labelValues: string[];
12
+ queryClient: QueryServiceClient;
13
+ setMatchersString: (arg: string) => void;
14
+ runQuery: () => void;
15
+ currentQuery: Query;
16
+ profileType: string;
17
+ start?: number;
18
+ end?: number;
19
+ suffix?: '_a' | '_b';
20
+ }
21
+ interface LabelsQueryProviderProps {
22
+ children: React.ReactNode;
23
+ queryClient: QueryServiceClient;
24
+ setMatchersString: (arg: string) => void;
25
+ runQuery: () => void;
26
+ currentQuery: Query;
27
+ profileType: string;
28
+ start?: number;
29
+ end?: number;
30
+ suffix?: '_a' | '_b';
31
+ }
32
+ export declare function LabelsQueryProvider({ children, queryClient, setMatchersString, runQuery, currentQuery, profileType, start, end, suffix, }: LabelsQueryProviderProps): JSX.Element;
33
+ export declare function useLabelsQueryProvider(): LabelsQueryProviderContextType;
34
+ export {};
35
+ //# sourceMappingURL=LabelsQueryProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LabelsQueryProvider.d.ts","sourceRoot":"","sources":["../../src/contexts/LabelsQueryProvider.tsx"],"names":[],"mappings":"AAoCA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AACjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAKpC,MAAM,WAAW,8BAA8B;IAC7C,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACnD,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAID,UAAU,wBAAwB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B,WAAW,EAAE,kBAAkB,CAAC;IAChC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,EAAE,KAAK,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,KAAK,EACL,GAAG,EACH,MAAM,GACP,EAAE,wBAAwB,GAAG,GAAG,CAAC,OAAO,CA2CxC;AAED,wBAAgB,sBAAsB,IAAI,8BAA8B,CAMvE"}
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Copyright 2022 The Parca Authors
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ /**
15
+ * LabelsQueryProvider - Data Fetching Layer
16
+ *
17
+ * This context provider is responsible for fetching label data from the Parca API
18
+ * and making it available to child components through React Context.
19
+ *
20
+ * Purpose:
21
+ * - Fetches label names and values from the Parca profiling API
22
+ * - Manages loading states for label data
23
+ * - Provides refetch functions for manual data refresh
24
+ * - Acts as the primary data source in the label provider architecture
25
+ *
26
+ * Architecture Pattern:
27
+ * This is the first layer in a three-layer architecture:
28
+ * 1. LabelsQueryProvider (this file) - Fetches data from API
29
+ * 2. LabelsSource (in ProfileSelector) - Transforms/merges data
30
+ * 3. UnifiedLabelsProvider - Provides unified interface to UI components
31
+ *
32
+ * Consumer Hook: useLabelsQueryProvider()
33
+ */
34
+ import { createContext, useContext, useState } from 'react';
35
+ import { useLabelNames, useLabelValues } from '../hooks/useLabels';
36
+ import { useExtractedLabelNames } from './utils';
37
+ const LabelsQueryProviderContext = createContext(null);
38
+ export function LabelsQueryProvider({ children, queryClient, setMatchersString, runQuery, currentQuery, profileType, start, end, suffix, }) {
39
+ const [currentLabelName, setCurrentLabelName] = useState(null);
40
+ const { result: labelNamesResponse, loading: isLabelNamesLoading, refetch: labelNamesRefetch, } = useLabelNames(queryClient, profileType, start, end);
41
+ const labelNames = useExtractedLabelNames(labelNamesResponse.response, labelNamesResponse.error);
42
+ const { result: labelValuesOriginal, loading: isLabelValuesLoading, refetch: labelValuesRefetch, } = useLabelValues(queryClient, currentLabelName ?? '', profileType, start, end);
43
+ const labelValues = labelValuesOriginal.response;
44
+ const value = {
45
+ labelNames,
46
+ labelValues,
47
+ isLabelNamesLoading,
48
+ isLabelValuesLoading,
49
+ refetchLabelValues: labelValuesRefetch,
50
+ refetchLabelNames: labelNamesRefetch,
51
+ queryClient,
52
+ setMatchersString,
53
+ runQuery,
54
+ currentQuery,
55
+ profileType,
56
+ start,
57
+ end,
58
+ setCurrentLabelName,
59
+ currentLabelName,
60
+ suffix,
61
+ };
62
+ return (_jsx(LabelsQueryProviderContext.Provider, { value: value, children: children }));
63
+ }
64
+ export function useLabelsQueryProvider() {
65
+ const context = useContext(LabelsQueryProviderContext);
66
+ if (context === null) {
67
+ throw new Error('useLabelsQueryProvider must be used within a LabelsQueryProvider');
68
+ }
69
+ return context;
70
+ }
@@ -0,0 +1,37 @@
1
+ import type { SelectItem } from '../SimpleMatchers/Select';
2
+ import { type LabelNameMapping } from './utils';
3
+ interface LabelNameSection {
4
+ type: string;
5
+ values: SelectItem[];
6
+ }
7
+ interface UnifiedLabelsContextType {
8
+ labelNameMappingsForMatchersInput: LabelNameMapping[];
9
+ labelNameMappingsForSimpleMatchers: LabelNameSection[];
10
+ labelNames: string[];
11
+ labelValues: string[];
12
+ isLabelNamesLoading: boolean;
13
+ isLabelValuesLoading: boolean;
14
+ currentLabelName: string | null;
15
+ setCurrentLabelName: (name: string | null) => void;
16
+ shouldHandlePrefixes: boolean;
17
+ refetchLabelValues: () => Promise<void>;
18
+ refetchLabelNames: () => Promise<void>;
19
+ labelNameFromMatchers: string[];
20
+ suffix?: '_a' | '_b';
21
+ }
22
+ interface UnifiedLabelsProviderProps {
23
+ children: React.ReactNode;
24
+ currentLabelName: string | null;
25
+ setCurrentLabelName: (name: string | null) => void;
26
+ labelNames: string[];
27
+ labelValues: string[];
28
+ isLabelNamesLoading: boolean;
29
+ isLabelValuesLoading: boolean;
30
+ refetchLabelValues: () => Promise<void>;
31
+ refetchLabelNames: () => Promise<void>;
32
+ suffix?: '_a' | '_b';
33
+ }
34
+ export declare function UnifiedLabelsProvider({ children, labelNames, isLabelNamesLoading, isLabelValuesLoading, refetchLabelValues, refetchLabelNames, currentLabelName, setCurrentLabelName, labelValues, suffix, }: UnifiedLabelsProviderProps): JSX.Element;
35
+ export declare function useUnifiedLabels(): UnifiedLabelsContextType;
36
+ export {};
37
+ //# sourceMappingURL=UnifiedLabelsContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnifiedLabelsContext.d.ts","sourceRoot":"","sources":["../../src/contexts/UnifiedLabelsContext.tsx"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAuB,KAAK,gBAAgB,EAAC,MAAM,SAAS,CAAC;AAEpE,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,UAAU,wBAAwB;IAChC,iCAAiC,EAAE,gBAAgB,EAAE,CAAC;IACtD,kCAAkC,EAAE,gBAAgB,EAAE,CAAC;IACvD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACnD,oBAAoB,EAAE,OAAO,CAAC;IAC9B,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAEhC,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAID,UAAU,0BAA0B;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAEnD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,CAAC;IAE9B,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACtB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,WAAW,EACX,MAAM,GACP,EAAE,0BAA0B,GAAG,GAAG,CAAC,OAAO,CAqD1C;AAED,wBAAgB,gBAAgB,IAAI,wBAAwB,CAM3D"}
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Copyright 2022 The Parca Authors
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+ /**
15
+ * UnifiedLabelsProvider - UI Presentation Layer
16
+ *
17
+ * This context provider transforms raw label data into a format optimized for
18
+ * UI components (QueryControls, SimpleMatchers, etc.).
19
+ *
20
+ * Purpose:
21
+ * - Transforms label arrays into structured formats for dropdowns and selectors
22
+ * - Groups labels by type (e.g., 'cpu', 'gpu') for organized display
23
+ * - Handles label name prefixes and mappings for user-friendly display
24
+ * - Provides a unified interface regardless of data source(s)
25
+ *
26
+ * Architecture Pattern:
27
+ * This is the final layer in a three-layer architecture:
28
+ * 1. LabelsQueryProvider - Fetches data from API
29
+ * 2. LabelsSource (in ProfileSelector) - Transforms/merges data
30
+ * 3. UnifiedLabelsProvider (this file) - Presents data to UI components
31
+ *
32
+ * Consumer Hook: useUnifiedLabels()
33
+ */
34
+ import { createContext, useContext } from 'react';
35
+ import { transformLabelsForSelect } from '../SimpleMatchers';
36
+ import { useLabelNameMappings } from './utils';
37
+ const UnifiedLabelsContext = createContext(null);
38
+ export function UnifiedLabelsProvider({ children, labelNames, isLabelNamesLoading, isLabelValuesLoading, refetchLabelValues, refetchLabelNames, currentLabelName, setCurrentLabelName, labelValues, suffix, }) {
39
+ const labelNameFromMatchers = [];
40
+ const labelNamesFromAPI = labelNames;
41
+ const labelNameMappingsForMatchersInput = useLabelNameMappings(labelNamesFromAPI);
42
+ const allLabelNames = new Set(labelNamesFromAPI);
43
+ const nonMatchingLabels = labelNameFromMatchers.filter(label => !allLabelNames.has(label));
44
+ const labelNameMappingsForSimpleMatchers = [];
45
+ const labels = {
46
+ type: 'cpu',
47
+ labelNames: labelNamesFromAPI,
48
+ isLoading: isLabelNamesLoading,
49
+ };
50
+ labelNameMappingsForSimpleMatchers.push({
51
+ type: labels.type,
52
+ values: transformLabelsForSelect(labels.labelNames),
53
+ });
54
+ if (nonMatchingLabels.length > 0) {
55
+ const uniqueNonMatchingLabels = Array.from(new Set(nonMatchingLabels));
56
+ labelNameMappingsForSimpleMatchers.push({
57
+ type: '',
58
+ values: transformLabelsForSelect(uniqueNonMatchingLabels),
59
+ });
60
+ }
61
+ const value = {
62
+ labelNames: labelNamesFromAPI,
63
+ labelNameMappingsForMatchersInput,
64
+ isLabelNamesLoading,
65
+ isLabelValuesLoading,
66
+ currentLabelName,
67
+ labelValues,
68
+ setCurrentLabelName,
69
+ shouldHandlePrefixes: false,
70
+ refetchLabelValues: async () => {
71
+ await refetchLabelValues();
72
+ },
73
+ refetchLabelNames: async () => {
74
+ await refetchLabelNames();
75
+ },
76
+ labelNameFromMatchers,
77
+ labelNameMappingsForSimpleMatchers,
78
+ suffix,
79
+ };
80
+ return _jsx(UnifiedLabelsContext.Provider, { value: value, children: children });
81
+ }
82
+ export function useUnifiedLabels() {
83
+ const context = useContext(UnifiedLabelsContext);
84
+ if (context === null) {
85
+ throw new Error('useUnifiedLabels must be used within a UnifiedLabelsProvider');
86
+ }
87
+ return context;
88
+ }