@parca/profile 0.16.460 → 0.16.461

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 CHANGED
@@ -3,6 +3,10 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.16.461](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.460...@parca/profile@0.16.461) (2025-01-13)
7
+
8
+ **Note:** Version bump only for package @parca/profile
9
+
6
10
  ## [0.16.460](https://github.com/parca-dev/parca/compare/@parca/profile@0.16.459...@parca/profile@0.16.460) (2025-01-09)
7
11
 
8
12
  **Note:** Version bump only for package @parca/profile
@@ -16,6 +16,14 @@ interface UseLabelNames {
16
16
  loading: boolean;
17
17
  }
18
18
  export declare const useLabelNames: (client: QueryServiceClient, profileType: string, start?: number, end?: number, match?: string[]) => UseLabelNames;
19
+ interface UseLabelValues {
20
+ result: {
21
+ response: string[];
22
+ error?: Error;
23
+ };
24
+ loading: boolean;
25
+ }
26
+ export declare const useLabelValues: (client: QueryServiceClient, labelName: string, profileType: string) => UseLabelValues;
19
27
  declare const MatchersInput: ({ queryClient, setMatchersString, runQuery, currentQuery, profileType, }: MatchersInputProps) => JSX.Element;
20
28
  export default MatchersInput;
21
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/MatchersInput/index.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAgB,cAAc,EAAE,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEhF,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAMpC,UAAU,kBAAkB;IAC1B,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;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,aAAa;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,aAAa,WAChB,kBAAkB,eACb,MAAM,UACX,MAAM,QACR,MAAM,UACJ,MAAM,EAAE,KACf,aAyBF,CAAC;AAEF,QAAA,MAAM,aAAa,6EAMhB,kBAAkB,KAAG,GAAG,CAAC,OAoL3B,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/MatchersInput/index.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAgB,cAAc,EAAE,kBAAkB,EAAgB,MAAM,eAAe,CAAC;AAE/F,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAMpC,UAAU,kBAAkB;IAC1B,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;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,UAAU,aAAa;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,aAAa,WAChB,kBAAkB,eACb,MAAM,UACX,MAAM,QACR,MAAM,UACJ,MAAM,EAAE,KACf,aAyBF,CAAC;AAEF,UAAU,cAAc;IACtB,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,EAAE,KAAK,CAAC;KACf,CAAC;IACF,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,cAAc,WACjB,kBAAkB,aACf,MAAM,eACJ,MAAM,KAClB,cAsBF,CAAC;AAEF,QAAA,MAAM,aAAa,6EAMhB,kBAAkB,KAAG,GAAG,CAAC,OAkK3B,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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 { useEffect, useMemo, useRef, useState } from 'react';
14
+ import { useMemo, useRef, useState } from 'react';
15
15
  import cx from 'classnames';
16
16
  import TextareaAutosize from 'react-textarea-autosize';
17
17
  import { useGrpcMetadata } from '@parca/components';
@@ -43,30 +43,34 @@ export const useLabelNames = (client, profileType, start, end, match) => {
43
43
  });
44
44
  return { result: { response: data, error: error }, loading: isLoading };
45
45
  };
46
+ export const useLabelValues = (client, labelName, profileType) => {
47
+ const metadata = useGrpcMetadata();
48
+ const { data, isLoading, error } = useGrpcQuery({
49
+ key: ['labelValues', labelName, profileType],
50
+ queryFn: async () => {
51
+ const request = { labelName, match: [], profileType };
52
+ const { response } = await client.values(request, { meta: metadata });
53
+ return sanitizeLabelValue(response.labelValues);
54
+ },
55
+ options: {
56
+ enabled: profileType !== undefined &&
57
+ profileType !== '' &&
58
+ labelName !== undefined &&
59
+ labelName !== '',
60
+ staleTime: 1000 * 60 * 5, // 5 minutes
61
+ keepPreviousData: false,
62
+ },
63
+ });
64
+ return { result: { response: data ?? [], error: error }, loading: isLoading };
65
+ };
46
66
  const MatchersInput = ({ queryClient, setMatchersString, runQuery, currentQuery, profileType, }) => {
47
67
  const inputRef = useRef(null);
48
68
  const [focusedInput, setFocusedInput] = useState(false);
49
- const [labelValuesLoading, setLabelValuesLoading] = useState(false);
50
69
  const [lastCompleted, setLastCompleted] = useState(new Suggestion('', '', ''));
51
- const [labelValues, setLabelValues] = useState(null);
52
70
  const [currentLabelName, setCurrentLabelName] = useState(null);
53
- const metadata = useGrpcMetadata();
54
71
  const { loading: labelNamesLoading, result } = useLabelNames(queryClient, profileType);
55
72
  const { response: labelNamesResponse, error: labelNamesError } = result;
56
- useEffect(() => {
57
- if (currentLabelName !== null) {
58
- const call = queryClient.values({ labelName: currentLabelName, match: [], profileType }, { meta: metadata });
59
- setLabelValuesLoading(true);
60
- call.response
61
- .then(response => {
62
- // replace single `\` in the `labelValues` string with doubles `\\` if available.
63
- const newValues = sanitizeLabelValue(response.labelValues);
64
- return setLabelValues(newValues);
65
- })
66
- .catch(() => setLabelValues(null))
67
- .finally(() => setLabelValuesLoading(false));
68
- }
69
- }, [currentLabelName, metadata, profileType, queryClient]);
73
+ const { loading: labelValuesLoading, result: { response: labelValues }, } = useLabelValues(queryClient, currentLabelName ?? '', profileType);
70
74
  const labelNames = useMemo(() => {
71
75
  return (labelNamesError === undefined || labelNamesError == null) &&
72
76
  labelNamesResponse !== undefined &&
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SimpleMatchers/index.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AAMpC,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;CAClD;AAgED,QAAA,MAAM,cAAc,oFAOjB,KAAK,KAAG,GAAG,CAAC,OAiQd,CAAC;AAEF,eAAe,cAAc,CAAC"}
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,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;CAClD;AAgED,QAAA,MAAM,cAAc,oFAOjB,KAAK,KAAG,GAAG,CAAC,OA8Qd,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -13,6 +13,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
13
13
  // limitations under the License.
14
14
  import { useCallback, useEffect, useMemo, useState } from 'react';
15
15
  import { Icon } from '@iconify/react';
16
+ import { useQueryClient } from '@tanstack/react-query';
16
17
  import cx from 'classnames';
17
18
  import { useGrpcMetadata } from '@parca/components';
18
19
  import { sanitizeLabelValue } from '@parca/utilities';
@@ -60,6 +61,7 @@ currentQuery, profileType, queryBrowserRef, }) => {
60
61
  const [queryRows, setQueryRows] = useState([
61
62
  { labelName: '', operator: '=', labelValue: '', labelValues: [], isLoading: false },
62
63
  ]);
64
+ const reactQueryClient = useQueryClient();
63
65
  const metadata = useGrpcMetadata();
64
66
  const { loading: labelNamesLoading, result } = useLabelNames(queryClient, profileType);
65
67
  const { response: labelNamesResponse, error: labelNamesError } = result;
@@ -75,16 +77,24 @@ currentQuery, profileType, queryBrowserRef, }) => {
75
77
  return matchers.map(matcher => matcher.key);
76
78
  }, [currentQuery]);
77
79
  const fetchLabelValues = useCallback(async (labelName) => {
80
+ if (labelName == null || labelName === '' || profileType == null || profileType === '') {
81
+ return [];
82
+ }
78
83
  try {
79
- const response = await queryClient.values({ labelName, match: [], profileType }, { meta: metadata }).response;
80
- const sanitizedValues = sanitizeLabelValue(response.labelValues);
81
- return sanitizedValues;
84
+ const values = await reactQueryClient.fetchQuery([labelName, profileType], async () => {
85
+ const response = await queryClient.values({ labelName, match: [], profileType }, { meta: metadata }).response;
86
+ const sanitizedValues = sanitizeLabelValue(response.labelValues);
87
+ return sanitizedValues;
88
+ }, {
89
+ staleTime: 1000 * 60 * 5, // 5 minutes
90
+ });
91
+ return values;
82
92
  }
83
93
  catch (error) {
84
94
  console.error('Error fetching label values:', error);
85
95
  return [];
86
96
  }
87
- }, [queryClient, metadata, profileType]);
97
+ }, [queryClient, metadata, profileType, reactQueryClient]);
88
98
  const updateMatchersString = useCallback((rows) => {
89
99
  const matcherString = rows
90
100
  .filter(row => row.labelName.length > 0 && row.labelValue)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.16.460",
3
+ "version": "0.16.461",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@headlessui/react": "^1.7.19",
@@ -74,5 +74,5 @@
74
74
  "access": "public",
75
75
  "registry": "https://registry.npmjs.org/"
76
76
  },
77
- "gitHead": "801d740c8340a5ca9e9ed638c24c8c805b50c94b"
77
+ "gitHead": "e004a5ef188bda51bfef2f4a4d2b488b3990d533"
78
78
  }
@@ -11,12 +11,12 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
- import React, {useEffect, useMemo, useRef, useState} from 'react';
14
+ import React, {useMemo, useRef, useState} from 'react';
15
15
 
16
16
  import cx from 'classnames';
17
17
  import TextareaAutosize from 'react-textarea-autosize';
18
18
 
19
- import {LabelsRequest, LabelsResponse, QueryServiceClient} from '@parca/client';
19
+ import {LabelsRequest, LabelsResponse, QueryServiceClient, ValuesRequest} from '@parca/client';
20
20
  import {useGrpcMetadata} from '@parca/components';
21
21
  import {Query} from '@parca/parser';
22
22
  import {millisToProtoTimestamp, sanitizeLabelValue} from '@parca/utilities';
@@ -75,6 +75,42 @@ export const useLabelNames = (
75
75
  return {result: {response: data, error: error as Error}, loading: isLoading};
76
76
  };
77
77
 
78
+ interface UseLabelValues {
79
+ result: {
80
+ response: string[];
81
+ error?: Error;
82
+ };
83
+ loading: boolean;
84
+ }
85
+
86
+ export const useLabelValues = (
87
+ client: QueryServiceClient,
88
+ labelName: string,
89
+ profileType: string
90
+ ): UseLabelValues => {
91
+ const metadata = useGrpcMetadata();
92
+
93
+ const {data, isLoading, error} = useGrpcQuery<string[]>({
94
+ key: ['labelValues', labelName, profileType],
95
+ queryFn: async () => {
96
+ const request: ValuesRequest = {labelName, match: [], profileType};
97
+ const {response} = await client.values(request, {meta: metadata});
98
+ return sanitizeLabelValue(response.labelValues);
99
+ },
100
+ options: {
101
+ enabled:
102
+ profileType !== undefined &&
103
+ profileType !== '' &&
104
+ labelName !== undefined &&
105
+ labelName !== '',
106
+ staleTime: 1000 * 60 * 5, // 5 minutes
107
+ keepPreviousData: false,
108
+ },
109
+ });
110
+
111
+ return {result: {response: data ?? [], error: error as Error}, loading: isLoading};
112
+ };
113
+
78
114
  const MatchersInput = ({
79
115
  queryClient,
80
116
  setMatchersString,
@@ -84,34 +120,16 @@ const MatchersInput = ({
84
120
  }: MatchersInputProps): JSX.Element => {
85
121
  const inputRef = useRef<HTMLTextAreaElement | null>(null);
86
122
  const [focusedInput, setFocusedInput] = useState(false);
87
- const [labelValuesLoading, setLabelValuesLoading] = useState(false);
88
123
  const [lastCompleted, setLastCompleted] = useState<Suggestion>(new Suggestion('', '', ''));
89
- const [labelValues, setLabelValues] = useState<string[] | null>(null);
90
124
  const [currentLabelName, setCurrentLabelName] = useState<string | null>(null);
91
- const metadata = useGrpcMetadata();
92
125
 
93
126
  const {loading: labelNamesLoading, result} = useLabelNames(queryClient, profileType);
94
127
  const {response: labelNamesResponse, error: labelNamesError} = result;
95
128
 
96
- useEffect(() => {
97
- if (currentLabelName !== null) {
98
- const call = queryClient.values(
99
- {labelName: currentLabelName, match: [], profileType},
100
- {meta: metadata}
101
- );
102
- setLabelValuesLoading(true);
103
-
104
- call.response
105
- .then(response => {
106
- // replace single `\` in the `labelValues` string with doubles `\\` if available.
107
- const newValues = sanitizeLabelValue(response.labelValues);
108
-
109
- return setLabelValues(newValues);
110
- })
111
- .catch(() => setLabelValues(null))
112
- .finally(() => setLabelValuesLoading(false));
113
- }
114
- }, [currentLabelName, metadata, profileType, queryClient]);
129
+ const {
130
+ loading: labelValuesLoading,
131
+ result: {response: labelValues},
132
+ } = useLabelValues(queryClient, currentLabelName ?? '', profileType);
115
133
 
116
134
  const labelNames = useMemo(() => {
117
135
  return (labelNamesError === undefined || labelNamesError == null) &&
@@ -14,6 +14,7 @@
14
14
  import {useCallback, useEffect, useMemo, useState} from 'react';
15
15
 
16
16
  import {Icon} from '@iconify/react';
17
+ import {useQueryClient} from '@tanstack/react-query';
17
18
  import cx from 'classnames';
18
19
 
19
20
  import {QueryServiceClient} from '@parca/client';
@@ -106,6 +107,7 @@ const SimpleMatchers = ({
106
107
  const [queryRows, setQueryRows] = useState<QueryRow[]>([
107
108
  {labelName: '', operator: '=', labelValue: '', labelValues: [], isLoading: false},
108
109
  ]);
110
+ const reactQueryClient = useQueryClient();
109
111
  const metadata = useGrpcMetadata();
110
112
 
111
113
  const {loading: labelNamesLoading, result} = useLabelNames(queryClient, profileType);
@@ -129,19 +131,31 @@ const SimpleMatchers = ({
129
131
 
130
132
  const fetchLabelValues = useCallback(
131
133
  async (labelName: string): Promise<string[]> => {
134
+ if (labelName == null || labelName === '' || profileType == null || profileType === '') {
135
+ return [];
136
+ }
132
137
  try {
133
- const response = await queryClient.values(
134
- {labelName, match: [], profileType},
135
- {meta: metadata}
136
- ).response;
137
- const sanitizedValues = sanitizeLabelValue(response.labelValues);
138
- return sanitizedValues;
138
+ const values = await reactQueryClient.fetchQuery(
139
+ [labelName, profileType],
140
+ async () => {
141
+ const response = await queryClient.values(
142
+ {labelName, match: [], profileType},
143
+ {meta: metadata}
144
+ ).response;
145
+ const sanitizedValues = sanitizeLabelValue(response.labelValues);
146
+ return sanitizedValues;
147
+ },
148
+ {
149
+ staleTime: 1000 * 60 * 5, // 5 minutes
150
+ }
151
+ );
152
+ return values;
139
153
  } catch (error) {
140
154
  console.error('Error fetching label values:', error);
141
155
  return [];
142
156
  }
143
157
  },
144
- [queryClient, metadata, profileType]
158
+ [queryClient, metadata, profileType, reactQueryClient]
145
159
  );
146
160
 
147
161
  const updateMatchersString = useCallback(