@localstack/appinspector-ui 1.0.185 → 1.0.186

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
@@ -2422,7 +2422,7 @@ var init_event_streams = __esm({
2422
2422
 
2423
2423
  // src/pages/spans-list-page.tsx
2424
2424
  import { Box as Box22, Paper as Paper2 } from "@mui/material";
2425
- import { useCallback as useCallback12, useEffect as useEffect22, useMemo as useMemo14, useRef as useRef19, useState as useState22 } from "react";
2425
+ import { useCallback as useCallback12, useEffect as useEffect22, useMemo as useMemo13, useRef as useRef19, useState as useState22 } from "react";
2426
2426
 
2427
2427
  // src/api/errors.ts
2428
2428
  var AppInspectorDisabledError = class extends Error {
@@ -13116,7 +13116,7 @@ var ResponsiveTimestampCell = memo3(({ date }) => {
13116
13116
 
13117
13117
  // src/dev-settings.tsx
13118
13118
  import * as React5 from "react";
13119
- var DEFAULT_DEV_SETTINGS = { requestTerms: "on", waterfallIssueBackgrounds: "on", waterfallLayout: "tree", waterfallStatusIcons: "first column" };
13119
+ var DEFAULT_DEV_SETTINGS = { requestTerms: "on", warningIcon: "iam shield", waterfallIssueBackgrounds: "on", waterfallLayout: "tree", waterfallStatusIcons: "first column" };
13120
13120
  var DevSettingsContext = React5.createContext(DEFAULT_DEV_SETTINGS);
13121
13121
  var useDevSettings = () => React5.useContext(DevSettingsContext);
13122
13122
 
@@ -15368,22 +15368,6 @@ ServiceCell.displayName = "ServiceCell";
15368
15368
  import { Cancel as Cancel2, Error as Error2, GppBad } from "@mui/icons-material";
15369
15369
  import { Box as Box3, Divider, Tooltip as Tooltip3, Typography as Typography3 } from "@mui/material";
15370
15370
 
15371
- // src/types/error-levels.ts
15372
- var ErrorLevel = {
15373
- ERROR: 3,
15374
- // General error
15375
- IAM_PERMISSION: 5,
15376
- // IAM permission issues
15377
- OK: 2,
15378
- // OK
15379
- // Standard OTEL status codes
15380
- UNSET: 1,
15381
- // Unset
15382
- // Extended custom levels
15383
- WARNING: 4
15384
- // Warning
15385
- };
15386
-
15387
15371
  // src/utils/iam-utils.ts
15388
15372
  function determinePermissionStatus(payload) {
15389
15373
  if (payload.explicit_deny_count && payload.explicit_deny_count > 0) {
@@ -15482,58 +15466,40 @@ var getIamSummary = (span) => {
15482
15466
  return { denials, level: void 0 };
15483
15467
  };
15484
15468
 
15485
- // src/utils/error-level.ts
15486
- var calculateHighestErrorLevel = (span) => {
15487
- if (span.operation_status) {
15488
- switch (span.operation_status) {
15489
- case "error": {
15490
- return ErrorLevel.ERROR;
15491
- }
15492
- case "iam_explicit_deny":
15493
- case "iam_explicit_warn":
15494
- case "iam_implicit_deny":
15495
- case "iam_implicit_warn": {
15496
- return ErrorLevel.IAM_PERMISSION;
15497
- }
15498
- default: {
15499
- return ErrorLevel.OK;
15500
- }
15501
- }
15469
+ // src/utils/span-issues.ts
15470
+ var firstText = (...values) => values.find((value) => !!value) ?? void 0;
15471
+ var OTEL_STATUS_ERROR = 2;
15472
+ var getIamDenials = (span) => span.events.flatMap((event) => {
15473
+ if (!event.event_type?.toLowerCase().includes("iam") || typeof event.attributes?.payload !== "string") {
15474
+ return [];
15502
15475
  }
15503
- const hasIamDenial = span.events.some((event) => {
15504
- if (!event.event_type?.toLowerCase().includes("iam")) return false;
15505
- if (typeof event.attributes?.payload !== "string") return false;
15506
- const parsed = parseIAMEvent(event.attributes.payload);
15507
- return parsed?.permission === "explicitly_denied" || parsed?.permission === "implicitly_denied";
15508
- });
15509
- if (hasIamDenial) return ErrorLevel.IAM_PERMISSION;
15510
- if (span.status_code === 2) return ErrorLevel.ERROR;
15511
- const hasErrorInErrors = span.errors?.some((error) => error.level === "error");
15512
- if (hasErrorInErrors) return ErrorLevel.ERROR;
15513
- const hasWarning = span.errors?.some((error) => error.level === "warning");
15514
- if (hasWarning) return ErrorLevel.WARNING;
15515
- return span.status_code || ErrorLevel.UNSET;
15476
+ const permission = parseIAMEvent(event.attributes.payload)?.permission;
15477
+ if (permission === "explicitly_denied") return ["explicit"];
15478
+ if (permission === "implicitly_denied") return ["implicit"];
15479
+ return [];
15480
+ });
15481
+ var getSpanIssues = (span) => {
15482
+ const status = span.operation_status;
15483
+ const iamDenied = status === "iam_explicit_deny" || status === "iam_implicit_deny";
15484
+ const iamWarned = status === "iam_explicit_warn" || status === "iam_implicit_warn";
15485
+ const errorMessages = (span.errors ?? []).filter((item) => item.level === "error");
15486
+ const warningMessages = (span.errors ?? []).filter((item) => item.level === "warning");
15487
+ const denials = getIamDenials(span);
15488
+ const unenforcedDenial = !iamDenied && (denials.length > 0 || span.attributes?.["localstack.has_iam_deny_event"] === true);
15489
+ const error = status === "error" || iamDenied || span.status_code === OTEL_STATUS_ERROR || errorMessages.length > 0;
15490
+ const warning2 = iamWarned || warningMessages.length > 0 || unenforcedDenial;
15491
+ const denialCount = unenforcedDenial ? Math.max(1, denials.length) : 0;
15492
+ return {
15493
+ error,
15494
+ errorType: error ? firstText(span.status_message, errorMessages[0]?.message) ?? (iamDenied ? "AccessDenied" : "Error") : void 0,
15495
+ warning: warning2,
15496
+ warningCount: warning2 ? Math.max(1, warningMessages.length + denialCount) : 0,
15497
+ warningType: warning2 ? firstText(warningMessages[0]?.message) ?? (unenforcedDenial ? `IAM ${denials[0] ?? "implicit"} deny` : iamWarned ? "IAM permission warning" : "Warning") : void 0
15498
+ };
15516
15499
  };
15517
15500
 
15518
15501
  // src/components/cells/status-cell.tsx
15519
15502
  import { Fragment as Fragment2, jsx as jsx125, jsxs as jsxs120 } from "react/jsx-runtime";
15520
- var StatusIcon = ({ errorLevel, operationStatus, spanStatusCode }) => {
15521
- switch (errorLevel) {
15522
- case ErrorLevel.ERROR: {
15523
- return /* @__PURE__ */ jsx125(Cancel2, { sx: { color: "red" } });
15524
- }
15525
- case ErrorLevel.IAM_PERMISSION: {
15526
- const isDenial = operationStatus ? operationStatus === "iam_explicit_deny" || operationStatus === "iam_implicit_deny" : spanStatusCode === 2;
15527
- return /* @__PURE__ */ jsx125(GppBad, { sx: { color: isDenial ? "red" : "warning.main" } });
15528
- }
15529
- case ErrorLevel.WARNING: {
15530
- return /* @__PURE__ */ jsx125(Error2, { sx: { color: "orange" } });
15531
- }
15532
- default: {
15533
- return /* @__PURE__ */ jsx125(Fragment2, {});
15534
- }
15535
- }
15536
- };
15537
15503
  var SPAN_STATUS_LABELS = {
15538
15504
  0: "Unset",
15539
15505
  1: "OK",
@@ -15561,9 +15527,14 @@ var buildTooltipContent = (span) => {
15561
15527
  ] });
15562
15528
  };
15563
15529
  var StatusIconWithTooltip = ({ span }) => {
15564
- const errorLevel = calculateHighestErrorLevel(span);
15565
- if (errorLevel !== ErrorLevel.ERROR && errorLevel !== ErrorLevel.IAM_PERMISSION) return /* @__PURE__ */ jsx125(Fragment2, {});
15566
- return /* @__PURE__ */ jsx125(Tooltip3, { title: buildTooltipContent(span), children: /* @__PURE__ */ jsx125(Box3, { sx: { alignItems: "center", cursor: "default", display: "flex" }, children: /* @__PURE__ */ jsx125(StatusIcon, { errorLevel, operationStatus: span.operation_status, spanStatusCode: span.status_code }) }) });
15530
+ const { warningIcon } = useDevSettings();
15531
+ const { error, warning: warning2 } = getSpanIssues(span);
15532
+ if (!error && !warning2) return /* @__PURE__ */ jsx125(Fragment2, {});
15533
+ const iamLevel = getIamSummary(span).level;
15534
+ return /* @__PURE__ */ jsx125(Tooltip3, { title: buildTooltipContent(span), children: /* @__PURE__ */ jsxs120(Box3, { sx: { alignItems: "center", cursor: "default", display: "flex" }, children: [
15535
+ error && /* @__PURE__ */ jsx125(Box3, { "aria-label": "Error", role: "img", sx: { display: "flex" }, children: iamLevel === "error" ? /* @__PURE__ */ jsx125(GppBad, { sx: { color: "error.main" } }) : /* @__PURE__ */ jsx125(Cancel2, { sx: { color: "error.main" } }) }),
15536
+ warning2 && /* @__PURE__ */ jsx125(Box3, { "aria-label": "Warning", role: "img", sx: { display: "flex" }, children: iamLevel === "warning" && warningIcon === "iam shield" ? /* @__PURE__ */ jsx125(GppBad, { sx: { color: "warning.main" } }) : /* @__PURE__ */ jsx125(Error2, { sx: { color: "warning.main" } }) })
15537
+ ] }) });
15567
15538
  };
15568
15539
 
15569
15540
  // src/components/spans-list/span-count-badge.tsx
@@ -16780,38 +16751,6 @@ var getEventGroupsByType = (events) => {
16780
16751
  return groups;
16781
16752
  };
16782
16753
 
16783
- // src/utils/span-issues.ts
16784
- var firstText = (...values) => values.find((value) => !!value) ?? void 0;
16785
- var OTEL_STATUS_ERROR = 2;
16786
- var getIamDenials = (span) => span.events.flatMap((event) => {
16787
- if (!event.event_type?.toLowerCase().includes("iam") || typeof event.attributes?.payload !== "string") {
16788
- return [];
16789
- }
16790
- const permission = parseIAMEvent(event.attributes.payload)?.permission;
16791
- if (permission === "explicitly_denied") return ["explicit"];
16792
- if (permission === "implicitly_denied") return ["implicit"];
16793
- return [];
16794
- });
16795
- var getSpanIssues = (span) => {
16796
- const status = span.operation_status;
16797
- const iamDenied = status === "iam_explicit_deny" || status === "iam_implicit_deny";
16798
- const iamWarned = status === "iam_explicit_warn" || status === "iam_implicit_warn";
16799
- const errorMessages = (span.errors ?? []).filter((item) => item.level === "error");
16800
- const warningMessages = (span.errors ?? []).filter((item) => item.level === "warning");
16801
- const denials = getIamDenials(span);
16802
- const unenforcedDenial = !iamDenied && (denials.length > 0 || span.attributes?.["localstack.has_iam_deny_event"] === true);
16803
- const error = status === "error" || iamDenied || span.status_code === OTEL_STATUS_ERROR || errorMessages.length > 0;
16804
- const warning2 = iamWarned || warningMessages.length > 0 || unenforcedDenial;
16805
- const denialCount = unenforcedDenial ? Math.max(1, denials.length) : 0;
16806
- return {
16807
- error,
16808
- errorType: error ? firstText(span.status_message, errorMessages[0]?.message) ?? (iamDenied ? "AccessDenied" : "Error") : void 0,
16809
- warning: warning2,
16810
- warningCount: warning2 ? Math.max(1, warningMessages.length + denialCount) : 0,
16811
- warningType: warning2 ? firstText(warningMessages[0]?.message) ?? (unenforcedDenial ? `IAM ${denials[0] ?? "implicit"} deny` : iamWarned ? "IAM permission warning" : "Warning") : void 0
16812
- };
16813
- };
16814
-
16815
16754
  // src/utils/time.ts
16816
16755
  function unixNanoToDate(unixNanoString) {
16817
16756
  const milliseconds = unixNanoToMilliseconds(unixNanoString);
@@ -16860,7 +16799,7 @@ import { useEffect as useEffect12, useMemo as useMemo7, useRef as useRef8, useSt
16860
16799
  import { Cancel as Cancel4, CheckCircle, Error as Error3, Info, RemoveCircle } from "@mui/icons-material";
16861
16800
  import { Box as Box9 } from "@mui/material";
16862
16801
  import { jsx as jsx132 } from "react/jsx-runtime";
16863
- var StatusIcon2 = ({ errorLevel }) => {
16802
+ var StatusIcon = ({ errorLevel }) => {
16864
16803
  switch (errorLevel) {
16865
16804
  case EventLevel.LevelError: {
16866
16805
  return /* @__PURE__ */ jsx132(Cancel4, { sx: { color: "red" } });
@@ -18961,7 +18900,7 @@ var EventDetail = ({
18961
18900
  const eventLevel = getEventLevelFromGroup(type);
18962
18901
  return /* @__PURE__ */ jsxs129(Box14, { children: [
18963
18902
  /* @__PURE__ */ jsxs129(Box14, { sx: { alignItems: "center", display: "flex", mb: 0.5 }, children: [
18964
- /* @__PURE__ */ jsx137(StatusIcon2, { errorLevel: eventLevel }),
18903
+ /* @__PURE__ */ jsx137(StatusIcon, { errorLevel: eventLevel }),
18965
18904
  /* @__PURE__ */ jsx137(Typography10, { color: "text.secondary", sx: { fontWeight: 600, ml: 0.5 }, variant: "caption", children: displayText })
18966
18905
  ] }),
18967
18906
  /* @__PURE__ */ jsx137(Box14, { children: events.map((event) => /* @__PURE__ */ jsx137(Box14, { sx: { maxWidth: "100%", mb: 0.5 }, children: /* @__PURE__ */ jsx137(EventMessage, { event }) }, `${event.span_id}-${event.event_id}`)) })
@@ -31598,20 +31537,21 @@ import { Background, MarkerType, MiniMap, Position as Position2, ReactFlow, useE
31598
31537
  import { useCallback as useCallback7, useEffect as useEffect16, useRef as useRef13, useState as useState15 } from "react";
31599
31538
 
31600
31539
  // src/components/trace-graph/service-node.tsx
31601
- import { Cancel as Cancel6, GppBad as GppBad2 } from "@mui/icons-material";
31540
+ import { Cancel as Cancel6, Error as ErrorIcon4, GppBad as GppBad2 } from "@mui/icons-material";
31602
31541
  import { Box as Box17, Divider as Divider4, Stack as Stack5, Tooltip as Tooltip6, Typography as Typography13, useTheme as useTheme4 } from "@mui/material";
31603
31542
  import { Handle, Position } from "@xyflow/react";
31604
31543
  import "@xyflow/react/dist/style.css";
31605
31544
  import "react";
31606
- import { memo as memo11, useMemo as useMemo11, useRef as useRef12, useState as useState14 } from "react";
31545
+ import { memo as memo11, useRef as useRef12, useState as useState14 } from "react";
31607
31546
  import { Fragment as Fragment11, jsx as jsx143, jsxs as jsxs132 } from "react/jsx-runtime";
31608
31547
  var handleStyle = {
31609
31548
  backgroundColor: "white",
31610
31549
  borderColor: "lightgray",
31611
31550
  opacity: 0
31612
31551
  };
31613
- var NodeContent = ({ event, expanded, hasPermissionEvents, isError, isWarning }) => {
31552
+ var NodeContent = ({ event, expanded, iamLevel, issues }) => {
31614
31553
  const theme = useTheme4();
31554
+ const { warningIcon } = useDevSettings();
31615
31555
  return /* @__PURE__ */ jsxs132(Fragment11, { children: [
31616
31556
  /* @__PURE__ */ jsx143(Handle, { position: Position.Left, style: handleStyle, type: "target" }),
31617
31557
  /* @__PURE__ */ jsx143(Handle, { position: Position.Right, style: handleStyle, type: "source" }),
@@ -31666,8 +31606,8 @@ var NodeContent = ({ event, expanded, hasPermissionEvents, isError, isWarning })
31666
31606
  width: "38px"
31667
31607
  },
31668
31608
  children: [
31669
- (isError || isWarning) && hasPermissionEvents && /* @__PURE__ */ jsx143(Tooltip6, { arrow: true, title: "Permission denied", children: /* @__PURE__ */ jsx143(GppBad2, { sx: { color: isError ? theme.palette.error.main : theme.palette.warning.main, fontSize: "16px" } }) }),
31670
- isError && !hasPermissionEvents && /* @__PURE__ */ jsx143(Tooltip6, { arrow: true, title: "Error", children: /* @__PURE__ */ jsx143(Cancel6, { sx: { color: theme.palette.error.main, fontSize: "16px" } }) })
31609
+ issues.error && /* @__PURE__ */ jsx143(Tooltip6, { arrow: true, title: issues.errorType, children: /* @__PURE__ */ jsx143(Box17, { "aria-label": "Error", role: "img", sx: { display: "flex" }, children: iamLevel === "error" ? /* @__PURE__ */ jsx143(GppBad2, { sx: { color: theme.palette.error.main, fontSize: "16px" } }) : /* @__PURE__ */ jsx143(Cancel6, { sx: { color: theme.palette.error.main, fontSize: "16px" } }) }) }),
31610
+ issues.warning && /* @__PURE__ */ jsx143(Tooltip6, { arrow: true, title: issues.warningType, children: /* @__PURE__ */ jsx143(Box17, { "aria-label": "Warning", role: "img", sx: { display: "flex" }, children: iamLevel === "warning" && warningIcon === "iam shield" ? /* @__PURE__ */ jsx143(GppBad2, { sx: { color: theme.palette.warning.main, fontSize: "16px" } }) : /* @__PURE__ */ jsx143(ErrorIcon4, { sx: { color: theme.palette.warning.main, fontSize: "16px" } }) }) })
31671
31611
  ]
31672
31612
  }
31673
31613
  ),
@@ -31690,17 +31630,9 @@ var ServiceNode = memo11(({ data, selected }) => {
31690
31630
  const theme = useTheme4();
31691
31631
  const [isHovered, setIsHovered] = useState14(false);
31692
31632
  const nodeRef = useRef12(null);
31693
- const { operation_status } = data.event;
31694
- const hasPermissionEventsLegacy = useMemo11(
31695
- () => data.event.events.some(
31696
- (event) => event.event_type === "iam.policy_evaluation" && event.name === "iam.policy.denied"
31697
- ),
31698
- [data.event.events]
31699
- );
31700
- const isError = operation_status ? operation_status === "error" || operation_status === "iam_explicit_deny" || operation_status === "iam_implicit_deny" : data.event.status_code === 2;
31701
- const isWarning = operation_status ? operation_status === "iam_explicit_warn" || operation_status === "iam_implicit_warn" : !isError && data.event.status_code === 1 && hasPermissionEventsLegacy;
31702
- const hasPermissionEvents = operation_status ? operation_status.startsWith("iam_") : hasPermissionEventsLegacy;
31703
- const outlineColor = isError ? theme.palette.error.main : isWarning ? theme.palette.warning.main : void 0;
31633
+ const issues = getSpanIssues(data.event);
31634
+ const iamLevel = getIamSummary(data.event).level;
31635
+ const outlineColor = issues.error ? theme.palette.error.main : issues.warning ? theme.palette.warning.main : void 0;
31704
31636
  const sharedBoxSx = {
31705
31637
  backgroundColor: theme.palette.background.paper,
31706
31638
  borderLeft: `3px solid ${outlineColor ?? "transparent"}`,
@@ -31709,7 +31641,7 @@ var ServiceNode = memo11(({ data, selected }) => {
31709
31641
  minWidth: 140,
31710
31642
  outline: selected ? `2px solid ${outlineColor ?? theme.palette.primary.main}` : `1px solid ${theme.palette.divider}`
31711
31643
  };
31712
- const contentProps = { event: data.event, hasPermissionEvents, isError, isWarning };
31644
+ const contentProps = { event: data.event, iamLevel, issues };
31713
31645
  const handleMouseEnter = () => {
31714
31646
  setIsHovered(true);
31715
31647
  const xyNode = nodeRef.current?.closest(".react-flow__node");
@@ -32298,7 +32230,7 @@ var getTraceBounds = (spans) => {
32298
32230
  };
32299
32231
 
32300
32232
  // src/components/trace-panel/waterfall.tsx
32301
- import { AccountTreeOutlined, Cancel as Cancel7, Error as ErrorIcon4, FormatListBulleted, KeyboardArrowDown as KeyboardArrowDown2, KeyboardArrowRight as KeyboardArrowRight2 } from "@mui/icons-material";
32233
+ import { AccountTreeOutlined, Cancel as Cancel7, Error as ErrorIcon5, FormatListBulleted, KeyboardArrowDown as KeyboardArrowDown2, KeyboardArrowRight as KeyboardArrowRight2 } from "@mui/icons-material";
32302
32234
  import { Box as Box19, ButtonBase, ToggleButton as ToggleButton2, ToggleButtonGroup as ToggleButtonGroup2, Typography as Typography14 } from "@mui/material";
32303
32235
  import { alpha as alpha5 } from "@mui/material/styles";
32304
32236
  import * as React124 from "react";
@@ -32382,7 +32314,7 @@ var flatten = (nodes2, collapsed, depth = 0, branches = [], parent) => nodes2.fl
32382
32314
  var flattenAll = (nodes2) => flatten(nodes2, /* @__PURE__ */ new Set()).map((row) => ({ ...row, branches: [], depth: 0, last: true })).toSorted((left, right) => compareNano(left.node.span.start_time_unix_nano, right.node.span.start_time_unix_nano));
32383
32315
  var StatusIcons = ({ error, warning: warning2 }) => /* @__PURE__ */ jsxs136(Fragment13, { children: [
32384
32316
  error && /* @__PURE__ */ jsx147(Cancel7, { sx: { color: "error.main", fontSize: 14 } }),
32385
- warning2 && /* @__PURE__ */ jsx147(ErrorIcon4, { sx: { color: "warning.main", fontSize: 14 } })
32317
+ warning2 && /* @__PURE__ */ jsx147(ErrorIcon5, { sx: { color: "warning.main", fontSize: 14 } })
32386
32318
  ] });
32387
32319
  var Line = ({ height, left, top = 0, width = 1 }) => /* @__PURE__ */ jsx147(
32388
32320
  Box19,
@@ -33603,11 +33535,11 @@ var SpansListPage = () => {
33603
33535
  setSelectedSpanId(void 0);
33604
33536
  }, []);
33605
33537
  const rootSpanId = selectedSpanId === void 0 ? void 0 : compute.rows[selectedSpanId]?.rootSpanId;
33606
- const treeSpans = useMemo14(() => {
33538
+ const treeSpans = useMemo13(() => {
33607
33539
  if (rootSpanId === void 0) return [];
33608
33540
  return Object.entries(compute.rows).filter(([, row]) => row.rootSpanId === rootSpanId).map(([spanId]) => getSpan(spanId)).filter((span) => span !== void 0);
33609
33541
  }, [compute.rows, getSpan, rootSpanId]);
33610
- const spanMatches = useMemo14(
33542
+ const spanMatches = useMemo13(
33611
33543
  () => compute.queryActive ? (spanId) => compute.rows[spanId]?.matches ?? false : void 0,
33612
33544
  [compute]
33613
33545
  );