@localstack/appinspector-ui 1.0.142 → 1.0.144

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 Box19 } from "@mui/material";
2425
- import { useCallback as useCallback11, useEffect as useEffect22, useRef as useRef16, useState as useState18 } from "react";
2425
+ import { useCallback as useCallback11, useEffect as useEffect22, useRef as useRef16, useState as useState19 } from "react";
2426
2426
 
2427
2427
  // src/api/errors.ts
2428
2428
  var AppInspectorDisabledError = class extends Error {
@@ -2476,6 +2476,9 @@ var SPANS_LIST_PAGE_TEST_ID = "spans-list-page";
2476
2476
  var TRACE_GRAPH_PAGE_TEST_ID = "trace-graph-page";
2477
2477
  var VIEW_SPAN_DETAILS_TEST_ID = "view-span-details";
2478
2478
  var SPANS_DATA_GRID_ROW_TEST_ID = "spans-data-grid-row";
2479
+ var SPANS_SEARCH_INPUT_TEST_ID = "spans-search-input";
2480
+ var SPANS_SEARCH_CLEAR_TEST_ID = "spans-search-clear";
2481
+ var SPANS_SEARCH_NO_MATCH_TEST_ID = "spans-search-no-match";
2479
2482
  var EVENT_DETAILS_SECTION_BASIC_INFORMATION_TEST_ID = "event-details-section-basic-information";
2480
2483
  var EVENT_DETAILS_SERVICE_NAME_TEST_ID = "event-details-service-name";
2481
2484
  var EVENT_DETAILS_OPERATION_NAME_TEST_ID = "event-details-operation-name";
@@ -2508,6 +2511,7 @@ var EMULATOR_VERSION_BANNER_TEST_ID = "emulator-version-banner";
2508
2511
  // src/utils/emulator-version.ts
2509
2512
  var import_semver = __toESM(require_semver2(), 1);
2510
2513
  var MINIMUM_EMULATOR_VERSION = "2026.4.0";
2514
+ var MIN_EMULATOR_VERSION_FOR_SEARCH = "2026.6.0";
2511
2515
  function checkEmulatorVersion(version, now = /* @__PURE__ */ new Date()) {
2512
2516
  if (!version) return "error";
2513
2517
  const coerced = import_semver.default.coerce(version);
@@ -2518,6 +2522,13 @@ function checkEmulatorVersion(version, now = /* @__PURE__ */ new Date()) {
2518
2522
  if (ageInMonths >= 3) return "warning";
2519
2523
  return "ok";
2520
2524
  }
2525
+ function emulatorSupportsSearch(version) {
2526
+ if (!version) return false;
2527
+ const coerced = import_semver.default.coerce(version);
2528
+ const minimum = import_semver.default.coerce(MIN_EMULATOR_VERSION_FOR_SEARCH);
2529
+ if (!coerced || !minimum) return false;
2530
+ return import_semver.default.gte(coerced, minimum);
2531
+ }
2521
2532
  function getVersionAgeInMonths(year, month, now = /* @__PURE__ */ new Date()) {
2522
2533
  const versionDate = new Date(year, month - 1, 1);
2523
2534
  return (now.getTime() - versionDate.getTime()) / (1e3 * 60 * 60 * 24 * 30.44);
@@ -2586,13 +2597,15 @@ var EmulatorVersionBanner = ({
2586
2597
  import { Alert as Alert2, Box as Box5, Button as Button3 } from "@mui/material";
2587
2598
 
2588
2599
  // src/components/spans-list/spans-data-grid.tsx
2589
- import { DeleteForeverOutlined } from "@mui/icons-material";
2600
+ import { Close, DeleteForeverOutlined, Search } from "@mui/icons-material";
2590
2601
  import {
2591
2602
  Box as Box4,
2592
2603
  Button as Button2,
2593
2604
  CircularProgress,
2594
2605
  FormControlLabel,
2595
2606
  Grid,
2607
+ IconButton as IconButton2,
2608
+ InputAdornment,
2596
2609
  Paper,
2597
2610
  Table,
2598
2611
  TableBody,
@@ -2601,6 +2614,7 @@ import {
2601
2614
  TableHead,
2602
2615
  TableRow,
2603
2616
  TableSortLabel,
2617
+ TextField,
2604
2618
  Tooltip as Tooltip4,
2605
2619
  Typography as Typography4,
2606
2620
  useTheme as useTheme2
@@ -13061,7 +13075,7 @@ var motion = /* @__PURE__ */ createMotionProxy(function(Component, config) {
13061
13075
  });
13062
13076
 
13063
13077
  // src/components/spans-list/spans-data-grid.tsx
13064
- import { memo as memo8, useCallback as useCallback2, useEffect as useEffect11, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef6 } from "react";
13078
+ import { memo as memo8, useCallback as useCallback2, useEffect as useEffect11, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef6, useState as useState5 } from "react";
13065
13079
 
13066
13080
  // src/components/cells/responsive-timestamp-cell.tsx
13067
13081
  import { memo as memo3 } from "react";
@@ -15370,7 +15384,7 @@ var StatRow = ({ label, value }) => /* @__PURE__ */ jsxs120(Box3, { sx: { alignI
15370
15384
  /* @__PURE__ */ jsx125(Typography3, { color: "text.secondary", sx: { mr: 4 }, variant: "caption", children: label }),
15371
15385
  /* @__PURE__ */ jsx125(Typography3, { fontWeight: 600, variant: "body2", children: fmtLimit(value) })
15372
15386
  ] });
15373
- var SpanCountBadge = ({ licenseLimit, systemLimit, totalCount, visibleCount }) => {
15387
+ var SpanCountBadge = ({ licenseLimit, searchActive = false, systemLimit, totalCount, visibleCount }) => {
15374
15388
  const [anchorElement, setAnchorElement] = useState4();
15375
15389
  const bindingLimit = resolveBindingLimit(systemLimit, licenseLimit);
15376
15390
  if (totalCount === void 0 && bindingLimit === void 0) {
@@ -15385,7 +15399,7 @@ var SpanCountBadge = ({ licenseLimit, systemLimit, totalCount, visibleCount }) =
15385
15399
  isLoading ? /* @__PURE__ */ jsx125(Skeleton, { sx: { minWidth: 120 } }) : /* @__PURE__ */ jsxs120(Box3, { sx: { alignItems: "baseline", display: "flex", gap: 0.5 }, children: [
15386
15400
  /* @__PURE__ */ jsx125(Typography3, { color: "text.secondary", variant: "caption", children: "Showing" }),
15387
15401
  /* @__PURE__ */ jsx125(Typography3, { variant: "body2", children: visibleCount === void 0 ? "\u2014" : fmt(visibleCount) }),
15388
- /* @__PURE__ */ jsx125(Typography3, { color: "text.secondary", variant: "caption", children: `of ${totalCount === void 0 ? "-" : fmt(totalCount)} operations` })
15402
+ /* @__PURE__ */ jsx125(Typography3, { color: "text.secondary", variant: "caption", children: `of ${totalCount === void 0 ? "-" : fmt(totalCount)} ${searchActive ? "matches" : "operations"}` })
15389
15403
  ] }),
15390
15404
  /* @__PURE__ */ jsx125(Tooltip3, { title: "Operation count details", children: /* @__PURE__ */ jsx125(
15391
15405
  IconButton,
@@ -15424,7 +15438,7 @@ var SpanCountBadge = ({ licenseLimit, systemLimit, totalCount, visibleCount }) =
15424
15438
  /* @__PURE__ */ jsx125(
15425
15439
  StatRow,
15426
15440
  {
15427
- label: /* @__PURE__ */ jsx125(Tooltip3, { placement: "left", title: "The total number of operations stored on the server.", children: /* @__PURE__ */ jsx125(Box3, { component: "span", sx: { borderBottom: "1px dashed", borderColor: "text.secondary", cursor: "help" }, children: "Total stored" }) }),
15441
+ label: /* @__PURE__ */ jsx125(Tooltip3, { placement: "left", title: searchActive ? "The total number of operations matching the search." : "The total number of operations stored on the server.", children: /* @__PURE__ */ jsx125(Box3, { component: "span", sx: { borderBottom: "1px dashed", borderColor: "text.secondary", cursor: "help" }, children: searchActive ? "Total matches" : "Total stored" }) }),
15428
15442
  value: totalCount
15429
15443
  }
15430
15444
  )
@@ -15491,9 +15505,55 @@ var newSpanBackgroundColor = {
15491
15505
  var LABEL_BEGINNING_OF_STREAM = "This is the beginning of the stream";
15492
15506
  var LABEL_END_OF_STREAM = "This is the end of the stream";
15493
15507
  var LABEL_NO_SPANS = "There are no operations";
15508
+ var SEARCH_MAX_LENGTH = 512;
15509
+ var SEARCH_DEBOUNCE_MS = 300;
15494
15510
  var STICK_TO_EDGE_THRESHOLD_PX = 4;
15495
15511
  var StreamBoundaryRow = ({ colSpan, label }) => /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(TableCell, { colSpan, sx: { fontStyle: "italic", textAlign: "center" }, children: /* @__PURE__ */ jsx126("span", { children: label }) }) });
15496
15512
  var LoadingRow = ({ colSpan }) => /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(TableCell, { colSpan, sx: { textAlign: "center" }, children: /* @__PURE__ */ jsx126(CircularProgress, { size: 24, sx: { m: 2 } }) }) });
15513
+ var NoMatchRow = ({
15514
+ colSpan,
15515
+ onClearSearch,
15516
+ term
15517
+ }) => /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(TableCell, { colSpan, "data-testid": SPANS_SEARCH_NO_MATCH_TEST_ID, sx: { textAlign: "center" }, children: /* @__PURE__ */ jsxs121(Box4, { sx: { alignItems: "center", display: "flex", flexDirection: "column", gap: 1, py: 3 }, children: [
15518
+ /* @__PURE__ */ jsx126(Typography4, { sx: { fontStyle: "italic" }, variant: "body2", children: `No operations match \u201C${term}\u201D` }),
15519
+ /* @__PURE__ */ jsx126(Button2, { onClick: onClearSearch, size: "small", variant: "text", children: "Clear search" })
15520
+ ] }) }) });
15521
+ var SpansSearchField = ({ onChange, onClear, searchSupported, value }) => {
15522
+ const field = /* @__PURE__ */ jsx126(
15523
+ TextField,
15524
+ {
15525
+ disabled: !searchSupported,
15526
+ inputProps: {
15527
+ "aria-label": "Search operations",
15528
+ "data-testid": SPANS_SEARCH_INPUT_TEST_ID,
15529
+ "maxLength": SEARCH_MAX_LENGTH
15530
+ },
15531
+ InputProps: {
15532
+ endAdornment: value ? /* @__PURE__ */ jsx126(InputAdornment, { position: "end", children: /* @__PURE__ */ jsx126(
15533
+ IconButton2,
15534
+ {
15535
+ "aria-label": "Clear search",
15536
+ "data-testid": SPANS_SEARCH_CLEAR_TEST_ID,
15537
+ edge: "end",
15538
+ onClick: onClear,
15539
+ size: "small",
15540
+ children: /* @__PURE__ */ jsx126(Close, { sx: { fontSize: 16 } })
15541
+ }
15542
+ ) }) : void 0,
15543
+ startAdornment: /* @__PURE__ */ jsx126(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx126(Search, { sx: { fontSize: 18 } }) })
15544
+ },
15545
+ onChange: (event) => {
15546
+ onChange(event.target.value);
15547
+ },
15548
+ placeholder: "Search operations\u2026",
15549
+ size: "small",
15550
+ sx: { minWidth: 260 },
15551
+ value
15552
+ }
15553
+ );
15554
+ if (searchSupported) return field;
15555
+ return /* @__PURE__ */ jsx126(Tooltip4, { title: `Search requires LocalStack \u2265 ${MIN_EMULATOR_VERSION_FOR_SEARCH}`, children: /* @__PURE__ */ jsx126("span", { children: field }) });
15556
+ };
15497
15557
  var SpansDataGridRow = memo8(
15498
15558
  ({ isNew, onSpanClick, row }) => {
15499
15559
  const theme = useTheme2();
@@ -15561,13 +15621,35 @@ var SpansDataGrid = ({
15561
15621
  licenseLimit,
15562
15622
  onClearSpans,
15563
15623
  onOrderChange,
15624
+ onSearchChange,
15564
15625
  onSpanClick,
15565
15626
  order: order2,
15627
+ search,
15628
+ searching,
15629
+ searchSupported,
15566
15630
  spans,
15567
15631
  systemLimit,
15568
15632
  totalCount,
15569
15633
  warningCount
15570
15634
  }) => {
15635
+ const searchActive = search.length > 0;
15636
+ const [searchDraft, setSearchDraft] = useState5(search);
15637
+ useEffect11(() => {
15638
+ const handle = setTimeout(() => {
15639
+ onSearchChange(searchDraft);
15640
+ }, SEARCH_DEBOUNCE_MS);
15641
+ return () => {
15642
+ clearTimeout(handle);
15643
+ };
15644
+ }, [searchDraft, onSearchChange]);
15645
+ const handleClearSearch = useCallback2(() => {
15646
+ setSearchDraft("");
15647
+ onSearchChange("");
15648
+ }, [onSearchChange]);
15649
+ const handleClearSpans = useCallback2(() => {
15650
+ setSearchDraft("");
15651
+ onClearSpans();
15652
+ }, [onClearSpans]);
15571
15653
  const columns = useMemo4(() => [
15572
15654
  columnHelper.accessor("startTime", {
15573
15655
  cell: (props) => /* @__PURE__ */ jsx126(ResponsiveTimestampCell, { date: props.getValue() }),
@@ -15590,7 +15672,13 @@ var SpansDataGrid = ({
15590
15672
  if (!props.row.original.parent_service_name || !props.row.original.parent_resource_name) {
15591
15673
  return /* @__PURE__ */ jsx126(Typography4, { sx: { fontStyle: "italic" }, variant: "body2", children: "External producer" });
15592
15674
  }
15593
- return /* @__PURE__ */ jsx126(ServiceCell, { resource: props.row.original.parent_resource_name, service: props.row.original.parent_service_name });
15675
+ return /* @__PURE__ */ jsx126(
15676
+ ServiceCell,
15677
+ {
15678
+ resource: props.row.original.parent_resource_name,
15679
+ service: props.row.original.parent_service_name
15680
+ }
15681
+ );
15594
15682
  },
15595
15683
  header: "Producer",
15596
15684
  id: "producer",
@@ -15701,14 +15789,40 @@ var SpansDataGrid = ({
15701
15789
  }, [order2, spans?.length, lastSpanId]);
15702
15790
  return /* @__PURE__ */ jsxs121(Fragment4, { children: [
15703
15791
  /* @__PURE__ */ jsx126(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsxs121(Box4, { sx: { alignItems: "center", display: "flex", gap: "0.5rem" }, children: [
15704
- /* @__PURE__ */ jsx126(SpanCountBadge, { licenseLimit, systemLimit, totalCount, visibleCount: spans?.length }),
15705
- errorCount !== void 0 && errorCount > 0 && /* @__PURE__ */ jsxs121(Fragment4, { children: [
15792
+ /* @__PURE__ */ jsx126(
15793
+ SpansSearchField,
15794
+ {
15795
+ onChange: setSearchDraft,
15796
+ onClear: handleClearSearch,
15797
+ searchSupported,
15798
+ value: searchDraft
15799
+ }
15800
+ ),
15801
+ /* @__PURE__ */ jsx126(
15802
+ SpanCountBadge,
15803
+ {
15804
+ licenseLimit,
15805
+ searchActive,
15806
+ systemLimit,
15807
+ totalCount,
15808
+ visibleCount: spans?.length
15809
+ }
15810
+ ),
15811
+ !searchActive && errorCount !== void 0 && errorCount > 0 && /* @__PURE__ */ jsxs121(Fragment4, { children: [
15706
15812
  /* @__PURE__ */ jsx126(Typography4, { color: "text.disabled", sx: { mr: 0.5 }, variant: "body2", children: "\u2014" }),
15707
15813
  /* @__PURE__ */ jsx126(Tooltip4, { title: "Number of errors detected across all operations", children: /* @__PURE__ */ jsxs121(
15708
15814
  Box4,
15709
15815
  {
15710
15816
  "data-testid": ERROR_COUNT_TEST_ID,
15711
- sx: { alignItems: "center", borderBottom: "1px dotted", borderColor: "error.main", cursor: "help", display: "flex", gap: 0.5, mb: "-1px" },
15817
+ sx: {
15818
+ alignItems: "center",
15819
+ borderBottom: "1px dotted",
15820
+ borderColor: "error.main",
15821
+ cursor: "help",
15822
+ display: "flex",
15823
+ gap: 0.5,
15824
+ mb: "-1px"
15825
+ },
15712
15826
  children: [
15713
15827
  /* @__PURE__ */ jsx126(Typography4, { color: "error", variant: "body2", children: errorCount.toLocaleString() }),
15714
15828
  /* @__PURE__ */ jsx126(Typography4, { color: "error", variant: "caption", children: errorCount === 1 ? "error" : "errors" })
@@ -15716,13 +15830,21 @@ var SpansDataGrid = ({
15716
15830
  }
15717
15831
  ) })
15718
15832
  ] }),
15719
- warningCount !== void 0 && warningCount > 0 && /* @__PURE__ */ jsxs121(Fragment4, { children: [
15833
+ !searchActive && warningCount !== void 0 && warningCount > 0 && /* @__PURE__ */ jsxs121(Fragment4, { children: [
15720
15834
  /* @__PURE__ */ jsx126(Typography4, { color: "text.disabled", sx: { mr: 0.5 }, variant: "body2", children: "\u2014" }),
15721
15835
  /* @__PURE__ */ jsx126(Tooltip4, { title: "Number of warnings detected across all operations", children: /* @__PURE__ */ jsxs121(
15722
15836
  Box4,
15723
15837
  {
15724
15838
  "data-testid": WARNING_COUNT_TEST_ID,
15725
- sx: { alignItems: "center", borderBottom: "1px dotted", borderColor: "warning.main", cursor: "help", display: "flex", gap: 0.5, mb: "-1px" },
15839
+ sx: {
15840
+ alignItems: "center",
15841
+ borderBottom: "1px dotted",
15842
+ borderColor: "warning.main",
15843
+ cursor: "help",
15844
+ display: "flex",
15845
+ gap: 0.5,
15846
+ mb: "-1px"
15847
+ },
15726
15848
  children: [
15727
15849
  /* @__PURE__ */ jsx126(Typography4, { color: "warning.main", variant: "body2", children: warningCount.toLocaleString() }),
15728
15850
  /* @__PURE__ */ jsx126(Typography4, { color: "warning.main", variant: "caption", children: warningCount === 1 ? "warning" : "warnings" })
@@ -15738,7 +15860,7 @@ var SpansDataGrid = ({
15738
15860
  Button2,
15739
15861
  {
15740
15862
  "data-testid": CLEAR_SPANS_TEST_ID,
15741
- onClick: onClearSpans,
15863
+ onClick: handleClearSpans,
15742
15864
  startIcon: /* @__PURE__ */ jsx126(DeleteForeverOutlined, {}),
15743
15865
  children: "Clear Operations"
15744
15866
  }
@@ -15791,31 +15913,71 @@ var SpansDataGrid = ({
15791
15913
  )) }, headerGroup.id);
15792
15914
  }) }),
15793
15915
  /* @__PURE__ */ jsxs121(TableBody, { children: [
15794
- (spans === void 0 || clearingSpans) && /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(TableCell, { colSpan: columnCount, sx: { fontStyle: "italic", textAlign: "center" }, children: /* @__PURE__ */ jsx126(CircularProgress, { size: 48, sx: { m: 4 } }) }) }),
15795
- !clearingSpans && spans?.length === 0 && /* @__PURE__ */ jsx126(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_NO_SPANS }),
15916
+ (spans === void 0 || clearingSpans || searching) && /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(TableCell, { colSpan: columnCount, sx: { fontStyle: "italic", textAlign: "center" }, children: /* @__PURE__ */ jsx126(CircularProgress, { size: 48, sx: { m: 4 } }) }) }),
15917
+ !clearingSpans && !searching && spans?.length === 0 && (searchActive ? /* @__PURE__ */ jsx126(NoMatchRow, { colSpan: columnCount, onClearSearch: handleClearSearch, term: search }) : /* @__PURE__ */ jsx126(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_NO_SPANS })),
15796
15918
  !clearingSpans && order2 === "newest_first" && /* @__PURE__ */ jsxs121(Fragment4, { children: [
15797
15919
  hasRows && hasMoreForward && fetchingForward && /* @__PURE__ */ jsx126(LoadingRow, { colSpan: columnCount }),
15798
15920
  hasRows && !hasMoreForward && /* @__PURE__ */ jsx126(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_END_OF_STREAM }),
15799
- hasRows && paddingTop > 0 && /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(TableCell, { colSpan: columnCount, sx: { border: 0, height: paddingTop, p: 0 } }) }),
15921
+ hasRows && paddingTop > 0 && /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(
15922
+ TableCell,
15923
+ {
15924
+ colSpan: columnCount,
15925
+ sx: { border: 0, height: paddingTop, p: 0 }
15926
+ }
15927
+ ) }),
15800
15928
  hasRows && virtualItems.map((virtualItem) => {
15801
15929
  const row = displayRows[virtualItem.index];
15802
15930
  const isNew = !(seenSpanIdsRef.current?.has(row.original.span_id) ?? false);
15803
- return /* @__PURE__ */ jsx126(SpansDataGridRow, { isNew, onSpanClick, row }, row.original.span_id);
15931
+ return /* @__PURE__ */ jsx126(
15932
+ SpansDataGridRow,
15933
+ {
15934
+ isNew,
15935
+ onSpanClick,
15936
+ row
15937
+ },
15938
+ row.original.span_id
15939
+ );
15804
15940
  }),
15805
- hasRows && paddingBottom > 0 && /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(TableCell, { colSpan: columnCount, sx: { border: 0, height: paddingBottom, p: 0 } }) }),
15941
+ hasRows && paddingBottom > 0 && /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(
15942
+ TableCell,
15943
+ {
15944
+ colSpan: columnCount,
15945
+ sx: { border: 0, height: paddingBottom, p: 0 }
15946
+ }
15947
+ ) }),
15806
15948
  hasRows && hasMoreBackward && fetchingBackward && /* @__PURE__ */ jsx126(LoadingRow, { colSpan: columnCount }),
15807
15949
  hasRows && !hasMoreBackward && /* @__PURE__ */ jsx126(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_BEGINNING_OF_STREAM })
15808
15950
  ] }),
15809
15951
  !clearingSpans && order2 === "oldest_first" && /* @__PURE__ */ jsxs121(Fragment4, { children: [
15810
15952
  hasRows && !hasMoreBackward && /* @__PURE__ */ jsx126(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_BEGINNING_OF_STREAM }),
15811
15953
  hasRows && hasMoreBackward && fetchingBackward && /* @__PURE__ */ jsx126(LoadingRow, { colSpan: columnCount }),
15812
- hasRows && paddingTop > 0 && /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(TableCell, { colSpan: columnCount, sx: { border: 0, height: paddingTop, p: 0 } }) }),
15954
+ hasRows && paddingTop > 0 && /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(
15955
+ TableCell,
15956
+ {
15957
+ colSpan: columnCount,
15958
+ sx: { border: 0, height: paddingTop, p: 0 }
15959
+ }
15960
+ ) }),
15813
15961
  hasRows && virtualItems.map((virtualItem) => {
15814
15962
  const row = displayRows[virtualItem.index];
15815
15963
  const isNew = !(seenSpanIdsRef.current?.has(row.original.span_id) ?? false);
15816
- return /* @__PURE__ */ jsx126(SpansDataGridRow, { isNew, onSpanClick, row }, row.original.span_id);
15964
+ return /* @__PURE__ */ jsx126(
15965
+ SpansDataGridRow,
15966
+ {
15967
+ isNew,
15968
+ onSpanClick,
15969
+ row
15970
+ },
15971
+ row.original.span_id
15972
+ );
15817
15973
  }),
15818
- hasRows && paddingBottom > 0 && /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(TableCell, { colSpan: columnCount, sx: { border: 0, height: paddingBottom, p: 0 } }) }),
15974
+ hasRows && paddingBottom > 0 && /* @__PURE__ */ jsx126(TableRow, { children: /* @__PURE__ */ jsx126(
15975
+ TableCell,
15976
+ {
15977
+ colSpan: columnCount,
15978
+ sx: { border: 0, height: paddingBottom, p: 0 }
15979
+ }
15980
+ ) }),
15819
15981
  hasRows && hasMoreForward && fetchingForward && /* @__PURE__ */ jsx126(LoadingRow, { colSpan: columnCount }),
15820
15982
  hasRows && !hasMoreForward && /* @__PURE__ */ jsx126(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_END_OF_STREAM })
15821
15983
  ] })
@@ -15856,8 +16018,12 @@ var SpansList = (props) => {
15856
16018
  licenseLimit: props.licenseLimit,
15857
16019
  onClearSpans: props.onClearSpans,
15858
16020
  onOrderChange: props.onOrderChange,
16021
+ onSearchChange: props.onSearchChange,
15859
16022
  onSpanClick: props.onSpanClick,
15860
16023
  order: props.order,
16024
+ search: props.search,
16025
+ searching: props.searching,
16026
+ searchSupported: props.searchSupported,
15861
16027
  spans: props.spans,
15862
16028
  systemLimit: props.systemLimit,
15863
16029
  totalCount: props.totalCount,
@@ -15870,7 +16036,7 @@ var SpansList = (props) => {
15870
16036
  // src/components/status-message/status-message.tsx
15871
16037
  import { ErrorOutline, InfoOutlined as InfoOutlined2 } from "@mui/icons-material";
15872
16038
  import { Alert as Alert3, AlertTitle, Box as Box6, Button as Button4, CircularProgress as CircularProgress2, Link, Typography as Typography5 } from "@mui/material";
15873
- import { useState as useState5 } from "react";
16039
+ import { useState as useState6 } from "react";
15874
16040
  import { Fragment as Fragment5, jsx as jsx128, jsxs as jsxs123 } from "react/jsx-runtime";
15875
16041
  var StatusMessage = ({
15876
16042
  localstackVersion,
@@ -15878,8 +16044,8 @@ var StatusMessage = ({
15878
16044
  onRetry,
15879
16045
  phase
15880
16046
  }) => {
15881
- const [enabling, setEnabling] = useState5(false);
15882
- const [enableError, setEnableError] = useState5();
16047
+ const [enabling, setEnabling] = useState6(false);
16048
+ const [enableError, setEnableError] = useState6();
15883
16049
  const handleEnable = async () => {
15884
16050
  if (!onEnable) return;
15885
16051
  setEnabling(true);
@@ -16082,14 +16248,14 @@ var StatusMessage = ({
16082
16248
  import { createContext as createContext8, useContext as useContext14 } from "react";
16083
16249
 
16084
16250
  // src/hooks/use-connection-state-machine.ts
16085
- import { useCallback as useCallback3, useEffect as useEffect12, useRef as useRef7, useState as useState6 } from "react";
16251
+ import { useCallback as useCallback3, useEffect as useEffect12, useRef as useRef7, useState as useState7 } from "react";
16086
16252
  var POLL_INTERVAL_MS = 5e3;
16087
16253
  function useConnectionStateMachine() {
16088
16254
  const api = useAppInspectorApi();
16089
- const [phase, setPhase] = useState6("CHECKING_STATUS");
16090
- const [checking, setChecking] = useState6(true);
16091
- const [status, setStatus] = useState6();
16092
- const [statusError, setStatusError] = useState6();
16255
+ const [phase, setPhase] = useState7("CHECKING_STATUS");
16256
+ const [checking, setChecking] = useState7(true);
16257
+ const [status, setStatus] = useState7();
16258
+ const [statusError, setStatusError] = useState7();
16093
16259
  const abortControllerRef = useRef7(null);
16094
16260
  const dispatchStatus = useCallback3((result, error) => {
16095
16261
  if (result === void 0) {
@@ -16179,9 +16345,9 @@ var useAppInspectorStatus = () => {
16179
16345
  };
16180
16346
 
16181
16347
  // src/hooks/use-local-storage.tsx
16182
- import { useCallback as useCallback4, useEffect as useEffect13, useState as useState7 } from "react";
16348
+ import { useCallback as useCallback4, useEffect as useEffect13, useState as useState8 } from "react";
16183
16349
  function useLocalStorage(key, initialValue) {
16184
- const [storedValue, setStoredValue] = useState7(() => {
16350
+ const [storedValue, setStoredValue] = useState8(() => {
16185
16351
  try {
16186
16352
  const item = globalThis.localStorage.getItem(key);
16187
16353
  return item ? JSON.parse(item) : initialValue;
@@ -16224,7 +16390,7 @@ function useLocalStorage(key, initialValue) {
16224
16390
  }
16225
16391
 
16226
16392
  // src/hooks/use-spans.ts
16227
- import { useCallback as useCallback7, useEffect as useEffect16, useRef as useRef10, useState as useState9 } from "react";
16393
+ import { useCallback as useCallback7, useEffect as useEffect16, useRef as useRef10, useState as useState10 } from "react";
16228
16394
 
16229
16395
  // src/utils/pagination-buffer.ts
16230
16396
  var insertSorted = (array, item, compareFunction, hint = "back") => {
@@ -16268,6 +16434,7 @@ var createPaginationBuffer = (options) => {
16268
16434
  let hasMoreBackward = void 0;
16269
16435
  let fetchingForward = false;
16270
16436
  let fetchingBackward = false;
16437
+ let abortController;
16271
16438
  const reportDataChange = () => {
16272
16439
  options.onDataChange({
16273
16440
  hasMoreBackward: hasMoreBackward ?? false,
@@ -16291,25 +16458,40 @@ var createPaginationBuffer = (options) => {
16291
16458
  if (hasMoreForward === true) {
16292
16459
  return;
16293
16460
  }
16461
+ const controller = new AbortController();
16462
+ abortController = controller;
16294
16463
  fetchingForward = true;
16295
16464
  reportStatusChange();
16296
16465
  try {
16297
- const newPage = await fetchPage(nextForwardToken);
16466
+ const newPage = await fetchPage(nextForwardToken, controller.signal);
16467
+ if (controller.signal.aborted) {
16468
+ return;
16469
+ }
16298
16470
  hasMoreForward = newPage.items.length > 0;
16299
16471
  reportDataChange();
16300
16472
  } catch (error) {
16473
+ if (controller.signal.aborted) {
16474
+ return;
16475
+ }
16301
16476
  reportError(error instanceof Error ? error : new Error("Failed to fetch forward page", { cause: error }));
16302
16477
  } finally {
16303
- fetchingForward = false;
16304
- reportStatusChange();
16478
+ if (!controller.signal.aborted) {
16479
+ fetchingForward = false;
16480
+ reportStatusChange();
16481
+ runPostFetchActions();
16482
+ }
16305
16483
  }
16306
- runPostFetchActions();
16307
16484
  };
16308
16485
  const fetchForward = async () => {
16486
+ const controller = new AbortController();
16487
+ abortController = controller;
16309
16488
  fetchingForward = true;
16310
16489
  reportStatusChange();
16311
16490
  try {
16312
- const newPage = await fetchPage(nextForwardToken);
16491
+ const newPage = await fetchPage(nextForwardToken, controller.signal);
16492
+ if (controller.signal.aborted) {
16493
+ return;
16494
+ }
16313
16495
  if (options.sortComparator === void 0) {
16314
16496
  items = [...items ?? [], ...newPage.items];
16315
16497
  } else {
@@ -16325,18 +16507,28 @@ var createPaginationBuffer = (options) => {
16325
16507
  nextBackwardToken ??= newPage.nextBackwardToken;
16326
16508
  reportDataChange();
16327
16509
  } catch (error) {
16510
+ if (controller.signal.aborted) {
16511
+ return;
16512
+ }
16328
16513
  reportError(error instanceof Error ? error : new Error("Failed to fetch forward page", { cause: error }));
16329
16514
  } finally {
16330
- fetchingForward = false;
16331
- reportStatusChange();
16515
+ if (!controller.signal.aborted) {
16516
+ fetchingForward = false;
16517
+ reportStatusChange();
16518
+ runPostFetchActions();
16519
+ }
16332
16520
  }
16333
- runPostFetchActions();
16334
16521
  };
16335
16522
  const fetchBackward = async () => {
16523
+ const controller = new AbortController();
16524
+ abortController = controller;
16336
16525
  fetchingBackward = true;
16337
16526
  reportStatusChange();
16338
16527
  try {
16339
- const newPage = await fetchPage(nextBackwardToken);
16528
+ const newPage = await fetchPage(nextBackwardToken, controller.signal);
16529
+ if (controller.signal.aborted) {
16530
+ return;
16531
+ }
16340
16532
  if (options.sortComparator === void 0) {
16341
16533
  items = [...newPage.items, ...items ?? []];
16342
16534
  } else {
@@ -16352,12 +16544,17 @@ var createPaginationBuffer = (options) => {
16352
16544
  nextForwardToken ??= newPage.nextForwardToken;
16353
16545
  reportDataChange();
16354
16546
  } catch (error) {
16547
+ if (controller.signal.aborted) {
16548
+ return;
16549
+ }
16355
16550
  reportError(error instanceof Error ? error : new Error("Failed to fetch forward page", { cause: error }));
16356
16551
  } finally {
16357
- fetchingBackward = false;
16358
- reportStatusChange();
16552
+ if (!controller.signal.aborted) {
16553
+ fetchingBackward = false;
16554
+ reportStatusChange();
16555
+ runPostFetchActions();
16556
+ }
16359
16557
  }
16360
- runPostFetchActions();
16361
16558
  };
16362
16559
  const runPostFetchActions = () => {
16363
16560
  if (fetchBackwardLater) {
@@ -16382,11 +16579,16 @@ var createPaginationBuffer = (options) => {
16382
16579
  void checkIfThereAreMoreItemsForward();
16383
16580
  },
16384
16581
  clear: () => {
16582
+ abortController?.abort();
16583
+ abortController = void 0;
16584
+ fetchingForward = false;
16585
+ fetchingBackward = false;
16385
16586
  items = [];
16386
16587
  hasMoreBackward = void 0;
16387
16588
  nextBackwardToken = void 0;
16388
16589
  hasMoreForward = void 0;
16389
16590
  nextForwardToken = void 0;
16591
+ reportStatusChange();
16390
16592
  reportDataChange();
16391
16593
  },
16392
16594
  fetchBackward: async () => {
@@ -16405,7 +16607,7 @@ var createPaginationBuffer = (options) => {
16405
16607
  };
16406
16608
 
16407
16609
  // src/hooks/use-spans-ws.tsx
16408
- import { useCallback as useCallback6, useEffect as useEffect15, useRef as useRef9, useState as useState8 } from "react";
16610
+ import { useCallback as useCallback6, useEffect as useEffect15, useRef as useRef9, useState as useState9 } from "react";
16409
16611
 
16410
16612
  // src/config.ts
16411
16613
  var APPINSPECTOR_API_PREFIX = "/_localstack/appinspector";
@@ -16622,9 +16824,9 @@ var createApiClient = ({ localstackEndpoint }) => {
16622
16824
  const endpoint = buildEventsEndpoint(iamEventsRequest);
16623
16825
  return makeRequest({ endpoint, localstackEndpoint });
16624
16826
  },
16625
- getSpans: async (request) => {
16827
+ getSpans: async (request, options) => {
16626
16828
  const endpoint = buildSpansEndpoint(request);
16627
- const response = await makeRequest({ endpoint, localstackEndpoint });
16829
+ const response = await makeRequest({ endpoint, localstackEndpoint, request: { signal: options?.signal } });
16628
16830
  return {
16629
16831
  ...response,
16630
16832
  spans: response.spans.map((span) => ({
@@ -18649,7 +18851,7 @@ function useSpansWebSocket(options) {
18649
18851
  useEffect15(() => {
18650
18852
  onCloseRef.current = onClose;
18651
18853
  }, [onClose]);
18652
- const [connected, setConnected] = useState8(false);
18854
+ const [connected, setConnected] = useState9(false);
18653
18855
  const wsRef = useRef9(void 0);
18654
18856
  const { localstackEndpoint } = useAppInspector();
18655
18857
  const connect = useCallback6(() => {
@@ -18710,66 +18912,89 @@ var PAGE_SIZE = 100;
18710
18912
  var useSpans = () => {
18711
18913
  const api = useAppInspectorApi();
18712
18914
  const { onWsClose, onWsOpen, wsEnabled } = useAppInspectorStatus();
18713
- const [spans, setSpans] = useState9();
18714
- const [fetchError, setFetchError] = useState9();
18715
- const [fetchingForward, setFetchingForward] = useState9(false);
18716
- const [fetchingBackward, setFetchingBackward] = useState9(false);
18717
- const paginationBufferRef = useRef10(createPaginationBuffer({
18718
- async fetchPage(token) {
18719
- const response = await api.getSpans({
18720
- limit: PAGE_SIZE,
18721
- pagination_token: token
18722
- });
18723
- setTotalCount(response.pagination.total_count);
18724
- setLicenseLimit(response.limits.span_count_limit_license);
18725
- setSystemLimit(response.limits.span_count_limit_system);
18726
- setErrorCount(response.error_count);
18727
- setWarningCount(response.warning_count);
18728
- return {
18729
- hasMoreBackward: response.pagination.has_prev,
18730
- hasMoreForward: response.pagination.has_next,
18731
- items: response.spans,
18732
- nextBackwardToken: response.pagination.prev_cursor ?? void 0,
18733
- nextForwardToken: response.pagination.next_cursor ?? void 0
18734
- };
18735
- },
18736
- onDataChange: ({ hasMoreBackward: hasMoreBackward2, hasMoreForward: hasMoreForward2, items }) => {
18737
- setFetchError(void 0);
18738
- setSpans(items);
18739
- setHasMoreBackward(hasMoreBackward2);
18740
- setHasMoreForward(hasMoreForward2);
18741
- },
18742
- onError: (error) => {
18743
- setFetchError(error);
18744
- },
18745
- onStatusChange(status) {
18746
- setFetchingBackward(status.fetchingBackward);
18747
- setFetchingForward(status.fetchingForward);
18748
- },
18749
- // The spans endpoint is sorted by span ingestion time, which might differ from
18750
- // the span timestamp. In order to accomodate for this, we will sort the spans
18751
- // in the frontend with the following sort comparator.
18752
- sortComparator(a3, b3) {
18753
- return a3.start_time_unix_nano < b3.start_time_unix_nano ? -1 : 1;
18754
- }
18755
- }));
18756
- const [totalCount, setTotalCount] = useState9();
18757
- const [licenseLimit, setLicenseLimit] = useState9();
18758
- const [systemLimit, setSystemLimit] = useState9();
18759
- const [errorCount, setErrorCount] = useState9();
18760
- const [warningCount, setWarningCount] = useState9();
18761
- const [hasMoreBackward, setHasMoreBackward] = useState9();
18762
- const [hasMoreForward, setHasMoreForward] = useState9();
18915
+ const [spans, setSpans] = useState10();
18916
+ const [fetchError, setFetchError] = useState10();
18917
+ const [fetchingForward, setFetchingForward] = useState10(false);
18918
+ const [fetchingBackward, setFetchingBackward] = useState10(false);
18919
+ const [search, setSearch] = useState10("");
18920
+ const searchRef = useRef10("");
18921
+ const [searching, setSearching] = useState10(false);
18922
+ const searchSeqRef = useRef10(0);
18923
+ const paginationBufferRef = useRef10();
18924
+ const [totalCount, setTotalCount] = useState10();
18925
+ const [licenseLimit, setLicenseLimit] = useState10();
18926
+ const [systemLimit, setSystemLimit] = useState10();
18927
+ const [errorCount, setErrorCount] = useState10();
18928
+ const [warningCount, setWarningCount] = useState10();
18929
+ const [hasMoreBackward, setHasMoreBackward] = useState10();
18930
+ const [hasMoreForward, setHasMoreForward] = useState10();
18763
18931
  const fetchForward = useCallback7(() => {
18764
- void paginationBufferRef.current.fetchForward();
18932
+ void paginationBufferRef.current?.fetchForward();
18765
18933
  }, []);
18766
18934
  const fetchBackward = useCallback7(() => {
18767
- void paginationBufferRef.current.fetchBackward();
18935
+ void paginationBufferRef.current?.fetchBackward();
18936
+ }, []);
18937
+ const applySearch = useCallback7((term) => {
18938
+ const normalized = term.trim();
18939
+ if (normalized === searchRef.current) {
18940
+ return;
18941
+ }
18942
+ searchRef.current = normalized;
18943
+ setSearch(normalized);
18944
+ const seq = ++searchSeqRef.current;
18945
+ setSearching(true);
18946
+ paginationBufferRef.current?.clear();
18947
+ void paginationBufferRef.current?.fetchForward().finally(() => {
18948
+ if (searchSeqRef.current === seq) {
18949
+ setSearching(false);
18950
+ }
18951
+ });
18768
18952
  }, []);
18769
18953
  useEffect16(() => {
18770
- fetchForward();
18771
- }, [fetchForward]);
18772
- const [clearingSpans, setClearingSpans] = useState9(false);
18954
+ const buffer = createPaginationBuffer({
18955
+ async fetchPage(token, signal) {
18956
+ const response = await api.getSpans({
18957
+ limit: PAGE_SIZE,
18958
+ pagination_token: token,
18959
+ search: searchRef.current || void 0
18960
+ }, { signal });
18961
+ setTotalCount(response.pagination.total_count);
18962
+ setLicenseLimit(response.limits.span_count_limit_license);
18963
+ setSystemLimit(response.limits.span_count_limit_system);
18964
+ setErrorCount(response.error_count);
18965
+ setWarningCount(response.warning_count);
18966
+ return {
18967
+ hasMoreBackward: response.pagination.has_prev,
18968
+ hasMoreForward: response.pagination.has_next,
18969
+ items: response.spans,
18970
+ nextBackwardToken: response.pagination.prev_cursor ?? void 0,
18971
+ nextForwardToken: response.pagination.next_cursor ?? void 0
18972
+ };
18973
+ },
18974
+ onDataChange: ({ hasMoreBackward: hasMoreBackward2, hasMoreForward: hasMoreForward2, items }) => {
18975
+ setFetchError(void 0);
18976
+ setSpans(items);
18977
+ setHasMoreBackward(hasMoreBackward2);
18978
+ setHasMoreForward(hasMoreForward2);
18979
+ },
18980
+ onError: (error) => {
18981
+ setFetchError(error);
18982
+ },
18983
+ onStatusChange(status) {
18984
+ setFetchingBackward(status.fetchingBackward);
18985
+ setFetchingForward(status.fetchingForward);
18986
+ },
18987
+ // The spans endpoint is sorted by span ingestion time, which might differ from
18988
+ // the span timestamp. In order to accomodate for this, we will sort the spans
18989
+ // in the frontend with the following sort comparator.
18990
+ sortComparator(a3, b3) {
18991
+ return a3.start_time_unix_nano < b3.start_time_unix_nano ? -1 : 1;
18992
+ }
18993
+ });
18994
+ paginationBufferRef.current = buffer;
18995
+ void buffer.fetchForward();
18996
+ }, [api]);
18997
+ const [clearingSpans, setClearingSpans] = useState10(false);
18773
18998
  const clearSpans = useCallback7(async () => {
18774
18999
  if (clearingSpans) {
18775
19000
  return;
@@ -18778,8 +19003,11 @@ var useSpans = () => {
18778
19003
  try {
18779
19004
  await api.deleteSpans();
18780
19005
  setTotalCount(void 0);
18781
- paginationBufferRef.current.clear();
18782
- await paginationBufferRef.current.fetchForward();
19006
+ searchRef.current = "";
19007
+ setSearch("");
19008
+ setSearching(false);
19009
+ paginationBufferRef.current?.clear();
19010
+ await paginationBufferRef.current?.fetchForward();
18783
19011
  } catch (error) {
18784
19012
  console.error("Failed to delete spans:", error);
18785
19013
  setFetchError(error instanceof Error ? error : new Error("Failed to delete spans", { cause: error }));
@@ -18789,12 +19017,12 @@ var useSpans = () => {
18789
19017
  }, [clearingSpans, api]);
18790
19018
  useEffect16(() => {
18791
19019
  if (hasMoreForward === true && !fetchingForward) {
18792
- void paginationBufferRef.current.fetchForward();
19020
+ void paginationBufferRef.current?.fetchForward();
18793
19021
  }
18794
19022
  }, [hasMoreForward, fetchingForward]);
18795
19023
  useEffect16(() => {
18796
19024
  if (hasMoreBackward === true && !fetchingBackward && !fetchingForward) {
18797
- void paginationBufferRef.current.fetchBackward();
19025
+ void paginationBufferRef.current?.fetchBackward();
18798
19026
  }
18799
19027
  }, [hasMoreBackward, fetchingBackward, fetchingForward]);
18800
19028
  useSpansWebSocket({
@@ -18805,7 +19033,7 @@ var useSpans = () => {
18805
19033
  return;
18806
19034
  }
18807
19035
  if (!hasMoreForward) {
18808
- void paginationBufferRef.current.fetchForward();
19036
+ void paginationBufferRef.current?.fetchForward();
18809
19037
  }
18810
19038
  },
18811
19039
  onOpen: () => {
@@ -18829,6 +19057,9 @@ var useSpans = () => {
18829
19057
  hasMoreBackward,
18830
19058
  hasMoreForward,
18831
19059
  licenseLimit,
19060
+ search,
19061
+ searching,
19062
+ setSearch: applySearch,
18832
19063
  spans,
18833
19064
  systemLimit,
18834
19065
  totalCount,
@@ -18841,12 +19072,12 @@ import { ArrowBack } from "@mui/icons-material";
18841
19072
  import { Box as Box18, Button as Button6, Drawer, Paper as Paper3 } from "@mui/material";
18842
19073
  import { styled } from "@mui/material/styles";
18843
19074
  import { ReactFlowProvider } from "@xyflow/react";
18844
- import { useCallback as useCallback10, useEffect as useEffect21, useRef as useRef15, useState as useState17 } from "react";
19075
+ import { useCallback as useCallback10, useEffect as useEffect21, useRef as useRef15, useState as useState18 } from "react";
18845
19076
 
18846
19077
  // src/components/event-details/event-details.tsx
18847
- import { Close as Close2 } from "@mui/icons-material";
18848
- import { Alert as Alert4, Box as Box15, Chip as Chip3, Divider as Divider3, FormControlLabel as FormControlLabel2, IconButton as IconButton4, Stack as Stack4, Switch, Typography as Typography11 } from "@mui/material";
18849
- import { useMemo as useMemo10, useState as useState15 } from "react";
19078
+ import { Close as Close3 } from "@mui/icons-material";
19079
+ import { Alert as Alert4, Box as Box15, Chip as Chip3, Divider as Divider3, FormControlLabel as FormControlLabel2, IconButton as IconButton5, Stack as Stack4, Switch, Typography as Typography11 } from "@mui/material";
19080
+ import { useMemo as useMemo10, useState as useState16 } from "react";
18850
19081
 
18851
19082
  // src/utils/event-utils.ts
18852
19083
  var iamEventParser = (eventName, attributes) => {
@@ -18958,11 +19189,11 @@ var getEventGroupsByType = (events) => {
18958
19189
 
18959
19190
  // src/components/event-details/copy-button.tsx
18960
19191
  import { Check, ContentCopy } from "@mui/icons-material";
18961
- import { IconButton as IconButton2, Tooltip as Tooltip5 } from "@mui/material";
18962
- import { useCallback as useCallback8, useState as useState10 } from "react";
19192
+ import { IconButton as IconButton3, Tooltip as Tooltip5 } from "@mui/material";
19193
+ import { useCallback as useCallback8, useState as useState11 } from "react";
18963
19194
  import { jsx as jsx131 } from "react/jsx-runtime";
18964
19195
  var CopyButton = ({ value }) => {
18965
- const [copied, setCopied] = useState10(false);
19196
+ const [copied, setCopied] = useState11(false);
18966
19197
  const handleCopy = useCallback8(() => {
18967
19198
  const text2 = typeof value === "string" ? value : JSON.stringify(value, void 0, 2);
18968
19199
  void navigator.clipboard.writeText(text2).then(() => {
@@ -18973,7 +19204,7 @@ var CopyButton = ({ value }) => {
18973
19204
  });
18974
19205
  }, [value]);
18975
19206
  return /* @__PURE__ */ jsx131(Tooltip5, { title: copied ? "Copied!" : "Copy", children: /* @__PURE__ */ jsx131(
18976
- IconButton2,
19207
+ IconButton3,
18977
19208
  {
18978
19209
  className: "copy-btn",
18979
19210
  onClick: handleCopy,
@@ -18986,8 +19217,8 @@ var CopyButton = ({ value }) => {
18986
19217
 
18987
19218
  // src/components/event-details/event-detail.tsx
18988
19219
  import { KeyboardArrowDown, KeyboardArrowRight } from "@mui/icons-material";
18989
- import { Box as Box12, IconButton as IconButton3, Typography as Typography8 } from "@mui/material";
18990
- import { useEffect as useEffect18, useMemo as useMemo8, useRef as useRef12, useState as useState12 } from "react";
19220
+ import { Box as Box12, IconButton as IconButton4, Typography as Typography8 } from "@mui/material";
19221
+ import { useEffect as useEffect18, useMemo as useMemo8, useRef as useRef12, useState as useState13 } from "react";
18991
19222
 
18992
19223
  // src/components/status-icon.tsx
18993
19224
  import { Cancel as Cancel2, CheckCircle, Error as Error3, Info, RemoveCircle } from "@mui/icons-material";
@@ -19153,7 +19384,7 @@ import { Box as Box11, useTheme as useTheme3 } from "@mui/material";
19153
19384
  var import_copy_to_clipboard = __toESM(require_copy_to_clipboard(), 1);
19154
19385
  import { jsx as jsx135, jsxs as jsxs126, Fragment as Fragment7 } from "react/jsx-runtime";
19155
19386
  import { Box as Box10, InputBase, NoSsr, SvgIcon, createTheme, ThemeProvider, Paper as Paper2 } from "@mui/material";
19156
- import { createContext as createContext10, useContext as useContext16, useState as useState11, useRef as useRef11, useCallback as useCallback9, useMemo as useMemo7, useEffect as useEffect17, memo as memo9 } from "react";
19387
+ import { createContext as createContext10, useContext as useContext16, useState as useState12, useRef as useRef11, useCallback as useCallback9, useMemo as useMemo7, useEffect as useEffect17, memo as memo9 } from "react";
19157
19388
  import { create, useStore, createStore } from "zustand";
19158
19389
  function r(e2) {
19159
19390
  var t, f2, n = "";
@@ -19397,7 +19628,7 @@ async function copyString(value) {
19397
19628
  }
19398
19629
  function useClipboard() {
19399
19630
  let { timeout = 2e3 } = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
19400
- const [copied, setCopied] = useState11(false);
19631
+ const [copied, setCopied] = useState12(false);
19401
19632
  const copyTimeout = useRef11(null);
19402
19633
  const handleCopyResult = useCallback9((value) => {
19403
19634
  const current = copyTimeout.current;
@@ -19480,7 +19711,7 @@ function useInspect(path, value, nestedIndex) {
19480
19711
  value,
19481
19712
  setInspectCache
19482
19713
  ]);
19483
- const [inspect, set] = useState11(() => {
19714
+ const [inspect, set] = useState12(() => {
19484
19715
  const shouldInspect = getInspectCache(path, nestedIndex);
19485
19716
  if (shouldInspect !== void 0) {
19486
19717
  return shouldInspect;
@@ -19829,7 +20060,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-.
19829
20060
  var Check2 = "M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z";
19830
20061
  var ChevronRight = "M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z";
19831
20062
  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";
19832
- 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";
20063
+ 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";
19833
20064
  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";
19834
20065
  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";
19835
20066
  var ExpandMore = "M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z";
@@ -19860,7 +20091,7 @@ var CircularArrowsIcon = (props) => {
19860
20091
  };
19861
20092
  var CloseIcon = (props) => {
19862
20093
  return /* @__PURE__ */ jsx135(BaseIcon, {
19863
- d: Close,
20094
+ d: Close2,
19864
20095
  ...props
19865
20096
  });
19866
20097
  };
@@ -20011,7 +20242,7 @@ var ObjectType = (props) => {
20011
20242
  const borderColor = useJsonViewerStore((store) => store.colorspace.base02);
20012
20243
  const groupArraysAfterLength = useJsonViewerStore((store) => store.groupArraysAfterLength);
20013
20244
  const isTrap = useIsCycleReference(props.path, props.value);
20014
- const [displayLength, setDisplayLength] = useState11(useJsonViewerStore((store) => store.maxDisplayLength));
20245
+ const [displayLength, setDisplayLength] = useState12(useJsonViewerStore((store) => store.maxDisplayLength));
20015
20246
  const objectSortKeys = useJsonViewerStore((store) => store.objectSortKeys);
20016
20247
  const elements = useMemo7(() => {
20017
20248
  if (!props.inspect) {
@@ -20217,7 +20448,7 @@ var stringType = defineEasyType({
20217
20448
  serialize: (value) => value,
20218
20449
  deserialize: (value) => value,
20219
20450
  Renderer: (props) => {
20220
- const [showRest, setShowRest] = useState11(false);
20451
+ const [showRest, setShowRest] = useState12(false);
20221
20452
  const collapseStringsAfterLength = useJsonViewerStore((store) => store.collapseStringsAfterLength);
20222
20453
  const value = showRest ? props.value : props.value.slice(0, collapseStringsAfterLength);
20223
20454
  const hasRest = props.value.length > collapseStringsAfterLength;
@@ -20371,7 +20602,7 @@ var DataKeyPair = (props) => {
20371
20602
  storeEditable,
20372
20603
  value
20373
20604
  ]);
20374
- const [tempValue, setTempValue] = useState11("");
20605
+ const [tempValue, setTempValue] = useState12("");
20375
20606
  const depth = path.length;
20376
20607
  const key = path[depth - 1];
20377
20608
  const hoverPath = useJsonViewerStore((store) => store.hoverPath);
@@ -20385,7 +20616,7 @@ var DataKeyPair = (props) => {
20385
20616
  const setHover = useJsonViewerStore((store) => store.setHover);
20386
20617
  const root2 = useJsonViewerStore((store) => store.value);
20387
20618
  const [inspect, setInspect] = useInspect(path, value, nestedIndex);
20388
- const [editing, setEditing] = useState11(false);
20619
+ const [editing, setEditing] = useState12(false);
20389
20620
  const onChange = useJsonViewerStore((store) => store.onChange);
20390
20621
  const keyColor = useTextColor();
20391
20622
  const numberKeyColor = useJsonViewerStore((store) => store.colorspace.base0C);
@@ -20770,7 +21001,7 @@ var DataKeyPair = (props) => {
20770
21001
  };
20771
21002
  var query = "(prefers-color-scheme: dark)";
20772
21003
  function useThemeDetector() {
20773
- const [isDark, setIsDark] = useState11(false);
21004
+ const [isDark, setIsDark] = useState12(false);
20774
21005
  useEffect17(() => {
20775
21006
  const listener = (e2) => setIsDark(e2.matches);
20776
21007
  setIsDark(window.matchMedia(query).matches);
@@ -20991,8 +21222,8 @@ var DetailRow2 = ({ content, label }) => /* @__PURE__ */ jsxs127(Box12, { sx: {
20991
21222
  /* @__PURE__ */ jsx137(Box12, { children: typeof content === "string" ? /* @__PURE__ */ jsx137(Typography8, { sx: { color: "text.primary", whiteSpace: "pre-wrap" }, variant: "caption", children: content }) : content })
20992
21223
  ] });
20993
21224
  var EventMessage = ({ event }) => {
20994
- const [isOpen, setIsOpen] = useState12(false);
20995
- const [isOverflowing, setIsOverflowing] = useState12(false);
21225
+ const [isOpen, setIsOpen] = useState13(false);
21226
+ const [isOverflowing, setIsOverflowing] = useState13(false);
20996
21227
  const textReference = useRef12(null);
20997
21228
  const parsedMessage = useMemo8(() => parseEventMessage(event), [event]);
20998
21229
  const isIAMPolicyEvent = useMemo8(
@@ -21023,7 +21254,7 @@ var EventMessage = ({ event }) => {
21023
21254
  return /* @__PURE__ */ jsxs127(Box12, { sx: { display: "flex", flexDirection: "column" }, children: [
21024
21255
  /* @__PURE__ */ jsxs127(Box12, { sx: { alignItems: "center", display: "flex" }, children: [
21025
21256
  Boolean(shouldShowToggle) && /* @__PURE__ */ jsx137(
21026
- IconButton3,
21257
+ IconButton4,
21027
21258
  {
21028
21259
  onClick: () => {
21029
21260
  setIsOpen(!isOpen);
@@ -31055,7 +31286,7 @@ var QueryStatus = {
31055
31286
 
31056
31287
  // src/components/event-details/lambda-invoke-logs-section.tsx
31057
31288
  import { Box as Box13, CircularProgress as CircularProgress3, Stack as Stack3, Typography as Typography9 } from "@mui/material";
31058
- import { useEffect as useEffect19, useMemo as useMemo9, useRef as useRef13, useState as useState13 } from "react";
31289
+ import { useEffect as useEffect19, useMemo as useMemo9, useRef as useRef13, useState as useState14 } from "react";
31059
31290
  import { jsx as jsx138, jsxs as jsxs128 } from "react/jsx-runtime";
31060
31291
  var POLL_INTERVAL_MS2 = 1e3;
31061
31292
  async function pollQueryResults(client, queryId) {
@@ -31084,9 +31315,9 @@ function useCloudWatchLogsClient(region) {
31084
31315
  return client;
31085
31316
  }
31086
31317
  function useLambdaInvokeLogs(functionName2, requestId, region, startTimeNano, endTimeNano) {
31087
- const [loading, setLoading] = useState13(true);
31088
- const [logs, setLogs] = useState13([]);
31089
- const [error, setError] = useState13();
31318
+ const [loading, setLoading] = useState14(true);
31319
+ const [logs, setLogs] = useState14([]);
31320
+ const [error, setError] = useState14();
31090
31321
  const pollTimerRef = useRef13(null);
31091
31322
  const cloudWatchLogs = useCloudWatchLogsClient(region);
31092
31323
  useEffect19(() => {
@@ -31163,10 +31394,10 @@ var LambdaInvokeLogsSection = ({ endTimeNano, functionName: functionName2, regio
31163
31394
  // src/components/event-details/toggle-section.tsx
31164
31395
  import { KeyboardArrowDown as KeyboardArrowDown2, KeyboardArrowRight as KeyboardArrowRight2 } from "@mui/icons-material";
31165
31396
  import { Box as Box14, Typography as Typography10 } from "@mui/material";
31166
- import { useState as useState14 } from "react";
31397
+ import { useState as useState15 } from "react";
31167
31398
  import { jsx as jsx139, jsxs as jsxs129 } from "react/jsx-runtime";
31168
31399
  var ToggleSection = ({ action, children, headline, initialOpen = true }) => {
31169
- const [isOpen, setIsOpen] = useState14(initialOpen);
31400
+ const [isOpen, setIsOpen] = useState15(initialOpen);
31170
31401
  return /* @__PURE__ */ jsxs129(Box14, { sx: { mt: 4 }, children: [
31171
31402
  /* @__PURE__ */ jsxs129(
31172
31403
  Box14,
@@ -31227,7 +31458,7 @@ var EventDetails = ({ onClose, selectedEvent }) => {
31227
31458
  () => getEventGroupsByType(selectedEvent?.events ?? []),
31228
31459
  [selectedEvent?.events]
31229
31460
  );
31230
- const [parseNestedJson, setParseNestedJson] = useState15(true);
31461
+ const [parseNestedJson, setParseNestedJson] = useState16(true);
31231
31462
  if (!selectedEvent) {
31232
31463
  return /* @__PURE__ */ jsx140(Box15, { sx: { p: 3, textAlign: "center" }, children: /* @__PURE__ */ jsx140(Typography11, { color: "text.secondary", variant: "body2", children: "Select an operation to view graph" }) });
31233
31464
  }
@@ -31270,7 +31501,7 @@ var EventDetails = ({ onClose, selectedEvent }) => {
31270
31501
  },
31271
31502
  children: [
31272
31503
  /* @__PURE__ */ jsx140(Box15, { children: /* @__PURE__ */ jsx140(Typography11, { sx: { fontWeight: 600 }, variant: "h6", children: "Operation Details" }) }),
31273
- onClose && /* @__PURE__ */ jsx140(IconButton4, { "data-testid": EVENT_DETAILS_CLOSE_BUTTON_TEST_ID, onClick: onClose, size: "small", children: /* @__PURE__ */ jsx140(Close2, {}) })
31504
+ onClose && /* @__PURE__ */ jsx140(IconButton5, { "data-testid": EVENT_DETAILS_CLOSE_BUTTON_TEST_ID, onClick: onClose, size: "small", children: /* @__PURE__ */ jsx140(Close3, {}) })
31274
31505
  ]
31275
31506
  }
31276
31507
  ),
@@ -31458,7 +31689,7 @@ import { Box as Box16, Divider as Divider4, Stack as Stack5, Tooltip as Tooltip6
31458
31689
  import { Handle, Position } from "@xyflow/react";
31459
31690
  import "@xyflow/react/dist/style.css";
31460
31691
  import "react";
31461
- import { memo as memo11, useMemo as useMemo11, useRef as useRef14, useState as useState16 } from "react";
31692
+ import { memo as memo11, useMemo as useMemo11, useRef as useRef14, useState as useState17 } from "react";
31462
31693
  import { Fragment as Fragment10, jsx as jsx141, jsxs as jsxs131 } from "react/jsx-runtime";
31463
31694
  var handleStyle = {
31464
31695
  backgroundColor: "white",
@@ -31543,7 +31774,7 @@ var NodeContent = ({ event, expanded, hasPermissionEvents, isError, isWarning })
31543
31774
  };
31544
31775
  var ServiceNode = memo11(({ data, selected }) => {
31545
31776
  const theme = useTheme4();
31546
- const [isHovered, setIsHovered] = useState16(false);
31777
+ const [isHovered, setIsHovered] = useState17(false);
31547
31778
  const nodeRef = useRef14(null);
31548
31779
  const isError = data.event.status_code === 2;
31549
31780
  const hasPermissionEvents = useMemo11(
@@ -31708,7 +31939,7 @@ var getLayoutedNodesAndEdges = (inputNodes, inputEdges) => {
31708
31939
 
31709
31940
  // src/components/trace-graph/xyflow/controls.tsx
31710
31941
  import { Add, FitScreen, Remove } from "@mui/icons-material";
31711
- import { Box as Box17, IconButton as IconButton5 } from "@mui/material";
31942
+ import { Box as Box17, IconButton as IconButton6 } from "@mui/material";
31712
31943
  import { useReactFlow, useStore as useStore2 } from "@xyflow/react";
31713
31944
  import { memo as memo12 } from "react";
31714
31945
  import { shallow } from "zustand/shallow";
@@ -31745,7 +31976,7 @@ var Controls = memo12(() => {
31745
31976
  },
31746
31977
  children: [
31747
31978
  /* @__PURE__ */ jsx142(
31748
- IconButton5,
31979
+ IconButton6,
31749
31980
  {
31750
31981
  className: "react-flow__controls-zoomin",
31751
31982
  disabled: maxZoomReached,
@@ -31755,7 +31986,7 @@ var Controls = memo12(() => {
31755
31986
  }
31756
31987
  ),
31757
31988
  /* @__PURE__ */ jsx142(
31758
- IconButton5,
31989
+ IconButton6,
31759
31990
  {
31760
31991
  className: "react-flow__controls-zoomout",
31761
31992
  disabled: minZoomReached,
@@ -31765,7 +31996,7 @@ var Controls = memo12(() => {
31765
31996
  }
31766
31997
  ),
31767
31998
  /* @__PURE__ */ jsx142(
31768
- IconButton5,
31999
+ IconButton6,
31769
32000
  {
31770
32001
  className: "react-flow__controls-fitview",
31771
32002
  onClick: onFitViewHandler,
@@ -32035,9 +32266,9 @@ async function fetchConnectedTraces(api, traceIds, allSpans = /* @__PURE__ */ ne
32035
32266
  var TraceGraphPage = ({ onClose, spanId, traceId }) => {
32036
32267
  const api = useAppInspectorApi();
32037
32268
  const { headerHeight } = useAppInspector();
32038
- const [spans, setSpans] = useState17();
32039
- const [loading, setLoading] = useState17(false);
32040
- const [fetchError, setFetchError] = useState17();
32269
+ const [spans, setSpans] = useState18();
32270
+ const [loading, setLoading] = useState18(false);
32271
+ const [fetchError, setFetchError] = useState18();
32041
32272
  const fetchSpans = useCallback10(async () => {
32042
32273
  setLoading(true);
32043
32274
  try {
@@ -32054,8 +32285,8 @@ var TraceGraphPage = ({ onClose, spanId, traceId }) => {
32054
32285
  useEffect21(() => {
32055
32286
  void fetchSpans();
32056
32287
  }, [fetchSpans]);
32057
- const [open, setOpen] = useState17(true);
32058
- const [selectedEvent, setSelectedEvent] = useState17(() => spans?.find((span) => span.span_id === spanId));
32288
+ const [open, setOpen] = useState18(true);
32289
+ const [selectedEvent, setSelectedEvent] = useState18(() => spans?.find((span) => span.span_id === spanId));
32059
32290
  const spansReference = useRef15();
32060
32291
  useEffect21(() => {
32061
32292
  if (!spansReference.current && spans) {
@@ -32108,6 +32339,9 @@ var SpansListPage = () => {
32108
32339
  hasMoreBackward,
32109
32340
  hasMoreForward,
32110
32341
  licenseLimit,
32342
+ search,
32343
+ searching,
32344
+ setSearch,
32111
32345
  spans,
32112
32346
  systemLimit,
32113
32347
  totalCount,
@@ -32119,10 +32353,11 @@ var SpansListPage = () => {
32119
32353
  }, [clearSpans]);
32120
32354
  const localstackVersion = status?.localstackVersion ?? (statusError instanceof AppInspectorNotFoundError ? statusError.localstackVersion : void 0);
32121
32355
  const versionCompatibility = checkEmulatorVersion(localstackVersion);
32122
- const [bannerDismissed, setBannerDismissed] = useState18(false);
32356
+ const searchSupported = emulatorSupportsSearch(localstackVersion);
32357
+ const [bannerDismissed, setBannerDismissed] = useState19(false);
32123
32358
  const [order2, setOrder] = useLocalStorage("spans-order", "oldest_first");
32124
- const [selectedTraceId, setSelectedTraceId] = useState18();
32125
- const [selectedSpanId, setSelectedSpanId] = useState18();
32359
+ const [selectedTraceId, setSelectedTraceId] = useState19();
32360
+ const [selectedSpanId, setSelectedSpanId] = useState19();
32126
32361
  const navigateToSpan = useCallback11((span) => {
32127
32362
  setSelectedTraceId(span.trace_id);
32128
32363
  setSelectedSpanId(span.span_id);
@@ -32235,8 +32470,12 @@ var SpansListPage = () => {
32235
32470
  onClearSpans: clearSpansSync,
32236
32471
  onFetchForward: fetchForward,
32237
32472
  onOrderChange: setOrder,
32473
+ onSearchChange: setSearch,
32238
32474
  onSpanClick: navigateToSpan,
32239
32475
  order: order2,
32476
+ search,
32477
+ searching,
32478
+ searchSupported,
32240
32479
  spans,
32241
32480
  systemLimit,
32242
32481
  totalCount,