@localstack/appinspector-ui 1.0.0-dev-a1ff335f → 1.0.0-dev-37c5a6b8

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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/pages/spans-list-page.tsx
2
2
  import { Box as Box6 } from "@mui/material";
3
- import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
3
+ import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef3, useState as useState5 } from "react";
4
4
  import { useNavigate } from "react-router-dom";
5
5
 
6
6
  // src/api/use-api.tsx
@@ -3254,16 +3254,58 @@ var AppInspectorContextProvider = (props) => {
3254
3254
  ) });
3255
3255
  };
3256
3256
 
3257
+ // src/hooks/use-local-storage.tsx
3258
+ import { useCallback as useCallback3, useEffect as useEffect2, useState as useState3 } from "react";
3259
+ function useLocalStorage(key, initialValue) {
3260
+ const [storedValue, setStoredValue] = useState3(() => {
3261
+ try {
3262
+ const item = globalThis.localStorage.getItem(key);
3263
+ return item ? JSON.parse(item) : initialValue;
3264
+ } catch (error) {
3265
+ console.error(`Error reading localStorage key "${key}":`, error);
3266
+ return initialValue;
3267
+ }
3268
+ });
3269
+ const setValue = useCallback3((value) => {
3270
+ try {
3271
+ setStoredValue(value);
3272
+ globalThis.localStorage.setItem(key, JSON.stringify(value));
3273
+ globalThis.dispatchEvent(new StorageEvent("storage", {
3274
+ key,
3275
+ newValue: JSON.stringify(value)
3276
+ }));
3277
+ } catch (error) {
3278
+ console.error(`Error setting localStorage key "${key}":`, error);
3279
+ }
3280
+ }, [key]);
3281
+ useEffect2(() => {
3282
+ const handleStorageChange = (event) => {
3283
+ if (event.key === key && event.newValue !== null) {
3284
+ try {
3285
+ setStoredValue(JSON.parse(event.newValue));
3286
+ } catch (error) {
3287
+ console.error(`Error parsing localStorage key "${key}":`, error);
3288
+ }
3289
+ }
3290
+ };
3291
+ globalThis.addEventListener("storage", handleStorageChange);
3292
+ return () => {
3293
+ globalThis.removeEventListener("storage", handleStorageChange);
3294
+ };
3295
+ }, [key]);
3296
+ return [storedValue, setValue];
3297
+ }
3298
+
3257
3299
  // src/hooks/use-status.tsx
3258
- import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
3300
+ import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
3259
3301
  var useStatus = () => {
3260
3302
  const api = useAppInspectorApi();
3261
- const [checking, setChecking] = useState3(true);
3262
- const [status, setStatus] = useState3();
3263
- const [error, setError] = useState3();
3303
+ const [checking, setChecking] = useState4(true);
3304
+ const [status, setStatus] = useState4();
3305
+ const [error, setError] = useState4();
3264
3306
  const hasFetchedReference = useRef2(false);
3265
3307
  const abortControllerReference = useRef2(null);
3266
- const checkStatus = useCallback3(async () => {
3308
+ const checkStatus = useCallback4(async () => {
3267
3309
  if (abortControllerReference.current) {
3268
3310
  abortControllerReference.current.abort();
3269
3311
  }
@@ -3287,7 +3329,7 @@ var useStatus = () => {
3287
3329
  }
3288
3330
  }
3289
3331
  }, [api]);
3290
- useEffect2(() => {
3332
+ useEffect3(() => {
3291
3333
  if (!hasFetchedReference.current) {
3292
3334
  hasFetchedReference.current = true;
3293
3335
  void checkStatus();
@@ -3301,13 +3343,13 @@ import { jsx as jsx128 } from "react/jsx-runtime";
3301
3343
  var MAX_WIDTH = 1400;
3302
3344
  var SpansListPage = () => {
3303
3345
  const api = useAppInspectorApi();
3304
- const [spans, setSpans] = useState4();
3305
- const [loading, setLoading] = useState4(false);
3346
+ const [spans, setSpans] = useState5();
3347
+ const [loading, setLoading] = useState5(false);
3306
3348
  const hasFetched = useRef3(false);
3307
- const [filterWriteOperations, setFilterWriteOperations] = useState4(true);
3308
- const [fetchError, setFetchError] = useState4();
3349
+ const [filterWriteOperations, setFilterWriteOperations] = useLocalStorage("filterWriteOperations", true);
3350
+ const [fetchError, setFetchError] = useState5();
3309
3351
  const { checkStatus, error: statusError, status } = useStatus();
3310
- const fetchSpans = useCallback4(async () => {
3352
+ const fetchSpans = useCallback5(async () => {
3311
3353
  setLoading(true);
3312
3354
  try {
3313
3355
  const response = await api.getSpans({
@@ -3323,26 +3365,26 @@ var SpansListPage = () => {
3323
3365
  setLoading(false);
3324
3366
  }
3325
3367
  }, [api, filterWriteOperations]);
3326
- const fetchSpansSync = useCallback4(() => {
3368
+ const fetchSpansSync = useCallback5(() => {
3327
3369
  void fetchSpans();
3328
3370
  }, [fetchSpans]);
3329
- useEffect3(() => {
3371
+ useEffect4(() => {
3330
3372
  if (hasFetched.current) return;
3331
3373
  if (status?.status !== "ENABLED") return;
3332
3374
  hasFetched.current = true;
3333
3375
  void fetchSpans();
3334
3376
  }, [fetchSpans, status]);
3335
- useEffect3(() => {
3377
+ useEffect4(() => {
3336
3378
  if (!hasFetched.current) return;
3337
3379
  void fetchSpans();
3338
3380
  }, [fetchSpans]);
3339
3381
  const navigate = useNavigate();
3340
3382
  const { resolveLink } = useAppInspector();
3341
- const navigateToSpan = useCallback4((span) => {
3383
+ const navigateToSpan = useCallback5((span) => {
3342
3384
  const url = resolveLink({ to: "trace-graph", traceId: span.trace_id });
3343
3385
  navigate(`${url}?spanId=${span.span_id}`);
3344
3386
  }, [navigate, resolveLink]);
3345
- const clearSpans = useCallback4(async () => {
3387
+ const clearSpans = useCallback5(async () => {
3346
3388
  setLoading(true);
3347
3389
  try {
3348
3390
  await api.deleteSpans();
@@ -3356,7 +3398,7 @@ var SpansListPage = () => {
3356
3398
  setLoading(false);
3357
3399
  }
3358
3400
  }, [api, fetchSpans]);
3359
- const handleRetry = useCallback4(() => {
3401
+ const handleRetry = useCallback5(() => {
3360
3402
  void checkStatus();
3361
3403
  }, [checkStatus]);
3362
3404
  const shouldShowStatusMessage = Boolean(statusError) || status?.status === "DISABLED";
@@ -3417,11 +3459,11 @@ var SpansListPage = () => {
3417
3459
  );
3418
3460
  };
3419
3461
 
3420
- // src/pages/trace-graph.tsx
3462
+ // src/pages/trace-graph-page.tsx
3421
3463
  import { Box as Box12, Drawer, Paper as Paper2 } from "@mui/material";
3422
3464
  import { styled } from "@mui/material/styles";
3423
3465
  import { ReactFlowProvider } from "@xyflow/react";
3424
- import { useCallback as useCallback7, useEffect as useEffect5, useState as useState7 } from "react";
3466
+ import { useCallback as useCallback8, useEffect as useEffect6, useState as useState8 } from "react";
3425
3467
  import { useParams, useSearchParams } from "react-router-dom";
3426
3468
 
3427
3469
  // src/components/event-details/event-details.tsx
@@ -3462,10 +3504,10 @@ var EventDetailsHeader = ({ event, onClose }) => {
3462
3504
  // src/components/event-details/toggle-section.tsx
3463
3505
  import { KeyboardArrowDown, KeyboardArrowRight } from "@mui/icons-material";
3464
3506
  import { Box as Box8, Typography as Typography5 } from "@mui/material";
3465
- import { useState as useState5 } from "react";
3507
+ import { useState as useState6 } from "react";
3466
3508
  import { jsx as jsx130, jsxs as jsxs124 } from "react/jsx-runtime";
3467
3509
  var ToggleSection = ({ children, headline, initialOpen = true }) => {
3468
- const [isOpen, setIsOpen] = useState5(initialOpen);
3510
+ const [isOpen, setIsOpen] = useState6(initialOpen);
3469
3511
  return /* @__PURE__ */ jsxs124(Box8, { sx: { mt: 4 }, children: [
3470
3512
  /* @__PURE__ */ jsx130(
3471
3513
  Box8,
@@ -3575,13 +3617,13 @@ var EventDetails = ({ selectedEvent }) => {
3575
3617
  import "@xyflow/react/dist/base.css";
3576
3618
  import { Alert as Alert3, Button as Button4 } from "@mui/material";
3577
3619
  import { Background, Controls, ReactFlow, useReactFlow, useStoreApi } from "@xyflow/react";
3578
- import { useCallback as useCallback6, useEffect as useEffect4, useState as useState6 } from "react";
3620
+ import { useCallback as useCallback7, useEffect as useEffect5, useState as useState7 } from "react";
3579
3621
 
3580
3622
  // src/components/trace-graph/service-node.tsx
3581
3623
  import { Box as Box11, Stack as Stack2, Typography as Typography7 } from "@mui/material";
3582
3624
  import "@xyflow/react/dist/style.css";
3583
3625
  import { Handle, Position } from "@xyflow/react";
3584
- import { memo, useCallback as useCallback5 } from "react";
3626
+ import { memo, useCallback as useCallback6 } from "react";
3585
3627
 
3586
3628
  // src/components/trace-graph/problem-indicator.tsx
3587
3629
  import { Error as ErrorIcon, Info, Warning } from "@mui/icons-material";
@@ -3642,10 +3684,10 @@ var handleStyle = {
3642
3684
  opacity: 0
3643
3685
  };
3644
3686
  var ServiceNode = memo(function ServiceNode2({ data, selected }) {
3645
- const handleClick = useCallback5(() => {
3687
+ const handleClick = useCallback6(() => {
3646
3688
  data.onEventSelect(data.event);
3647
3689
  }, [data]);
3648
- const handleKeyPress = useCallback5(
3690
+ const handleKeyPress = useCallback6(
3649
3691
  (event) => {
3650
3692
  if (event.key === "Enter" || event.key === " ") {
3651
3693
  handleClick();
@@ -3838,18 +3880,16 @@ var nodeTypes = {
3838
3880
  var TraceGraph = ({
3839
3881
  initialFocusEventId,
3840
3882
  onEventSelect,
3841
- traceId,
3883
+ onRefresh,
3884
+ spans,
3842
3885
  viewPortPixelWidth
3843
3886
  }) => {
3844
- const [nodes, setNodes] = useState6([]);
3845
- const [edges, setEdges] = useState6([]);
3846
- const [isLoading, setIsLoading] = useState6(true);
3847
- const [initialViewSet, setInitialViewSet] = useState6(false);
3887
+ const [nodes, setNodes] = useState7([]);
3888
+ const [edges, setEdges] = useState7([]);
3889
+ const [initialViewSet, setInitialViewSet] = useState7(false);
3848
3890
  const { fitView, setCenter } = useReactFlow();
3849
3891
  const store = useStoreApi();
3850
- const api = useAppInspectorApi();
3851
- const [fetchError, setFetchError] = useState6();
3852
- const updateSelectedNode = useCallback6(
3892
+ const updateSelectedNode = useCallback7(
3853
3893
  (nodeId) => {
3854
3894
  const { addSelectedNodes } = store.getState();
3855
3895
  if (nodeId) {
@@ -3861,57 +3901,40 @@ var TraceGraph = ({
3861
3901
  },
3862
3902
  [nodes, store]
3863
3903
  );
3864
- const fetchTrace = useCallback6(async () => {
3865
- setIsLoading(true);
3866
- try {
3867
- const response = await api.getSpans({ trace_id: traceId });
3868
- const spansMap = /* @__PURE__ */ new Map();
3869
- const rootSpans = [];
3870
- for (const span of response.spans) {
3871
- spansMap.set(span.span_id, { ...span, children: [] });
3872
- }
3873
- for (const span of response.spans) {
3874
- const currentSpan = spansMap.get(span.span_id);
3875
- if (!currentSpan) continue;
3876
- if (span.parent_span_id && spansMap.has(span.parent_span_id)) {
3877
- const parentSpan = spansMap.get(span.parent_span_id);
3878
- if (parentSpan) {
3879
- parentSpan.children ??= [];
3880
- parentSpan.children.push(currentSpan);
3881
- }
3882
- } else {
3883
- rootSpans.push(currentSpan);
3904
+ useEffect5(() => {
3905
+ const spansMap = /* @__PURE__ */ new Map();
3906
+ const rootSpans = [];
3907
+ for (const span of spans.data ?? []) {
3908
+ spansMap.set(span.span_id, { ...span, children: [] });
3909
+ }
3910
+ for (const span of spans.data ?? []) {
3911
+ const currentSpan = spansMap.get(span.span_id);
3912
+ if (!currentSpan) continue;
3913
+ if (span.parent_span_id && spansMap.has(span.parent_span_id)) {
3914
+ const parentSpan = spansMap.get(span.parent_span_id);
3915
+ if (parentSpan) {
3916
+ parentSpan.children ??= [];
3917
+ parentSpan.children.push(currentSpan);
3884
3918
  }
3919
+ } else {
3920
+ rootSpans.push(currentSpan);
3885
3921
  }
3886
- let allNodes = [];
3887
- let allEdges = [];
3888
- for (const rootSpan of rootSpans) {
3889
- const { edges: newEdges, nodes: newNodes } = generateNodesAndEdges(rootSpan, (event) => {
3890
- onEventSelect(event);
3891
- });
3892
- allNodes = [...allNodes, ...newNodes];
3893
- allEdges = [...allEdges, ...newEdges];
3894
- }
3895
- const { edges: layoutedEdges, nodes: layoutedNodes } = getLayoutedNodesAndEdges(allNodes, allEdges);
3896
- setNodes(layoutedNodes);
3897
- setEdges(layoutedEdges);
3898
- setIsLoading(false);
3899
- setFetchError(void 0);
3900
- } catch (error) {
3901
- console.error("Error fetching trace:", error);
3902
- setFetchError(error instanceof Error ? error : new Error("Unknown error", { cause: error }));
3903
- } finally {
3904
- setIsLoading(false);
3905
3922
  }
3906
- }, [api, traceId, onEventSelect]);
3907
- const fetchTraceSync = useCallback6(() => {
3908
- void fetchTrace();
3909
- }, [fetchTrace]);
3910
- useEffect4(() => {
3911
- fetchTraceSync();
3912
- }, [fetchTraceSync]);
3913
- useEffect4(() => {
3914
- const shouldSetInitialView = !isLoading && nodes.length > 0 && !initialViewSet;
3923
+ let allNodes = [];
3924
+ let allEdges = [];
3925
+ for (const rootSpan of rootSpans) {
3926
+ const { edges: newEdges, nodes: newNodes } = generateNodesAndEdges(rootSpan, (event) => {
3927
+ onEventSelect(event);
3928
+ });
3929
+ allNodes = [...allNodes, ...newNodes];
3930
+ allEdges = [...allEdges, ...newEdges];
3931
+ }
3932
+ const { edges: layoutedEdges, nodes: layoutedNodes } = getLayoutedNodesAndEdges(allNodes, allEdges);
3933
+ setNodes(layoutedNodes);
3934
+ setEdges(layoutedEdges);
3935
+ }, [onEventSelect, spans.data]);
3936
+ useEffect5(() => {
3937
+ const shouldSetInitialView = !spans.loading && nodes.length > 0 && !initialViewSet;
3915
3938
  if (shouldSetInitialView) {
3916
3939
  if (initialFocusEventId) {
3917
3940
  const focusNode = nodes.find((node) => node.id === initialFocusEventId);
@@ -3929,26 +3952,28 @@ var TraceGraph = ({
3929
3952
  }
3930
3953
  setInitialViewSet(true);
3931
3954
  }
3932
- }, [isLoading, nodes, initialFocusEventId, viewPortPixelWidth, setCenter, fitView, onEventSelect, initialViewSet]);
3933
- useEffect4(() => {
3955
+ }, [spans.loading, nodes, initialFocusEventId, viewPortPixelWidth, setCenter, fitView, onEventSelect, initialViewSet]);
3956
+ useEffect5(() => {
3934
3957
  if (initialFocusEventId) {
3935
3958
  updateSelectedNode(initialFocusEventId);
3936
3959
  }
3937
3960
  }, [initialFocusEventId, updateSelectedNode]);
3938
- const onNodeClick = useCallback6((_event, node) => {
3961
+ const onNodeClick = useCallback7((_event, node) => {
3939
3962
  onEventSelect(node.data.event);
3940
3963
  updateSelectedNode(node.id);
3941
3964
  }, [onEventSelect, updateSelectedNode]);
3942
3965
  return /* @__PURE__ */ jsxs127(Fragment3, { children: [
3943
- fetchError && /* @__PURE__ */ jsxs127(
3966
+ spans.error && /* @__PURE__ */ jsxs127(
3944
3967
  Alert3,
3945
3968
  {
3946
- action: /* @__PURE__ */ jsx134(Button4, { onClick: fetchTraceSync, children: "Retry" }),
3969
+ action: /* @__PURE__ */ jsx134(Button4, { onClick: () => {
3970
+ onRefresh();
3971
+ }, children: "Retry" }),
3947
3972
  severity: "error",
3948
3973
  children: [
3949
3974
  "Unexpected error:",
3950
3975
  " ",
3951
- fetchError.message
3976
+ spans.error.message
3952
3977
  ]
3953
3978
  }
3954
3979
  ),
@@ -3974,7 +3999,7 @@ var TraceGraph = ({
3974
3999
  ] });
3975
4000
  };
3976
4001
 
3977
- // src/pages/trace-graph.tsx
4002
+ // src/pages/trace-graph-page.tsx
3978
4003
  import { Fragment as Fragment4, jsx as jsx135, jsxs as jsxs128 } from "react/jsx-runtime";
3979
4004
  var HEADER_HEIGHT = 56;
3980
4005
  var DRAWER_WIDTH = "35%";
@@ -3995,7 +4020,6 @@ var Main = styled("main", { shouldForwardProp: (property) => property !== "open"
3995
4020
  }));
3996
4021
  var StyledDrawer = styled(Drawer)(() => ({
3997
4022
  "& .MuiDrawer-paper": {
3998
- backgroundColor: "#ffffff",
3999
4023
  bottom: 0,
4000
4024
  boxSizing: "border-box",
4001
4025
  height: `calc(100% - ${HEADER_HEIGHT.toString()}px)`,
@@ -4011,11 +4035,33 @@ var TraceGraphPage = () => {
4011
4035
  const { traceId } = useParams();
4012
4036
  const [searchParameters] = useSearchParams();
4013
4037
  const spanId = searchParameters.get("spanId");
4014
- const [open, setOpen] = useState7(true);
4015
- const [selectedEvent, setSelectedEvent] = useState7();
4016
- const [viewPortPixelWidth, setViewPortPixelWidth] = useState7(0);
4038
+ const api = useAppInspectorApi();
4039
+ const [spans, setSpans] = useState8();
4040
+ const [loading, setLoading] = useState8(false);
4041
+ const [fetchError, setFetchError] = useState8();
4042
+ const fetchSpans = useCallback8(async () => {
4043
+ setLoading(true);
4044
+ try {
4045
+ const response = await api.getSpans({
4046
+ trace_id: traceId
4047
+ });
4048
+ setSpans(response.spans);
4049
+ setFetchError(void 0);
4050
+ } catch (error) {
4051
+ console.error(error);
4052
+ setFetchError(error instanceof Error ? error : new Error("Unknown error", { cause: error }));
4053
+ } finally {
4054
+ setLoading(false);
4055
+ }
4056
+ }, [api, traceId]);
4057
+ useEffect6(() => {
4058
+ void fetchSpans();
4059
+ }, [fetchSpans]);
4060
+ const [open, setOpen] = useState8(true);
4061
+ const [selectedEvent, setSelectedEvent] = useState8();
4062
+ const [viewPortPixelWidth, setViewPortPixelWidth] = useState8(0);
4017
4063
  const { checkStatus, error: statusError, status } = useStatus();
4018
- const calculateDrawerWidth = useCallback7(() => {
4064
+ const calculateDrawerWidth = useCallback8(() => {
4019
4065
  const percentageMatch = /(\d+)%/.exec(DRAWER_WIDTH);
4020
4066
  if (percentageMatch && open) {
4021
4067
  const percentage = Number.parseInt(percentageMatch[1], 10);
@@ -4023,7 +4069,7 @@ var TraceGraphPage = () => {
4023
4069
  }
4024
4070
  return 0;
4025
4071
  }, [open]);
4026
- useEffect5(() => {
4072
+ useEffect6(() => {
4027
4073
  const updateViewportWidth = () => {
4028
4074
  const totalWidth = window.innerWidth;
4029
4075
  const drawerPixelWidth = open ? calculateDrawerWidth() : 0;
@@ -4037,11 +4083,11 @@ var TraceGraphPage = () => {
4037
4083
  clearTimeout(timeoutId);
4038
4084
  };
4039
4085
  }, [open, calculateDrawerWidth]);
4040
- const handleEventSelect = useCallback7((event) => {
4086
+ const handleEventSelect = useCallback8((event) => {
4041
4087
  setSelectedEvent(event);
4042
4088
  setOpen(true);
4043
4089
  }, []);
4044
- const handleRetry = useCallback7(() => {
4090
+ const handleRetry = useCallback8(() => {
4045
4091
  void checkStatus();
4046
4092
  }, [checkStatus]);
4047
4093
  const shouldShowStatusMessage = Boolean(statusError) || status?.status === "DISABLED";
@@ -4086,7 +4132,14 @@ var TraceGraphPage = () => {
4086
4132
  {
4087
4133
  initialFocusEventId: spanId,
4088
4134
  onEventSelect: handleEventSelect,
4089
- traceId,
4135
+ onRefresh: () => {
4136
+ void fetchSpans();
4137
+ },
4138
+ spans: {
4139
+ data: spans,
4140
+ error: fetchError,
4141
+ loading
4142
+ },
4090
4143
  viewPortPixelWidth
4091
4144
  }
4092
4145
  ) })