@localstack/appinspector-ui 1.0.143 → 1.0.145

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.cjs CHANGED
@@ -2480,6 +2480,9 @@ var SPANS_LIST_PAGE_TEST_ID = "spans-list-page";
2480
2480
  var TRACE_GRAPH_PAGE_TEST_ID = "trace-graph-page";
2481
2481
  var VIEW_SPAN_DETAILS_TEST_ID = "view-span-details";
2482
2482
  var SPANS_DATA_GRID_ROW_TEST_ID = "spans-data-grid-row";
2483
+ var SPANS_SEARCH_INPUT_TEST_ID = "spans-search-input";
2484
+ var SPANS_SEARCH_CLEAR_TEST_ID = "spans-search-clear";
2485
+ var SPANS_SEARCH_NO_MATCH_TEST_ID = "spans-search-no-match";
2483
2486
  var EVENT_DETAILS_SECTION_BASIC_INFORMATION_TEST_ID = "event-details-section-basic-information";
2484
2487
  var EVENT_DETAILS_SERVICE_NAME_TEST_ID = "event-details-service-name";
2485
2488
  var EVENT_DETAILS_OPERATION_NAME_TEST_ID = "event-details-operation-name";
@@ -2512,6 +2515,7 @@ var EMULATOR_VERSION_BANNER_TEST_ID = "emulator-version-banner";
2512
2515
  // src/utils/emulator-version.ts
2513
2516
  var import_semver = __toESM(require_semver2(), 1);
2514
2517
  var MINIMUM_EMULATOR_VERSION = "2026.4.0";
2518
+ var MIN_EMULATOR_VERSION_FOR_SEARCH = "2026.6.0";
2515
2519
  function checkEmulatorVersion(version, now = /* @__PURE__ */ new Date()) {
2516
2520
  if (!version) return "error";
2517
2521
  const coerced = import_semver.default.coerce(version);
@@ -2522,6 +2526,13 @@ function checkEmulatorVersion(version, now = /* @__PURE__ */ new Date()) {
2522
2526
  if (ageInMonths >= 3) return "warning";
2523
2527
  return "ok";
2524
2528
  }
2529
+ function emulatorSupportsSearch(version) {
2530
+ if (!version) return false;
2531
+ const coerced = import_semver.default.coerce(version);
2532
+ const minimum = import_semver.default.coerce(MIN_EMULATOR_VERSION_FOR_SEARCH);
2533
+ if (!coerced || !minimum) return false;
2534
+ return import_semver.default.gte(coerced, minimum);
2535
+ }
2525
2536
  function getVersionAgeInMonths(year, month, now = /* @__PURE__ */ new Date()) {
2526
2537
  const versionDate = new Date(year, month - 1, 1);
2527
2538
  return (now.getTime() - versionDate.getTime()) / (1e3 * 60 * 60 * 24 * 30.44);
@@ -15273,13 +15284,14 @@ function parseIAMEvent(payloadString) {
15273
15284
 
15274
15285
  // src/components/cells/status-cell.tsx
15275
15286
  var import_jsx_runtime124 = require("react/jsx-runtime");
15276
- var StatusIcon = ({ errorLevel, spanStatusCode }) => {
15287
+ var StatusIcon = ({ errorLevel, operationStatus, spanStatusCode }) => {
15277
15288
  switch (errorLevel) {
15278
15289
  case ErrorLevel.ERROR: {
15279
15290
  return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(import_icons_material.Cancel, { sx: { color: "red" } });
15280
15291
  }
15281
15292
  case ErrorLevel.IAM_PERMISSION: {
15282
- return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(import_icons_material.GppBad, { sx: { color: spanStatusCode === 2 ? "red" : "warning.main" } });
15293
+ const isDenial = operationStatus ? operationStatus === "iam_explicit_deny" || operationStatus === "iam_implicit_deny" : spanStatusCode === 2;
15294
+ return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(import_icons_material.GppBad, { sx: { color: isDenial ? "red" : "warning.main" } });
15283
15295
  }
15284
15296
  case ErrorLevel.WARNING: {
15285
15297
  return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(import_icons_material.Error, { sx: { color: "orange" } });
@@ -15290,6 +15302,22 @@ var StatusIcon = ({ errorLevel, spanStatusCode }) => {
15290
15302
  }
15291
15303
  };
15292
15304
  var calculateHighestErrorLevel = (span) => {
15305
+ if (span.operation_status) {
15306
+ switch (span.operation_status) {
15307
+ case "error": {
15308
+ return ErrorLevel.ERROR;
15309
+ }
15310
+ case "iam_explicit_deny":
15311
+ case "iam_explicit_warn":
15312
+ case "iam_implicit_deny":
15313
+ case "iam_implicit_warn": {
15314
+ return ErrorLevel.IAM_PERMISSION;
15315
+ }
15316
+ default: {
15317
+ return ErrorLevel.OK;
15318
+ }
15319
+ }
15320
+ }
15293
15321
  const hasIamDenial = span.events.some((event) => {
15294
15322
  if (!event.event_type?.toLowerCase().includes("iam")) return false;
15295
15323
  if (typeof event.attributes?.payload !== "string") return false;
@@ -15333,7 +15361,7 @@ var buildTooltipContent = (span) => {
15333
15361
  var StatusIconWithTooltip = ({ span }) => {
15334
15362
  const errorLevel = calculateHighestErrorLevel(span);
15335
15363
  if (errorLevel !== ErrorLevel.ERROR && errorLevel !== ErrorLevel.IAM_PERMISSION) return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(import_jsx_runtime124.Fragment, {});
15336
- return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(import_material4.Tooltip, { title: buildTooltipContent(span), children: /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(import_material4.Box, { sx: { alignItems: "center", cursor: "default", display: "flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(StatusIcon, { errorLevel, spanStatusCode: span.status_code }) }) });
15364
+ return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(import_material4.Tooltip, { title: buildTooltipContent(span), children: /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(import_material4.Box, { sx: { alignItems: "center", cursor: "default", display: "flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(StatusIcon, { errorLevel, operationStatus: span.operation_status, spanStatusCode: span.status_code }) }) });
15337
15365
  };
15338
15366
 
15339
15367
  // src/components/spans-list/span-count-badge.tsx
@@ -15357,7 +15385,7 @@ var StatRow = ({ label, value }) => /* @__PURE__ */ (0, import_jsx_runtime125.js
15357
15385
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Typography, { color: "text.secondary", sx: { mr: 4 }, variant: "caption", children: label }),
15358
15386
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Typography, { fontWeight: 600, variant: "body2", children: fmtLimit(value) })
15359
15387
  ] });
15360
- var SpanCountBadge = ({ licenseLimit, systemLimit, totalCount, visibleCount }) => {
15388
+ var SpanCountBadge = ({ licenseLimit, searchActive = false, systemLimit, totalCount, visibleCount }) => {
15361
15389
  const [anchorElement, setAnchorElement] = (0, import_react36.useState)();
15362
15390
  const bindingLimit = resolveBindingLimit(systemLimit, licenseLimit);
15363
15391
  if (totalCount === void 0 && bindingLimit === void 0) {
@@ -15372,7 +15400,7 @@ var SpanCountBadge = ({ licenseLimit, systemLimit, totalCount, visibleCount }) =
15372
15400
  isLoading ? /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Skeleton, { sx: { minWidth: 120 } }) : /* @__PURE__ */ (0, import_jsx_runtime125.jsxs)(import_material5.Box, { sx: { alignItems: "baseline", display: "flex", gap: 0.5 }, children: [
15373
15401
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Typography, { color: "text.secondary", variant: "caption", children: "Showing" }),
15374
15402
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Typography, { variant: "body2", children: visibleCount === void 0 ? "\u2014" : fmt(visibleCount) }),
15375
- /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Typography, { color: "text.secondary", variant: "caption", children: `of ${totalCount === void 0 ? "-" : fmt(totalCount)} operations` })
15403
+ /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Typography, { color: "text.secondary", variant: "caption", children: `of ${totalCount === void 0 ? "-" : fmt(totalCount)} ${searchActive ? "matches" : "operations"}` })
15376
15404
  ] }),
15377
15405
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Tooltip, { title: "Operation count details", children: /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(
15378
15406
  import_material5.IconButton,
@@ -15411,7 +15439,7 @@ var SpanCountBadge = ({ licenseLimit, systemLimit, totalCount, visibleCount }) =
15411
15439
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(
15412
15440
  StatRow,
15413
15441
  {
15414
- label: /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Tooltip, { placement: "left", title: "The total number of operations stored on the server.", children: /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Box, { component: "span", sx: { borderBottom: "1px dashed", borderColor: "text.secondary", cursor: "help" }, children: "Total stored" }) }),
15442
+ label: /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Tooltip, { placement: "left", title: searchActive ? "The total number of operations matching the search." : "The total number of operations stored on the server.", children: /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Box, { component: "span", sx: { borderBottom: "1px dashed", borderColor: "text.secondary", cursor: "help" }, children: searchActive ? "Total matches" : "Total stored" }) }),
15415
15443
  value: totalCount
15416
15444
  }
15417
15445
  )
@@ -15478,9 +15506,55 @@ var newSpanBackgroundColor = {
15478
15506
  var LABEL_BEGINNING_OF_STREAM = "This is the beginning of the stream";
15479
15507
  var LABEL_END_OF_STREAM = "This is the end of the stream";
15480
15508
  var LABEL_NO_SPANS = "There are no operations";
15509
+ var SEARCH_MAX_LENGTH = 512;
15510
+ var SEARCH_DEBOUNCE_MS = 300;
15481
15511
  var STICK_TO_EDGE_THRESHOLD_PX = 4;
15482
15512
  var StreamBoundaryRow = ({ colSpan, label }) => /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableCell, { colSpan, sx: { fontStyle: "italic", textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)("span", { children: label }) }) });
15483
15513
  var LoadingRow = ({ colSpan }) => /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableCell, { colSpan, sx: { textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.CircularProgress, { size: 24, sx: { m: 2 } }) }) });
15514
+ var NoMatchRow = ({
15515
+ colSpan,
15516
+ onClearSearch,
15517
+ term
15518
+ }) => /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableCell, { colSpan, "data-testid": SPANS_SEARCH_NO_MATCH_TEST_ID, sx: { textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_material6.Box, { sx: { alignItems: "center", display: "flex", flexDirection: "column", gap: 1, py: 3 }, children: [
15519
+ /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { sx: { fontStyle: "italic" }, variant: "body2", children: `No operations match \u201C${term}\u201D` }),
15520
+ /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Button, { onClick: onClearSearch, size: "small", variant: "text", children: "Clear search" })
15521
+ ] }) }) });
15522
+ var SpansSearchField = ({ onChange, onClear, searchSupported, value }) => {
15523
+ const field = /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15524
+ import_material6.TextField,
15525
+ {
15526
+ disabled: !searchSupported,
15527
+ inputProps: {
15528
+ "aria-label": "Search operations",
15529
+ "data-testid": SPANS_SEARCH_INPUT_TEST_ID,
15530
+ "maxLength": SEARCH_MAX_LENGTH
15531
+ },
15532
+ InputProps: {
15533
+ endAdornment: value ? /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.InputAdornment, { position: "end", children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15534
+ import_material6.IconButton,
15535
+ {
15536
+ "aria-label": "Clear search",
15537
+ "data-testid": SPANS_SEARCH_CLEAR_TEST_ID,
15538
+ edge: "end",
15539
+ onClick: onClear,
15540
+ size: "small",
15541
+ children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_icons_material3.Close, { sx: { fontSize: 16 } })
15542
+ }
15543
+ ) }) : void 0,
15544
+ startAdornment: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.InputAdornment, { position: "start", children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_icons_material3.Search, { sx: { fontSize: 18 } }) })
15545
+ },
15546
+ onChange: (event) => {
15547
+ onChange(event.target.value);
15548
+ },
15549
+ placeholder: "Search operations\u2026",
15550
+ size: "small",
15551
+ sx: { minWidth: 260 },
15552
+ value
15553
+ }
15554
+ );
15555
+ if (searchSupported) return field;
15556
+ return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Tooltip, { title: `Search requires LocalStack \u2265 ${MIN_EMULATOR_VERSION_FOR_SEARCH}`, children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)("span", { children: field }) });
15557
+ };
15484
15558
  var SpansDataGridRow = (0, import_react37.memo)(
15485
15559
  ({ isNew, onSpanClick, row }) => {
15486
15560
  const theme = (0, import_material6.useTheme)();
@@ -15548,13 +15622,35 @@ var SpansDataGrid = ({
15548
15622
  licenseLimit,
15549
15623
  onClearSpans,
15550
15624
  onOrderChange,
15625
+ onSearchChange,
15551
15626
  onSpanClick,
15552
15627
  order: order2,
15628
+ search,
15629
+ searching,
15630
+ searchSupported,
15553
15631
  spans,
15554
15632
  systemLimit,
15555
15633
  totalCount,
15556
15634
  warningCount
15557
15635
  }) => {
15636
+ const searchActive = search.length > 0;
15637
+ const [searchDraft, setSearchDraft] = (0, import_react37.useState)(search);
15638
+ (0, import_react37.useEffect)(() => {
15639
+ const handle = setTimeout(() => {
15640
+ onSearchChange(searchDraft);
15641
+ }, SEARCH_DEBOUNCE_MS);
15642
+ return () => {
15643
+ clearTimeout(handle);
15644
+ };
15645
+ }, [searchDraft, onSearchChange]);
15646
+ const handleClearSearch = (0, import_react37.useCallback)(() => {
15647
+ setSearchDraft("");
15648
+ onSearchChange("");
15649
+ }, [onSearchChange]);
15650
+ const handleClearSpans = (0, import_react37.useCallback)(() => {
15651
+ setSearchDraft("");
15652
+ onClearSpans();
15653
+ }, [onClearSpans]);
15558
15654
  const columns = (0, import_react37.useMemo)(() => [
15559
15655
  columnHelper.accessor("startTime", {
15560
15656
  cell: (props) => /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(ResponsiveTimestampCell, { date: props.getValue() }),
@@ -15577,7 +15673,13 @@ var SpansDataGrid = ({
15577
15673
  if (!props.row.original.parent_service_name || !props.row.original.parent_resource_name) {
15578
15674
  return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { sx: { fontStyle: "italic" }, variant: "body2", children: "External producer" });
15579
15675
  }
15580
- return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(ServiceCell, { resource: props.row.original.parent_resource_name, service: props.row.original.parent_service_name });
15676
+ return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15677
+ ServiceCell,
15678
+ {
15679
+ resource: props.row.original.parent_resource_name,
15680
+ service: props.row.original.parent_service_name
15681
+ }
15682
+ );
15581
15683
  },
15582
15684
  header: "Producer",
15583
15685
  id: "producer",
@@ -15688,14 +15790,40 @@ var SpansDataGrid = ({
15688
15790
  }, [order2, spans?.length, lastSpanId]);
15689
15791
  return /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15690
15792
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Grid, { item: true, xs: 12, children: /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_material6.Box, { sx: { alignItems: "center", display: "flex", gap: "0.5rem" }, children: [
15691
- /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(SpanCountBadge, { licenseLimit, systemLimit, totalCount, visibleCount: spans?.length }),
15692
- errorCount !== void 0 && errorCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15793
+ /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15794
+ SpansSearchField,
15795
+ {
15796
+ onChange: setSearchDraft,
15797
+ onClear: handleClearSearch,
15798
+ searchSupported,
15799
+ value: searchDraft
15800
+ }
15801
+ ),
15802
+ /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15803
+ SpanCountBadge,
15804
+ {
15805
+ licenseLimit,
15806
+ searchActive,
15807
+ systemLimit,
15808
+ totalCount,
15809
+ visibleCount: spans?.length
15810
+ }
15811
+ ),
15812
+ !searchActive && errorCount !== void 0 && errorCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15693
15813
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "text.disabled", sx: { mr: 0.5 }, variant: "body2", children: "\u2014" }),
15694
15814
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Tooltip, { title: "Number of errors detected across all operations", children: /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(
15695
15815
  import_material6.Box,
15696
15816
  {
15697
15817
  "data-testid": ERROR_COUNT_TEST_ID,
15698
- sx: { alignItems: "center", borderBottom: "1px dotted", borderColor: "error.main", cursor: "help", display: "flex", gap: 0.5, mb: "-1px" },
15818
+ sx: {
15819
+ alignItems: "center",
15820
+ borderBottom: "1px dotted",
15821
+ borderColor: "error.main",
15822
+ cursor: "help",
15823
+ display: "flex",
15824
+ gap: 0.5,
15825
+ mb: "-1px"
15826
+ },
15699
15827
  children: [
15700
15828
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "error", variant: "body2", children: errorCount.toLocaleString() }),
15701
15829
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "error", variant: "caption", children: errorCount === 1 ? "error" : "errors" })
@@ -15703,13 +15831,21 @@ var SpansDataGrid = ({
15703
15831
  }
15704
15832
  ) })
15705
15833
  ] }),
15706
- warningCount !== void 0 && warningCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15834
+ !searchActive && warningCount !== void 0 && warningCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15707
15835
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "text.disabled", sx: { mr: 0.5 }, variant: "body2", children: "\u2014" }),
15708
15836
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Tooltip, { title: "Number of warnings detected across all operations", children: /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(
15709
15837
  import_material6.Box,
15710
15838
  {
15711
15839
  "data-testid": WARNING_COUNT_TEST_ID,
15712
- sx: { alignItems: "center", borderBottom: "1px dotted", borderColor: "warning.main", cursor: "help", display: "flex", gap: 0.5, mb: "-1px" },
15840
+ sx: {
15841
+ alignItems: "center",
15842
+ borderBottom: "1px dotted",
15843
+ borderColor: "warning.main",
15844
+ cursor: "help",
15845
+ display: "flex",
15846
+ gap: 0.5,
15847
+ mb: "-1px"
15848
+ },
15713
15849
  children: [
15714
15850
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "warning.main", variant: "body2", children: warningCount.toLocaleString() }),
15715
15851
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "warning.main", variant: "caption", children: warningCount === 1 ? "warning" : "warnings" })
@@ -15725,7 +15861,7 @@ var SpansDataGrid = ({
15725
15861
  import_material6.Button,
15726
15862
  {
15727
15863
  "data-testid": CLEAR_SPANS_TEST_ID,
15728
- onClick: onClearSpans,
15864
+ onClick: handleClearSpans,
15729
15865
  startIcon: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_icons_material3.DeleteForeverOutlined, {}),
15730
15866
  children: "Clear Operations"
15731
15867
  }
@@ -15778,31 +15914,71 @@ var SpansDataGrid = ({
15778
15914
  )) }, headerGroup.id);
15779
15915
  }) }),
15780
15916
  /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_material6.TableBody, { children: [
15781
- (spans === void 0 || clearingSpans) && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableCell, { colSpan: columnCount, sx: { fontStyle: "italic", textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.CircularProgress, { size: 48, sx: { m: 4 } }) }) }),
15782
- !clearingSpans && spans?.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_NO_SPANS }),
15917
+ (spans === void 0 || clearingSpans || searching) && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableCell, { colSpan: columnCount, sx: { fontStyle: "italic", textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.CircularProgress, { size: 48, sx: { m: 4 } }) }) }),
15918
+ !clearingSpans && !searching && spans?.length === 0 && (searchActive ? /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(NoMatchRow, { colSpan: columnCount, onClearSearch: handleClearSearch, term: search }) : /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_NO_SPANS })),
15783
15919
  !clearingSpans && order2 === "newest_first" && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15784
15920
  hasRows && hasMoreForward && fetchingForward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(LoadingRow, { colSpan: columnCount }),
15785
15921
  hasRows && !hasMoreForward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_END_OF_STREAM }),
15786
- hasRows && paddingTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableCell, { colSpan: columnCount, sx: { border: 0, height: paddingTop, p: 0 } }) }),
15922
+ hasRows && paddingTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15923
+ import_material6.TableCell,
15924
+ {
15925
+ colSpan: columnCount,
15926
+ sx: { border: 0, height: paddingTop, p: 0 }
15927
+ }
15928
+ ) }),
15787
15929
  hasRows && virtualItems.map((virtualItem) => {
15788
15930
  const row = displayRows[virtualItem.index];
15789
15931
  const isNew = !(seenSpanIdsRef.current?.has(row.original.span_id) ?? false);
15790
- return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(SpansDataGridRow, { isNew, onSpanClick, row }, row.original.span_id);
15932
+ return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15933
+ SpansDataGridRow,
15934
+ {
15935
+ isNew,
15936
+ onSpanClick,
15937
+ row
15938
+ },
15939
+ row.original.span_id
15940
+ );
15791
15941
  }),
15792
- hasRows && paddingBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableCell, { colSpan: columnCount, sx: { border: 0, height: paddingBottom, p: 0 } }) }),
15942
+ hasRows && paddingBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15943
+ import_material6.TableCell,
15944
+ {
15945
+ colSpan: columnCount,
15946
+ sx: { border: 0, height: paddingBottom, p: 0 }
15947
+ }
15948
+ ) }),
15793
15949
  hasRows && hasMoreBackward && fetchingBackward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(LoadingRow, { colSpan: columnCount }),
15794
15950
  hasRows && !hasMoreBackward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_BEGINNING_OF_STREAM })
15795
15951
  ] }),
15796
15952
  !clearingSpans && order2 === "oldest_first" && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15797
15953
  hasRows && !hasMoreBackward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_BEGINNING_OF_STREAM }),
15798
15954
  hasRows && hasMoreBackward && fetchingBackward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(LoadingRow, { colSpan: columnCount }),
15799
- hasRows && paddingTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableCell, { colSpan: columnCount, sx: { border: 0, height: paddingTop, p: 0 } }) }),
15955
+ hasRows && paddingTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15956
+ import_material6.TableCell,
15957
+ {
15958
+ colSpan: columnCount,
15959
+ sx: { border: 0, height: paddingTop, p: 0 }
15960
+ }
15961
+ ) }),
15800
15962
  hasRows && virtualItems.map((virtualItem) => {
15801
15963
  const row = displayRows[virtualItem.index];
15802
15964
  const isNew = !(seenSpanIdsRef.current?.has(row.original.span_id) ?? false);
15803
- return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(SpansDataGridRow, { isNew, onSpanClick, row }, row.original.span_id);
15965
+ return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15966
+ SpansDataGridRow,
15967
+ {
15968
+ isNew,
15969
+ onSpanClick,
15970
+ row
15971
+ },
15972
+ row.original.span_id
15973
+ );
15804
15974
  }),
15805
- hasRows && paddingBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableCell, { colSpan: columnCount, sx: { border: 0, height: paddingBottom, p: 0 } }) }),
15975
+ hasRows && paddingBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15976
+ import_material6.TableCell,
15977
+ {
15978
+ colSpan: columnCount,
15979
+ sx: { border: 0, height: paddingBottom, p: 0 }
15980
+ }
15981
+ ) }),
15806
15982
  hasRows && hasMoreForward && fetchingForward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(LoadingRow, { colSpan: columnCount }),
15807
15983
  hasRows && !hasMoreForward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_END_OF_STREAM })
15808
15984
  ] })
@@ -15843,8 +16019,12 @@ var SpansList = (props) => {
15843
16019
  licenseLimit: props.licenseLimit,
15844
16020
  onClearSpans: props.onClearSpans,
15845
16021
  onOrderChange: props.onOrderChange,
16022
+ onSearchChange: props.onSearchChange,
15846
16023
  onSpanClick: props.onSpanClick,
15847
16024
  order: props.order,
16025
+ search: props.search,
16026
+ searching: props.searching,
16027
+ searchSupported: props.searchSupported,
15848
16028
  spans: props.spans,
15849
16029
  systemLimit: props.systemLimit,
15850
16030
  totalCount: props.totalCount,
@@ -16255,6 +16435,7 @@ var createPaginationBuffer = (options) => {
16255
16435
  let hasMoreBackward = void 0;
16256
16436
  let fetchingForward = false;
16257
16437
  let fetchingBackward = false;
16438
+ let abortController;
16258
16439
  const reportDataChange = () => {
16259
16440
  options.onDataChange({
16260
16441
  hasMoreBackward: hasMoreBackward ?? false,
@@ -16278,25 +16459,40 @@ var createPaginationBuffer = (options) => {
16278
16459
  if (hasMoreForward === true) {
16279
16460
  return;
16280
16461
  }
16462
+ const controller = new AbortController();
16463
+ abortController = controller;
16281
16464
  fetchingForward = true;
16282
16465
  reportStatusChange();
16283
16466
  try {
16284
- const newPage = await fetchPage(nextForwardToken);
16467
+ const newPage = await fetchPage(nextForwardToken, controller.signal);
16468
+ if (controller.signal.aborted) {
16469
+ return;
16470
+ }
16285
16471
  hasMoreForward = newPage.items.length > 0;
16286
16472
  reportDataChange();
16287
16473
  } catch (error) {
16474
+ if (controller.signal.aborted) {
16475
+ return;
16476
+ }
16288
16477
  reportError(error instanceof Error ? error : new Error("Failed to fetch forward page", { cause: error }));
16289
16478
  } finally {
16290
- fetchingForward = false;
16291
- reportStatusChange();
16479
+ if (!controller.signal.aborted) {
16480
+ fetchingForward = false;
16481
+ reportStatusChange();
16482
+ runPostFetchActions();
16483
+ }
16292
16484
  }
16293
- runPostFetchActions();
16294
16485
  };
16295
16486
  const fetchForward = async () => {
16487
+ const controller = new AbortController();
16488
+ abortController = controller;
16296
16489
  fetchingForward = true;
16297
16490
  reportStatusChange();
16298
16491
  try {
16299
- const newPage = await fetchPage(nextForwardToken);
16492
+ const newPage = await fetchPage(nextForwardToken, controller.signal);
16493
+ if (controller.signal.aborted) {
16494
+ return;
16495
+ }
16300
16496
  if (options.sortComparator === void 0) {
16301
16497
  items = [...items ?? [], ...newPage.items];
16302
16498
  } else {
@@ -16312,18 +16508,28 @@ var createPaginationBuffer = (options) => {
16312
16508
  nextBackwardToken ??= newPage.nextBackwardToken;
16313
16509
  reportDataChange();
16314
16510
  } catch (error) {
16511
+ if (controller.signal.aborted) {
16512
+ return;
16513
+ }
16315
16514
  reportError(error instanceof Error ? error : new Error("Failed to fetch forward page", { cause: error }));
16316
16515
  } finally {
16317
- fetchingForward = false;
16318
- reportStatusChange();
16516
+ if (!controller.signal.aborted) {
16517
+ fetchingForward = false;
16518
+ reportStatusChange();
16519
+ runPostFetchActions();
16520
+ }
16319
16521
  }
16320
- runPostFetchActions();
16321
16522
  };
16322
16523
  const fetchBackward = async () => {
16524
+ const controller = new AbortController();
16525
+ abortController = controller;
16323
16526
  fetchingBackward = true;
16324
16527
  reportStatusChange();
16325
16528
  try {
16326
- const newPage = await fetchPage(nextBackwardToken);
16529
+ const newPage = await fetchPage(nextBackwardToken, controller.signal);
16530
+ if (controller.signal.aborted) {
16531
+ return;
16532
+ }
16327
16533
  if (options.sortComparator === void 0) {
16328
16534
  items = [...newPage.items, ...items ?? []];
16329
16535
  } else {
@@ -16339,12 +16545,17 @@ var createPaginationBuffer = (options) => {
16339
16545
  nextForwardToken ??= newPage.nextForwardToken;
16340
16546
  reportDataChange();
16341
16547
  } catch (error) {
16548
+ if (controller.signal.aborted) {
16549
+ return;
16550
+ }
16342
16551
  reportError(error instanceof Error ? error : new Error("Failed to fetch forward page", { cause: error }));
16343
16552
  } finally {
16344
- fetchingBackward = false;
16345
- reportStatusChange();
16553
+ if (!controller.signal.aborted) {
16554
+ fetchingBackward = false;
16555
+ reportStatusChange();
16556
+ runPostFetchActions();
16557
+ }
16346
16558
  }
16347
- runPostFetchActions();
16348
16559
  };
16349
16560
  const runPostFetchActions = () => {
16350
16561
  if (fetchBackwardLater) {
@@ -16369,11 +16580,16 @@ var createPaginationBuffer = (options) => {
16369
16580
  void checkIfThereAreMoreItemsForward();
16370
16581
  },
16371
16582
  clear: () => {
16583
+ abortController?.abort();
16584
+ abortController = void 0;
16585
+ fetchingForward = false;
16586
+ fetchingBackward = false;
16372
16587
  items = [];
16373
16588
  hasMoreBackward = void 0;
16374
16589
  nextBackwardToken = void 0;
16375
16590
  hasMoreForward = void 0;
16376
16591
  nextForwardToken = void 0;
16592
+ reportStatusChange();
16377
16593
  reportDataChange();
16378
16594
  },
16379
16595
  fetchBackward: async () => {
@@ -16609,9 +16825,9 @@ var createApiClient = ({ localstackEndpoint }) => {
16609
16825
  const endpoint = buildEventsEndpoint(iamEventsRequest);
16610
16826
  return makeRequest({ endpoint, localstackEndpoint });
16611
16827
  },
16612
- getSpans: async (request) => {
16828
+ getSpans: async (request, options) => {
16613
16829
  const endpoint = buildSpansEndpoint(request);
16614
- const response = await makeRequest({ endpoint, localstackEndpoint });
16830
+ const response = await makeRequest({ endpoint, localstackEndpoint, request: { signal: options?.signal } });
16615
16831
  return {
16616
16832
  ...response,
16617
16833
  spans: response.spans.map((span) => ({
@@ -18701,45 +18917,11 @@ var useSpans = () => {
18701
18917
  const [fetchError, setFetchError] = (0, import_react46.useState)();
18702
18918
  const [fetchingForward, setFetchingForward] = (0, import_react46.useState)(false);
18703
18919
  const [fetchingBackward, setFetchingBackward] = (0, import_react46.useState)(false);
18704
- const paginationBufferRef = (0, import_react46.useRef)(createPaginationBuffer({
18705
- async fetchPage(token) {
18706
- const response = await api.getSpans({
18707
- limit: PAGE_SIZE,
18708
- pagination_token: token
18709
- });
18710
- setTotalCount(response.pagination.total_count);
18711
- setLicenseLimit(response.limits.span_count_limit_license);
18712
- setSystemLimit(response.limits.span_count_limit_system);
18713
- setErrorCount(response.error_count);
18714
- setWarningCount(response.warning_count);
18715
- return {
18716
- hasMoreBackward: response.pagination.has_prev,
18717
- hasMoreForward: response.pagination.has_next,
18718
- items: response.spans,
18719
- nextBackwardToken: response.pagination.prev_cursor ?? void 0,
18720
- nextForwardToken: response.pagination.next_cursor ?? void 0
18721
- };
18722
- },
18723
- onDataChange: ({ hasMoreBackward: hasMoreBackward2, hasMoreForward: hasMoreForward2, items }) => {
18724
- setFetchError(void 0);
18725
- setSpans(items);
18726
- setHasMoreBackward(hasMoreBackward2);
18727
- setHasMoreForward(hasMoreForward2);
18728
- },
18729
- onError: (error) => {
18730
- setFetchError(error);
18731
- },
18732
- onStatusChange(status) {
18733
- setFetchingBackward(status.fetchingBackward);
18734
- setFetchingForward(status.fetchingForward);
18735
- },
18736
- // The spans endpoint is sorted by span ingestion time, which might differ from
18737
- // the span timestamp. In order to accomodate for this, we will sort the spans
18738
- // in the frontend with the following sort comparator.
18739
- sortComparator(a3, b3) {
18740
- return a3.start_time_unix_nano < b3.start_time_unix_nano ? -1 : 1;
18741
- }
18742
- }));
18920
+ const [search, setSearch] = (0, import_react46.useState)("");
18921
+ const searchRef = (0, import_react46.useRef)("");
18922
+ const [searching, setSearching] = (0, import_react46.useState)(false);
18923
+ const searchSeqRef = (0, import_react46.useRef)(0);
18924
+ const paginationBufferRef = (0, import_react46.useRef)();
18743
18925
  const [totalCount, setTotalCount] = (0, import_react46.useState)();
18744
18926
  const [licenseLimit, setLicenseLimit] = (0, import_react46.useState)();
18745
18927
  const [systemLimit, setSystemLimit] = (0, import_react46.useState)();
@@ -18748,14 +18930,71 @@ var useSpans = () => {
18748
18930
  const [hasMoreBackward, setHasMoreBackward] = (0, import_react46.useState)();
18749
18931
  const [hasMoreForward, setHasMoreForward] = (0, import_react46.useState)();
18750
18932
  const fetchForward = (0, import_react46.useCallback)(() => {
18751
- void paginationBufferRef.current.fetchForward();
18933
+ void paginationBufferRef.current?.fetchForward();
18752
18934
  }, []);
18753
18935
  const fetchBackward = (0, import_react46.useCallback)(() => {
18754
- void paginationBufferRef.current.fetchBackward();
18936
+ void paginationBufferRef.current?.fetchBackward();
18937
+ }, []);
18938
+ const applySearch = (0, import_react46.useCallback)((term) => {
18939
+ const normalized = term.trim();
18940
+ if (normalized === searchRef.current) {
18941
+ return;
18942
+ }
18943
+ searchRef.current = normalized;
18944
+ setSearch(normalized);
18945
+ const seq = ++searchSeqRef.current;
18946
+ setSearching(true);
18947
+ paginationBufferRef.current?.clear();
18948
+ void paginationBufferRef.current?.fetchForward().finally(() => {
18949
+ if (searchSeqRef.current === seq) {
18950
+ setSearching(false);
18951
+ }
18952
+ });
18755
18953
  }, []);
18756
18954
  (0, import_react46.useEffect)(() => {
18757
- fetchForward();
18758
- }, [fetchForward]);
18955
+ const buffer = createPaginationBuffer({
18956
+ async fetchPage(token, signal) {
18957
+ const response = await api.getSpans({
18958
+ limit: PAGE_SIZE,
18959
+ pagination_token: token,
18960
+ search: searchRef.current || void 0
18961
+ }, { signal });
18962
+ setTotalCount(response.pagination.total_count);
18963
+ setLicenseLimit(response.limits.span_count_limit_license);
18964
+ setSystemLimit(response.limits.span_count_limit_system);
18965
+ setErrorCount(response.error_count);
18966
+ setWarningCount(response.warning_count);
18967
+ return {
18968
+ hasMoreBackward: response.pagination.has_prev,
18969
+ hasMoreForward: response.pagination.has_next,
18970
+ items: response.spans,
18971
+ nextBackwardToken: response.pagination.prev_cursor ?? void 0,
18972
+ nextForwardToken: response.pagination.next_cursor ?? void 0
18973
+ };
18974
+ },
18975
+ onDataChange: ({ hasMoreBackward: hasMoreBackward2, hasMoreForward: hasMoreForward2, items }) => {
18976
+ setFetchError(void 0);
18977
+ setSpans(items);
18978
+ setHasMoreBackward(hasMoreBackward2);
18979
+ setHasMoreForward(hasMoreForward2);
18980
+ },
18981
+ onError: (error) => {
18982
+ setFetchError(error);
18983
+ },
18984
+ onStatusChange(status) {
18985
+ setFetchingBackward(status.fetchingBackward);
18986
+ setFetchingForward(status.fetchingForward);
18987
+ },
18988
+ // The spans endpoint is sorted by span ingestion time, which might differ from
18989
+ // the span timestamp. In order to accomodate for this, we will sort the spans
18990
+ // in the frontend with the following sort comparator.
18991
+ sortComparator(a3, b3) {
18992
+ return a3.start_time_unix_nano < b3.start_time_unix_nano ? -1 : 1;
18993
+ }
18994
+ });
18995
+ paginationBufferRef.current = buffer;
18996
+ void buffer.fetchForward();
18997
+ }, [api]);
18759
18998
  const [clearingSpans, setClearingSpans] = (0, import_react46.useState)(false);
18760
18999
  const clearSpans = (0, import_react46.useCallback)(async () => {
18761
19000
  if (clearingSpans) {
@@ -18765,8 +19004,11 @@ var useSpans = () => {
18765
19004
  try {
18766
19005
  await api.deleteSpans();
18767
19006
  setTotalCount(void 0);
18768
- paginationBufferRef.current.clear();
18769
- await paginationBufferRef.current.fetchForward();
19007
+ searchRef.current = "";
19008
+ setSearch("");
19009
+ setSearching(false);
19010
+ paginationBufferRef.current?.clear();
19011
+ await paginationBufferRef.current?.fetchForward();
18770
19012
  } catch (error) {
18771
19013
  console.error("Failed to delete spans:", error);
18772
19014
  setFetchError(error instanceof Error ? error : new Error("Failed to delete spans", { cause: error }));
@@ -18776,12 +19018,12 @@ var useSpans = () => {
18776
19018
  }, [clearingSpans, api]);
18777
19019
  (0, import_react46.useEffect)(() => {
18778
19020
  if (hasMoreForward === true && !fetchingForward) {
18779
- void paginationBufferRef.current.fetchForward();
19021
+ void paginationBufferRef.current?.fetchForward();
18780
19022
  }
18781
19023
  }, [hasMoreForward, fetchingForward]);
18782
19024
  (0, import_react46.useEffect)(() => {
18783
19025
  if (hasMoreBackward === true && !fetchingBackward && !fetchingForward) {
18784
- void paginationBufferRef.current.fetchBackward();
19026
+ void paginationBufferRef.current?.fetchBackward();
18785
19027
  }
18786
19028
  }, [hasMoreBackward, fetchingBackward, fetchingForward]);
18787
19029
  useSpansWebSocket({
@@ -18792,7 +19034,7 @@ var useSpans = () => {
18792
19034
  return;
18793
19035
  }
18794
19036
  if (!hasMoreForward) {
18795
- void paginationBufferRef.current.fetchForward();
19037
+ void paginationBufferRef.current?.fetchForward();
18796
19038
  }
18797
19039
  },
18798
19040
  onOpen: () => {
@@ -18816,6 +19058,9 @@ var useSpans = () => {
18816
19058
  hasMoreBackward,
18817
19059
  hasMoreForward,
18818
19060
  licenseLimit,
19061
+ search,
19062
+ searching,
19063
+ setSearch: applySearch,
18819
19064
  spans,
18820
19065
  systemLimit,
18821
19066
  totalCount,
@@ -19816,7 +20061,7 @@ var AddBox = "M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.
19816
20061
  var Check2 = "M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z";
19817
20062
  var ChevronRight = "M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z";
19818
20063
  var CircularArrows = "M 12 2 C 10.615 1.998 9.214625 2.2867656 7.890625 2.8847656 L 8.9003906 4.6328125 C 9.9043906 4.2098125 10.957 3.998 12 4 C 15.080783 4 17.738521 5.7633175 19.074219 8.3222656 L 17.125 9 L 21.25 11 L 22.875 7 L 20.998047 7.6523438 C 19.377701 4.3110398 15.95585 2 12 2 z M 6.5097656 4.4882812 L 2.2324219 5.0820312 L 3.734375 6.3808594 C 1.6515335 9.4550558 1.3615962 13.574578 3.3398438 17 C 4.0308437 18.201 4.9801562 19.268234 6.1601562 20.115234 L 7.1699219 18.367188 C 6.3019219 17.710187 5.5922656 16.904 5.0722656 16 C 3.5320014 13.332354 3.729203 10.148679 5.2773438 7.7128906 L 6.8398438 9.0625 L 6.5097656 4.4882812 z M 19.929688 13 C 19.794687 14.08 19.450734 15.098 18.927734 16 C 17.386985 18.668487 14.531361 20.090637 11.646484 19.966797 L 12.035156 17.9375 L 8.2402344 20.511719 L 10.892578 23.917969 L 11.265625 21.966797 C 14.968963 22.233766 18.681899 20.426323 20.660156 17 C 21.355156 15.801 21.805219 14.445 21.949219 13 L 19.929688 13 z";
19819
- var Close = "M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z";
20064
+ var Close2 = "M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z";
19820
20065
  var ContentCopy2 = "M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z";
19821
20066
  var Edit = "M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a.9959.9959 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z";
19822
20067
  var ExpandMore = "M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z";
@@ -19847,7 +20092,7 @@ var CircularArrowsIcon = (props) => {
19847
20092
  };
19848
20093
  var CloseIcon = (props) => {
19849
20094
  return /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(BaseIcon, {
19850
- d: Close,
20095
+ d: Close2,
19851
20096
  ...props
19852
20097
  });
19853
20098
  };
@@ -31532,14 +31777,16 @@ var ServiceNode = (0, import_react56.memo)(({ data, selected }) => {
31532
31777
  const theme = (0, import_material19.useTheme)();
31533
31778
  const [isHovered, setIsHovered] = (0, import_react56.useState)(false);
31534
31779
  const nodeRef = (0, import_react56.useRef)(null);
31535
- const isError = data.event.status_code === 2;
31536
- const hasPermissionEvents = (0, import_react56.useMemo)(
31780
+ const { operation_status } = data.event;
31781
+ const hasPermissionEventsLegacy = (0, import_react56.useMemo)(
31537
31782
  () => data.event.events.some(
31538
31783
  (event) => event.event_type === "iam.policy_evaluation" && event.name === "iam.policy.denied"
31539
31784
  ),
31540
31785
  [data.event.events]
31541
31786
  );
31542
- const isWarning = !isError && data.event.status_code === 1 && hasPermissionEvents;
31787
+ const isError = operation_status ? operation_status === "error" || operation_status === "iam_explicit_deny" || operation_status === "iam_implicit_deny" : data.event.status_code === 2;
31788
+ const isWarning = operation_status ? operation_status === "iam_explicit_warn" || operation_status === "iam_implicit_warn" : !isError && data.event.status_code === 1 && hasPermissionEventsLegacy;
31789
+ const hasPermissionEvents = operation_status ? operation_status.startsWith("iam_") : hasPermissionEventsLegacy;
31543
31790
  const outlineColor = isError ? theme.palette.error.main : isWarning ? theme.palette.warning.main : void 0;
31544
31791
  const sharedBoxSx = {
31545
31792
  backgroundColor: theme.palette.background.paper,
@@ -32095,6 +32342,9 @@ var SpansListPage = () => {
32095
32342
  hasMoreBackward,
32096
32343
  hasMoreForward,
32097
32344
  licenseLimit,
32345
+ search,
32346
+ searching,
32347
+ setSearch,
32098
32348
  spans,
32099
32349
  systemLimit,
32100
32350
  totalCount,
@@ -32106,6 +32356,7 @@ var SpansListPage = () => {
32106
32356
  }, [clearSpans]);
32107
32357
  const localstackVersion = status?.localstackVersion ?? (statusError instanceof AppInspectorNotFoundError ? statusError.localstackVersion : void 0);
32108
32358
  const versionCompatibility = checkEmulatorVersion(localstackVersion);
32359
+ const searchSupported = emulatorSupportsSearch(localstackVersion);
32109
32360
  const [bannerDismissed, setBannerDismissed] = (0, import_react63.useState)(false);
32110
32361
  const [order2, setOrder] = useLocalStorage("spans-order", "oldest_first");
32111
32362
  const [selectedTraceId, setSelectedTraceId] = (0, import_react63.useState)();
@@ -32222,8 +32473,12 @@ var SpansListPage = () => {
32222
32473
  onClearSpans: clearSpansSync,
32223
32474
  onFetchForward: fetchForward,
32224
32475
  onOrderChange: setOrder,
32476
+ onSearchChange: setSearch,
32225
32477
  onSpanClick: navigateToSpan,
32226
32478
  order: order2,
32479
+ search,
32480
+ searching,
32481
+ searchSupported,
32227
32482
  spans,
32228
32483
  systemLimit,
32229
32484
  totalCount,