@parca/profile 0.14.31 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,75 @@
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.15.0](https://github.com/parca-dev/parca/compare/ui-v0.14.37...ui-v0.15.0) (2022-08-24)
7
+
8
+
9
+
10
+ ## [0.14.32](https://github.com/parca-dev/parca/compare/ui-v0.14.30...ui-v0.14.32) (2022-08-18)
11
+
12
+
13
+
14
+ ## [0.14.30](https://github.com/parca-dev/parca/compare/ui-v0.14.24...ui-v0.14.30) (2022-08-17)
15
+
16
+
17
+ ### Features
18
+
19
+ * implement callgraph report api ([83c81c6](https://github.com/parca-dev/parca/commit/83c81c67a140b4b723094ee0d6c2308b112b800c))
20
+ * tooltip for callgraph ([c1d0195](https://github.com/parca-dev/parca/commit/c1d0195e45a5aa065a89c936861646f1455336ff))
21
+
22
+
23
+
24
+ ## [0.14.16](https://github.com/parca-dev/parca/compare/ui-v0.14.11...ui-v0.14.16) (2022-08-03)
25
+
26
+
27
+
28
+ ## [0.14.3-alpha.0](https://github.com/parca-dev/parca/compare/ui-v0.14.1...ui-v0.14.3-alpha.0) (2022-07-26)
29
+
30
+
31
+
32
+ ## [0.13.14](https://github.com/parca-dev/parca/compare/ui-v0.13.12...ui-v0.13.14) (2022-06-29)
33
+
34
+
35
+ ### Features
36
+
37
+ * add tooltip to callgraph ([b261a48](https://github.com/parca-dev/parca/commit/b261a48a92739d5fb957957da929495cb206417e))
38
+ * connect callgraph component to actual data ([b7dd5d8](https://github.com/parca-dev/parca/commit/b7dd5d8b0851d11010f66a3a5ca03d50902636a1))
39
+
40
+
41
+
42
+ ## [0.13.10](https://github.com/parca-dev/parca/compare/ui-v0.13.2...ui-v0.13.10) (2022-06-22)
43
+
44
+
45
+
46
+ ## [0.13.2](https://github.com/parca-dev/parca/compare/ui-v0.13.1...ui-v0.13.2) (2022-05-31)
47
+
48
+
49
+
50
+
51
+
52
+ ## [0.14.37](https://github.com/parca-dev/parca/compare/ui-v0.14.36...ui-v0.14.37) (2022-08-24)
53
+
54
+ **Note:** Version bump only for package @parca/profile
55
+
56
+
57
+
58
+
59
+
60
+ ## [0.14.36](https://github.com/parca-dev/parca/compare/ui-v0.14.35...ui-v0.14.36) (2022-08-24)
61
+
62
+
63
+ ### Bug Fixes
64
+
65
+ * **ui:** enforce @typescript-eslint/explicit-function-return-type ([22a18fd](https://github.com/parca-dev/parca/commit/22a18fd3f7befef5cc175131d8c997c3967a5713))
66
+
67
+
68
+
69
+ ## 0.14.32 (2022-08-18)
70
+
71
+
72
+
73
+
74
+
6
75
  ## [0.14.31](https://github.com/parca-dev/parca/compare/ui-v0.14.30...ui-v0.14.31) (2022-08-18)
7
76
 
8
77
 
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@parca/profile",
3
- "version": "0.14.31",
3
+ "version": "0.15.0",
4
4
  "description": "Profile viewing libraries",
5
5
  "dependencies": {
6
6
  "@iconify/react": "^3.2.2",
7
- "@parca/client": "^0.14.16",
8
- "@parca/dynamicsize": "^0.14.29",
9
- "@parca/parser": "^0.14.29",
7
+ "@parca/client": "^0.15.0",
8
+ "@parca/dynamicsize": "^0.15.0",
9
+ "@parca/functions": "^0.15.0",
10
+ "@parca/parser": "^0.15.0",
10
11
  "d3-scale": "^4.0.2",
11
12
  "d3-selection": "3.0.0",
12
13
  "react-copy-to-clipboard": "^5.1.0"
@@ -22,5 +23,5 @@
22
23
  "access": "public",
23
24
  "registry": "https://registry.npmjs.org/"
24
25
  },
25
- "gitHead": "ad5f13b18fb7c5dd70df3dc6b40477e44f839381"
26
+ "gitHead": "9bba12f8523b8d5763d9fb6d81c9df971a83f6fa"
26
27
  }
@@ -17,7 +17,7 @@ import {throttle} from 'lodash';
17
17
  import {pointer} from 'd3-selection';
18
18
  import {scaleLinear} from 'd3-scale';
19
19
  import {Flamegraph, FlamegraphNode, FlamegraphRootNode} from '@parca/client';
20
- import {FlamegraphTooltip} from '@parca/components';
20
+ import {GraphTooltip} from '@parca/components';
21
21
  import {getLastItem, diffColor, isSearchMatch} from '@parca/functions';
22
22
  import {useAppSelector, selectDarkMode, selectSearchNodeString} from '@parca/store';
23
23
 
@@ -91,7 +91,7 @@ function IcicleRect({
91
91
  onMouseLeave,
92
92
  onClick,
93
93
  curPath,
94
- }: IcicleRectProps) {
94
+ }: IcicleRectProps): JSX.Element {
95
95
  const currentSearchString = useAppSelector(selectSearchNodeString);
96
96
  const isFaded = curPath.length > 0 && name !== curPath[curPath.length - 1];
97
97
  const styles = isFaded ? fadedIcicleRectStyles : icicleRectStyles;
@@ -158,7 +158,7 @@ export function IcicleGraphNodes({
158
158
  path,
159
159
  setCurPath,
160
160
  curPath,
161
- }: IcicleGraphNodesProps) {
161
+ }: IcicleGraphNodesProps): JSX.Element {
162
162
  const isDarkMode = useAppSelector(selectDarkMode);
163
163
 
164
164
  const nodes =
@@ -189,7 +189,7 @@ export function IcicleGraphNodes({
189
189
 
190
190
  const color = diffColor(diff, cumulative, isDarkMode);
191
191
 
192
- const onClick = () => {
192
+ const onClick = (): void => {
193
193
  setCurPath(nextPath);
194
194
  };
195
195
 
@@ -199,8 +199,8 @@ export function IcicleGraphNodes({
199
199
  ? scaleLinear().domain([0, cumulative]).range([0, totalWidth])
200
200
  : xScale;
201
201
 
202
- const onMouseEnter = () => setHoveringNode(d);
203
- const onMouseLeave = () => setHoveringNode(undefined);
202
+ const onMouseEnter = (): void => setHoveringNode(d);
203
+ const onMouseLeave = (): void => setHoveringNode(undefined);
204
204
 
205
205
  return (
206
206
  <React.Fragment key={`node-${key}`}>
@@ -250,16 +250,16 @@ export function IcicleGraphRootNode({
250
250
  setHoveringNode,
251
251
  setCurPath,
252
252
  curPath,
253
- }: IcicleGraphRootNodeProps) {
253
+ }: IcicleGraphRootNodeProps): JSX.Element {
254
254
  const isDarkMode = useAppSelector(selectDarkMode);
255
255
 
256
256
  const cumulative = parseFloat(node.cumulative);
257
257
  const diff = parseFloat(node.diff);
258
258
  const color = diffColor(diff, cumulative, isDarkMode);
259
259
 
260
- const onClick = () => setCurPath([]);
261
- const onMouseEnter = () => setHoveringNode(node);
262
- const onMouseLeave = () => setHoveringNode(undefined);
260
+ const onClick = (): void => setCurPath([]);
261
+ const onMouseEnter = (): void => setHoveringNode(node);
262
+ const onMouseLeave = (): void => setHoveringNode(undefined);
263
263
  const path = [];
264
264
 
265
265
  return (
@@ -301,7 +301,7 @@ export default function IcicleGraph({
301
301
  setCurPath,
302
302
  curPath,
303
303
  sampleUnit,
304
- }: IcicleGraphProps) {
304
+ }: IcicleGraphProps): JSX.Element {
305
305
  const [hoveringNode, setHoveringNode] = useState<
306
306
  FlamegraphNode | FlamegraphRootNode | undefined
307
307
  >();
@@ -331,7 +331,7 @@ export default function IcicleGraph({
331
331
 
332
332
  return (
333
333
  <div onMouseLeave={() => setHoveringNode(undefined)}>
334
- <FlamegraphTooltip
334
+ <GraphTooltip
335
335
  unit={sampleUnit}
336
336
  total={total}
337
337
  x={pos[0]}
@@ -31,7 +31,7 @@ const ProfileIcicleGraph = ({
31
31
  curPath,
32
32
  setNewCurPath,
33
33
  sampleUnit,
34
- }: ProfileIcicleGraphProps) => {
34
+ }: ProfileIcicleGraphProps): JSX.Element => {
35
35
  const compareMode = useAppSelector(selectCompareMode);
36
36
  const {ref, dimensions} = useContainerDimensions();
37
37
 
@@ -14,8 +14,17 @@
14
14
  import React, {useEffect, useMemo, useState} from 'react';
15
15
 
16
16
  import {parseParams} from '@parca/functions';
17
- import {QueryServiceClient, Flamegraph, Top} from '@parca/client';
18
- import {Button, Card, SearchNodes, useGrpcMetadata, useParcaTheme} from '@parca/components';
17
+ import useUIFeatureFlag from '@parca/functions/useUIFeatureFlag';
18
+ import {QueryServiceClient, Flamegraph, Top, Callgraph} from '@parca/client';
19
+ import {
20
+ Button,
21
+ Card,
22
+ SearchNodes,
23
+ useGrpcMetadata,
24
+ useParcaTheme,
25
+ Callgraph as CallgraphComponent,
26
+ } from '@parca/components';
27
+ import {useContainerDimensions} from '@parca/dynamicsize';
19
28
 
20
29
  import ProfileShareButton from './components/ProfileShareButton';
21
30
  import ProfileIcicleGraph from './ProfileIcicleGraph';
@@ -40,7 +49,13 @@ interface TopTableData {
40
49
  error?: any;
41
50
  }
42
51
 
43
- type VisualizationType = 'icicle' | 'table' | 'both';
52
+ interface CallgraphData {
53
+ loading: boolean;
54
+ data?: Callgraph;
55
+ error?: any;
56
+ }
57
+
58
+ type VisualizationType = 'icicle' | 'table' | 'callgraph' | 'both';
44
59
 
45
60
  interface ProfileVisState {
46
61
  currentView: VisualizationType;
@@ -50,6 +65,7 @@ interface ProfileVisState {
50
65
  interface ProfileViewProps {
51
66
  flamegraphData?: FlamegraphData;
52
67
  topTableData?: TopTableData;
68
+ callgraphData?: CallgraphData;
53
69
  sampleUnit: string;
54
70
  profileVisState: ProfileVisState;
55
71
  profileSource?: ProfileSource;
@@ -58,7 +74,7 @@ interface ProfileViewProps {
58
74
  compare?: boolean;
59
75
  }
60
76
 
61
- function arrayEquals(a, b): boolean {
77
+ function arrayEquals<T>(a: T[], b: T[]): boolean {
62
78
  return (
63
79
  Array.isArray(a) &&
64
80
  Array.isArray(b) &&
@@ -79,15 +95,19 @@ export const useProfileVisState = (): ProfileVisState => {
79
95
  export const ProfileView = ({
80
96
  flamegraphData,
81
97
  topTableData,
98
+ callgraphData,
82
99
  sampleUnit,
83
100
  profileSource,
84
101
  queryClient,
85
102
  navigateTo,
86
103
  profileVisState,
87
104
  }: ProfileViewProps): JSX.Element => {
105
+ const {ref, dimensions} = useContainerDimensions();
88
106
  const [curPath, setCurPath] = useState<string[]>([]);
89
107
  const {currentView, setCurrentView} = profileVisState;
90
108
 
109
+ const [callgraphEnabled] = useUIFeatureFlag('callgraph');
110
+
91
111
  const metadata = useGrpcMetadata();
92
112
  const {loader} = useParcaTheme();
93
113
 
@@ -100,6 +120,9 @@ export const ProfileView = ({
100
120
  if (currentView === 'icicle') {
101
121
  return Boolean(flamegraphData?.loading);
102
122
  }
123
+ if (currentView === 'callgraph') {
124
+ return !!callgraphData?.loading;
125
+ }
103
126
  if (currentView === 'table') {
104
127
  return Boolean(topTableData?.loading);
105
128
  }
@@ -107,7 +130,7 @@ export const ProfileView = ({
107
130
  return Boolean(flamegraphData?.loading) || Boolean(topTableData?.loading);
108
131
  }
109
132
  return false;
110
- }, [currentView, flamegraphData?.loading, topTableData?.loading]);
133
+ }, [currentView, callgraphData?.loading, flamegraphData?.loading, topTableData?.loading]);
111
134
 
112
135
  const isLoaderVisible = useDelayedLoader(isLoading);
113
136
 
@@ -124,7 +147,7 @@ export const ProfileView = ({
124
147
  );
125
148
  }
126
149
 
127
- const downloadPProf = async (e: React.MouseEvent<HTMLElement>) => {
150
+ const downloadPProf = async (e: React.MouseEvent<HTMLElement>): Promise<void> => {
128
151
  e.preventDefault();
129
152
  if (profileSource == null || queryClient == null) {
130
153
  return;
@@ -141,15 +164,15 @@ export const ProfileView = ({
141
164
  }
142
165
  };
143
166
 
144
- const resetIcicleGraph = () => setCurPath([]);
167
+ const resetIcicleGraph = (): void => setCurPath([]);
145
168
 
146
- const setNewCurPath = (path: string[]) => {
169
+ const setNewCurPath = (path: string[]): void => {
147
170
  if (!arrayEquals(curPath, path)) {
148
171
  setCurPath(path);
149
172
  }
150
173
  };
151
174
 
152
- const switchProfileView = (view: VisualizationType) => {
175
+ const switchProfileView = (view: VisualizationType): void => {
153
176
  if (view == null) {
154
177
  return;
155
178
  }
@@ -196,6 +219,18 @@ export const ProfileView = ({
196
219
  </Button>
197
220
  </div>
198
221
 
222
+ {callgraphEnabled ? (
223
+ <div className="mr-3">
224
+ <Button
225
+ variant={`${currentView === 'callgraph' ? 'primary' : 'neutral'}`}
226
+ onClick={() => switchProfileView('callgraph')}
227
+ className="whitespace-nowrap text-ellipsis"
228
+ >
229
+ Call Graph
230
+ </Button>
231
+ </div>
232
+ ) : null}
233
+
199
234
  <Button
200
235
  variant={`${currentView === 'table' ? 'primary' : 'neutral'}`}
201
236
  className="items-center rounded-tr-none rounded-br-none w-auto px-8 whitespace-nowrap text-ellipsis no-outline-on-buttons"
@@ -222,7 +257,7 @@ export const ProfileView = ({
222
257
  </div>
223
258
  </div>
224
259
 
225
- <div className="flex space-x-4 justify-between">
260
+ <div ref={ref} className="flex space-x-4 justify-between w-full">
226
261
  {currentView === 'icicle' && flamegraphData?.data != null && (
227
262
  <div className="w-full">
228
263
  <ProfileIcicleGraph
@@ -234,6 +269,18 @@ export const ProfileView = ({
234
269
  </div>
235
270
  )}
236
271
 
272
+ {currentView === 'callgraph' && callgraphData?.data != null && (
273
+ <div className="w-full">
274
+ {dimensions?.width && (
275
+ <CallgraphComponent
276
+ graph={callgraphData.data}
277
+ sampleUnit={sampleUnit}
278
+ width={dimensions?.width}
279
+ />
280
+ )}
281
+ </div>
282
+ )}
283
+
237
284
  {currentView === 'table' && topTableData != null && (
238
285
  <div className="w-full">
239
286
  <TopTable data={topTableData.data} sampleUnit={sampleUnit} />
@@ -30,7 +30,7 @@ export const ProfileViewWithData = ({
30
30
  queryClient,
31
31
  profileSource,
32
32
  navigateTo,
33
- }: ProfileViewWithDataProps) => {
33
+ }: ProfileViewWithDataProps): JSX.Element => {
34
34
  const profileVisState = useProfileVisState();
35
35
  const {currentView} = profileVisState;
36
36
  const {
@@ -49,6 +49,14 @@ export const ProfileViewWithData = ({
49
49
  skip: currentView !== 'table' && currentView !== 'both',
50
50
  });
51
51
 
52
+ const {
53
+ isLoading: callgraphLoading,
54
+ response: callgraphResponse,
55
+ error: callgraphError,
56
+ } = useQuery(queryClient, profileSource, QueryRequest_ReportType.CALLGRAPH, {
57
+ skip: currentView != 'callgraph',
58
+ });
59
+
52
60
  const sampleUnit = profileSource.ProfileType().sampleUnit;
53
61
 
54
62
  return (
@@ -67,6 +75,14 @@ export const ProfileViewWithData = ({
67
75
  topTableResponse?.report.oneofKind === 'top' ? topTableResponse.report.top : undefined,
68
76
  error: topTableError,
69
77
  }}
78
+ callgraphData={{
79
+ loading: callgraphLoading,
80
+ data:
81
+ callgraphResponse?.report.oneofKind === 'callgraph'
82
+ ? callgraphResponse?.report?.callgraph
83
+ : undefined,
84
+ error: callgraphError,
85
+ }}
70
86
  profileVisState={profileVisState}
71
87
  sampleUnit={sampleUnit}
72
88
  profileSource={profileSource}
package/src/TopTable.tsx CHANGED
@@ -26,7 +26,7 @@ interface TopTableProps {
26
26
  sampleUnit: string;
27
27
  }
28
28
 
29
- const Arrow = ({direction}: {direction: string | undefined}) => {
29
+ const Arrow = ({direction}: {direction: string | undefined}): JSX.Element => {
30
30
  return (
31
31
  <svg
32
32
  className={`${direction !== undefined ? 'fill-[#161616] dark:fill-[#ffffff]' : ''}`}
@@ -41,7 +41,22 @@ const Arrow = ({direction}: {direction: string | undefined}) => {
41
41
  );
42
42
  };
43
43
 
44
- const useSortableData = (top?: Top, config = {key: 'cumulative', direction: 'desc'}) => {
44
+ const useSortableData = (
45
+ top?: Top,
46
+ config = {key: 'cumulative', direction: 'desc'}
47
+ ): {
48
+ items:
49
+ | Array<{
50
+ diff: number;
51
+ cumulative: number;
52
+ flat: number;
53
+ name: string | undefined;
54
+ meta?: TopNodeMeta | undefined;
55
+ }>
56
+ | undefined;
57
+ requestSort: (key: string) => void;
58
+ sortConfig: {key: string; direction: string} | null;
59
+ } => {
45
60
  const [sortConfig, setSortConfig] = React.useState<{key: string; direction: string} | null>(
46
61
  config
47
62
  );
@@ -74,7 +89,7 @@ const useSortableData = (top?: Top, config = {key: 'cumulative', direction: 'des
74
89
  return sortableItems;
75
90
  }, [items, sortConfig]);
76
91
 
77
- const requestSort = (key: string) => {
92
+ const requestSort = (key: string): void => {
78
93
  let direction = 'desc';
79
94
  if (sortConfig != null && sortConfig.key === key && sortConfig.direction === 'desc') {
80
95
  direction = 'asc';
@@ -112,14 +127,14 @@ export const TopTable = ({data: top, sampleUnit}: TopTableProps): JSX.Element =>
112
127
  const total = top != null ? top.list.length : 0;
113
128
  if (total === 0) return <>Profile has no samples</>;
114
129
 
115
- const getClassNamesFor = (name: string) => {
130
+ const getClassNamesFor = (name: string): string | undefined => {
116
131
  if (sortConfig == null) {
117
132
  return;
118
133
  }
119
134
  return sortConfig.key === name ? sortConfig.direction : undefined;
120
135
  };
121
136
 
122
- const addPlusSign = (num: string) => {
137
+ const addPlusSign = (num: string): string => {
123
138
  if (num.charAt(0) === '0' || num.charAt(0) === '-') {
124
139
  return num;
125
140
  }
@@ -25,7 +25,7 @@ const DiffLegendBar = ({
25
25
  }: {
26
26
  onMouseEnter: () => void;
27
27
  onMouseLeave: () => void;
28
- }) => {
28
+ }): JSX.Element => {
29
29
  const isDarkMode = useAppSelector(selectDarkMode);
30
30
 
31
31
  return (
@@ -54,7 +54,7 @@ const DiffLegendBar = ({
54
54
  );
55
55
  };
56
56
 
57
- const DiffLegend = () => {
57
+ const DiffLegend = (): JSX.Element => {
58
58
  const [showLegendTooltip, setShowLegendTooltip] = useState(false);
59
59
  const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
60
60
  const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null);
@@ -64,10 +64,10 @@ const DiffLegend = () => {
64
64
  strategy: 'absolute',
65
65
  });
66
66
 
67
- const handleMouseEnter = () => {
67
+ const handleMouseEnter = (): void => {
68
68
  setShowLegendTooltip(true);
69
69
  };
70
- const handleMouseLeave = () => {
70
+ const handleMouseLeave = (): void => {
71
71
  setShowLegendTooltip(false);
72
72
  };
73
73
 
@@ -24,10 +24,10 @@ interface Props {
24
24
 
25
25
  let timeoutHandle: ReturnType<typeof setTimeout> | null = null;
26
26
 
27
- const ResultBox = ({value, className = ''}: Props) => {
27
+ const ResultBox = ({value, className = ''}: Props): JSX.Element => {
28
28
  const [isCopied, setIsCopied] = useState<boolean>(false);
29
29
 
30
- const onCopy = () => {
30
+ const onCopy = (): void => {
31
31
  setIsCopied(true);
32
32
  (window.document?.activeElement as HTMLElement)?.blur();
33
33
  if (timeoutHandle != null) {
@@ -34,14 +34,14 @@ const ProfileShareModal = ({
34
34
  closeModal,
35
35
  queryRequest,
36
36
  queryClient,
37
- }: ProfileShareModalProps) => {
37
+ }: ProfileShareModalProps): JSX.Element => {
38
38
  const [isShared, setIsShared] = useState(false);
39
39
  const [loading, setLoading] = useState<boolean>(false);
40
40
  const [error, setError] = useState<string>('');
41
41
  const [description, setDescription] = useState<string>('');
42
42
  const [sharedLink, setSharedLink] = useState<string>('');
43
43
  const metadata = useGrpcMetadata();
44
- const isFormDataValid = () => true;
44
+ const isFormDataValid = (): boolean => true;
45
45
 
46
46
  const handleSubmit: () => Promise<void> = async () => {
47
47
  try {
@@ -60,7 +60,7 @@ const ProfileShareModal = ({
60
60
  }
61
61
  };
62
62
 
63
- const onClose = () => {
63
+ const onClose = (): void => {
64
64
  setLoading(false);
65
65
  setError('');
66
66
  setDescription('');
@@ -113,7 +113,7 @@ const ProfileShareModal = ({
113
113
  );
114
114
  };
115
115
 
116
- const ProfileShareButton = ({queryRequest, queryClient}: Props) => {
116
+ const ProfileShareButton = ({queryRequest, queryClient}: Props): JSX.Element => {
117
117
  const [isOpen, setIsOpen] = useState<boolean>(false);
118
118
 
119
119
  return (
@@ -0,0 +1,56 @@
1
+ {
2
+ "total": "4358676",
3
+ "unit": "count",
4
+ "height": 27,
5
+ "data": {
6
+ "nodes": {
7
+ "root": {
8
+ "id": "root",
9
+ "cumulative": "10",
10
+ "diff": "0"
11
+ },
12
+ "1": {
13
+ "id": "1",
14
+ "cumulative": "2",
15
+ "diff": "0"
16
+ },
17
+ "2": {
18
+ "id": "2",
19
+ "cumulative": "9",
20
+ "diff": "0"
21
+ },
22
+ "3": {
23
+ "id": "3",
24
+ "cumulative": "8"
25
+ },
26
+ "4": {
27
+ "id": "4",
28
+ "cumulative": "2"
29
+ }
30
+ },
31
+ "links": [
32
+ {
33
+ "source": "root",
34
+ "target": "1"
35
+ },
36
+ {
37
+ "source": "root",
38
+ "target": "4"
39
+ },
40
+ {
41
+ "source": "1",
42
+ "target": "2"
43
+ },
44
+ {
45
+ "source": "2",
46
+ "target": "3"
47
+ }
48
+ ],
49
+ "reversedLinks": [
50
+ {
51
+ "source": "3",
52
+ "target": "1"
53
+ }
54
+ ]
55
+ }
56
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "total": "4358676",
3
+ "unit": "count",
4
+ "height": 27,
5
+ "data": [
6
+ {
7
+ "id": "root"
8
+ },
9
+ {
10
+ "id": "child_1",
11
+ "parentIds": ["root", "loop_child"]
12
+ },
13
+ {
14
+ "id": "child_2",
15
+ "parentIds": ["root"]
16
+ },
17
+ {
18
+ "id": "grandchild_1",
19
+ "parentIds": ["child_1"]
20
+ },
21
+ {
22
+ "id": "grandchild_2",
23
+ "parentIds": ["child_1"]
24
+ },
25
+ {
26
+ "id": "loop_child",
27
+ "parentIds": ["grandchild_1"]
28
+ }
29
+ ]
30
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "total": "4358676",
3
+ "unit": "count",
4
+ "height": 27,
5
+ "root": {
6
+ "id": "root",
7
+ "cumulative": "4358676",
8
+ "diff": "0",
9
+ "children": [
10
+ {
11
+ "id": "child_1",
12
+ "cumulative": "1",
13
+ "diff": "0",
14
+ "children": [
15
+ {
16
+ "id": "child_2",
17
+ "cumulative": "1",
18
+ "diff": "0",
19
+ "children": [
20
+ {
21
+ "id": "root",
22
+ "cumulative": "4358676",
23
+ "diff": "0",
24
+ "children": [
25
+ {
26
+ "id": "child_1",
27
+ "cumulative": "1",
28
+ "diff": "0",
29
+ "children": [
30
+ {
31
+ "id": "child_2",
32
+ "cumulative": "1",
33
+ "diff": "0",
34
+ "children": [
35
+ {
36
+ "id": "child_1",
37
+ "cumulative": "1",
38
+ "diff": "0",
39
+ "children": []
40
+ }
41
+ ]
42
+ }
43
+ ]
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ }
49
+ ]
50
+ }
51
+ ]
52
+ }
53
+ }
@@ -17,7 +17,7 @@ interface DelayedLoaderOptions {
17
17
  delay?: number;
18
18
  }
19
19
 
20
- const useDelayedLoader = (isLoading = false, options?: DelayedLoaderOptions) => {
20
+ const useDelayedLoader = (isLoading = false, options?: DelayedLoaderOptions): boolean => {
21
21
  const {delay = 500} = options ?? {};
22
22
  const [isLoaderVisible, setIsLoaderVisible] = useState<boolean>(false);
23
23
  useEffect(() => {
package/src/utils.ts CHANGED
@@ -25,7 +25,7 @@ export const downloadPprof = async (
25
25
  request: QueryRequest,
26
26
  queryClient: QueryServiceClient,
27
27
  metadata: RpcMetadata
28
- ) => {
28
+ ): Promise<Blob> => {
29
29
  const req = {
30
30
  ...request,
31
31
  reportType: QueryRequest_ReportType.PPROF,