@localstack/appinspector-ui 1.0.143 → 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.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);
@@ -15357,7 +15368,7 @@ var StatRow = ({ label, value }) => /* @__PURE__ */ (0, import_jsx_runtime125.js
15357
15368
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Typography, { color: "text.secondary", sx: { mr: 4 }, variant: "caption", children: label }),
15358
15369
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Typography, { fontWeight: 600, variant: "body2", children: fmtLimit(value) })
15359
15370
  ] });
15360
- var SpanCountBadge = ({ licenseLimit, systemLimit, totalCount, visibleCount }) => {
15371
+ var SpanCountBadge = ({ licenseLimit, searchActive = false, systemLimit, totalCount, visibleCount }) => {
15361
15372
  const [anchorElement, setAnchorElement] = (0, import_react36.useState)();
15362
15373
  const bindingLimit = resolveBindingLimit(systemLimit, licenseLimit);
15363
15374
  if (totalCount === void 0 && bindingLimit === void 0) {
@@ -15372,7 +15383,7 @@ var SpanCountBadge = ({ licenseLimit, systemLimit, totalCount, visibleCount }) =
15372
15383
  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
15384
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Typography, { color: "text.secondary", variant: "caption", children: "Showing" }),
15374
15385
  /* @__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` })
15386
+ /* @__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
15387
  ] }),
15377
15388
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(import_material5.Tooltip, { title: "Operation count details", children: /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(
15378
15389
  import_material5.IconButton,
@@ -15411,7 +15422,7 @@ var SpanCountBadge = ({ licenseLimit, systemLimit, totalCount, visibleCount }) =
15411
15422
  /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(
15412
15423
  StatRow,
15413
15424
  {
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" }) }),
15425
+ 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
15426
  value: totalCount
15416
15427
  }
15417
15428
  )
@@ -15478,9 +15489,55 @@ var newSpanBackgroundColor = {
15478
15489
  var LABEL_BEGINNING_OF_STREAM = "This is the beginning of the stream";
15479
15490
  var LABEL_END_OF_STREAM = "This is the end of the stream";
15480
15491
  var LABEL_NO_SPANS = "There are no operations";
15492
+ var SEARCH_MAX_LENGTH = 512;
15493
+ var SEARCH_DEBOUNCE_MS = 300;
15481
15494
  var STICK_TO_EDGE_THRESHOLD_PX = 4;
15482
15495
  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
15496
  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 } }) }) });
15497
+ var NoMatchRow = ({
15498
+ colSpan,
15499
+ onClearSearch,
15500
+ term
15501
+ }) => /* @__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: [
15502
+ /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { sx: { fontStyle: "italic" }, variant: "body2", children: `No operations match \u201C${term}\u201D` }),
15503
+ /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Button, { onClick: onClearSearch, size: "small", variant: "text", children: "Clear search" })
15504
+ ] }) }) });
15505
+ var SpansSearchField = ({ onChange, onClear, searchSupported, value }) => {
15506
+ const field = /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15507
+ import_material6.TextField,
15508
+ {
15509
+ disabled: !searchSupported,
15510
+ inputProps: {
15511
+ "aria-label": "Search operations",
15512
+ "data-testid": SPANS_SEARCH_INPUT_TEST_ID,
15513
+ "maxLength": SEARCH_MAX_LENGTH
15514
+ },
15515
+ InputProps: {
15516
+ endAdornment: value ? /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.InputAdornment, { position: "end", children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15517
+ import_material6.IconButton,
15518
+ {
15519
+ "aria-label": "Clear search",
15520
+ "data-testid": SPANS_SEARCH_CLEAR_TEST_ID,
15521
+ edge: "end",
15522
+ onClick: onClear,
15523
+ size: "small",
15524
+ children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_icons_material3.Close, { sx: { fontSize: 16 } })
15525
+ }
15526
+ ) }) : void 0,
15527
+ 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 } }) })
15528
+ },
15529
+ onChange: (event) => {
15530
+ onChange(event.target.value);
15531
+ },
15532
+ placeholder: "Search operations\u2026",
15533
+ size: "small",
15534
+ sx: { minWidth: 260 },
15535
+ value
15536
+ }
15537
+ );
15538
+ if (searchSupported) return field;
15539
+ 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 }) });
15540
+ };
15484
15541
  var SpansDataGridRow = (0, import_react37.memo)(
15485
15542
  ({ isNew, onSpanClick, row }) => {
15486
15543
  const theme = (0, import_material6.useTheme)();
@@ -15548,13 +15605,35 @@ var SpansDataGrid = ({
15548
15605
  licenseLimit,
15549
15606
  onClearSpans,
15550
15607
  onOrderChange,
15608
+ onSearchChange,
15551
15609
  onSpanClick,
15552
15610
  order: order2,
15611
+ search,
15612
+ searching,
15613
+ searchSupported,
15553
15614
  spans,
15554
15615
  systemLimit,
15555
15616
  totalCount,
15556
15617
  warningCount
15557
15618
  }) => {
15619
+ const searchActive = search.length > 0;
15620
+ const [searchDraft, setSearchDraft] = (0, import_react37.useState)(search);
15621
+ (0, import_react37.useEffect)(() => {
15622
+ const handle = setTimeout(() => {
15623
+ onSearchChange(searchDraft);
15624
+ }, SEARCH_DEBOUNCE_MS);
15625
+ return () => {
15626
+ clearTimeout(handle);
15627
+ };
15628
+ }, [searchDraft, onSearchChange]);
15629
+ const handleClearSearch = (0, import_react37.useCallback)(() => {
15630
+ setSearchDraft("");
15631
+ onSearchChange("");
15632
+ }, [onSearchChange]);
15633
+ const handleClearSpans = (0, import_react37.useCallback)(() => {
15634
+ setSearchDraft("");
15635
+ onClearSpans();
15636
+ }, [onClearSpans]);
15558
15637
  const columns = (0, import_react37.useMemo)(() => [
15559
15638
  columnHelper.accessor("startTime", {
15560
15639
  cell: (props) => /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(ResponsiveTimestampCell, { date: props.getValue() }),
@@ -15577,7 +15656,13 @@ var SpansDataGrid = ({
15577
15656
  if (!props.row.original.parent_service_name || !props.row.original.parent_resource_name) {
15578
15657
  return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { sx: { fontStyle: "italic" }, variant: "body2", children: "External producer" });
15579
15658
  }
15580
- return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(ServiceCell, { resource: props.row.original.parent_resource_name, service: props.row.original.parent_service_name });
15659
+ return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15660
+ ServiceCell,
15661
+ {
15662
+ resource: props.row.original.parent_resource_name,
15663
+ service: props.row.original.parent_service_name
15664
+ }
15665
+ );
15581
15666
  },
15582
15667
  header: "Producer",
15583
15668
  id: "producer",
@@ -15688,14 +15773,40 @@ var SpansDataGrid = ({
15688
15773
  }, [order2, spans?.length, lastSpanId]);
15689
15774
  return /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15690
15775
  /* @__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: [
15776
+ /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15777
+ SpansSearchField,
15778
+ {
15779
+ onChange: setSearchDraft,
15780
+ onClear: handleClearSearch,
15781
+ searchSupported,
15782
+ value: searchDraft
15783
+ }
15784
+ ),
15785
+ /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15786
+ SpanCountBadge,
15787
+ {
15788
+ licenseLimit,
15789
+ searchActive,
15790
+ systemLimit,
15791
+ totalCount,
15792
+ visibleCount: spans?.length
15793
+ }
15794
+ ),
15795
+ !searchActive && errorCount !== void 0 && errorCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15693
15796
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "text.disabled", sx: { mr: 0.5 }, variant: "body2", children: "\u2014" }),
15694
15797
  /* @__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
15798
  import_material6.Box,
15696
15799
  {
15697
15800
  "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" },
15801
+ sx: {
15802
+ alignItems: "center",
15803
+ borderBottom: "1px dotted",
15804
+ borderColor: "error.main",
15805
+ cursor: "help",
15806
+ display: "flex",
15807
+ gap: 0.5,
15808
+ mb: "-1px"
15809
+ },
15699
15810
  children: [
15700
15811
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "error", variant: "body2", children: errorCount.toLocaleString() }),
15701
15812
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "error", variant: "caption", children: errorCount === 1 ? "error" : "errors" })
@@ -15703,13 +15814,21 @@ var SpansDataGrid = ({
15703
15814
  }
15704
15815
  ) })
15705
15816
  ] }),
15706
- warningCount !== void 0 && warningCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15817
+ !searchActive && warningCount !== void 0 && warningCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15707
15818
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "text.disabled", sx: { mr: 0.5 }, variant: "body2", children: "\u2014" }),
15708
15819
  /* @__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
15820
  import_material6.Box,
15710
15821
  {
15711
15822
  "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" },
15823
+ sx: {
15824
+ alignItems: "center",
15825
+ borderBottom: "1px dotted",
15826
+ borderColor: "warning.main",
15827
+ cursor: "help",
15828
+ display: "flex",
15829
+ gap: 0.5,
15830
+ mb: "-1px"
15831
+ },
15713
15832
  children: [
15714
15833
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "warning.main", variant: "body2", children: warningCount.toLocaleString() }),
15715
15834
  /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.Typography, { color: "warning.main", variant: "caption", children: warningCount === 1 ? "warning" : "warnings" })
@@ -15725,7 +15844,7 @@ var SpansDataGrid = ({
15725
15844
  import_material6.Button,
15726
15845
  {
15727
15846
  "data-testid": CLEAR_SPANS_TEST_ID,
15728
- onClick: onClearSpans,
15847
+ onClick: handleClearSpans,
15729
15848
  startIcon: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_icons_material3.DeleteForeverOutlined, {}),
15730
15849
  children: "Clear Operations"
15731
15850
  }
@@ -15778,31 +15897,71 @@ var SpansDataGrid = ({
15778
15897
  )) }, headerGroup.id);
15779
15898
  }) }),
15780
15899
  /* @__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 }),
15900
+ (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 } }) }) }),
15901
+ !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
15902
  !clearingSpans && order2 === "newest_first" && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15784
15903
  hasRows && hasMoreForward && fetchingForward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(LoadingRow, { colSpan: columnCount }),
15785
15904
  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 } }) }),
15905
+ hasRows && paddingTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15906
+ import_material6.TableCell,
15907
+ {
15908
+ colSpan: columnCount,
15909
+ sx: { border: 0, height: paddingTop, p: 0 }
15910
+ }
15911
+ ) }),
15787
15912
  hasRows && virtualItems.map((virtualItem) => {
15788
15913
  const row = displayRows[virtualItem.index];
15789
15914
  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);
15915
+ return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15916
+ SpansDataGridRow,
15917
+ {
15918
+ isNew,
15919
+ onSpanClick,
15920
+ row
15921
+ },
15922
+ row.original.span_id
15923
+ );
15791
15924
  }),
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 } }) }),
15925
+ hasRows && paddingBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15926
+ import_material6.TableCell,
15927
+ {
15928
+ colSpan: columnCount,
15929
+ sx: { border: 0, height: paddingBottom, p: 0 }
15930
+ }
15931
+ ) }),
15793
15932
  hasRows && hasMoreBackward && fetchingBackward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(LoadingRow, { colSpan: columnCount }),
15794
15933
  hasRows && !hasMoreBackward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_BEGINNING_OF_STREAM })
15795
15934
  ] }),
15796
15935
  !clearingSpans && order2 === "oldest_first" && /* @__PURE__ */ (0, import_jsx_runtime126.jsxs)(import_jsx_runtime126.Fragment, { children: [
15797
15936
  hasRows && !hasMoreBackward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_BEGINNING_OF_STREAM }),
15798
15937
  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 } }) }),
15938
+ hasRows && paddingTop > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15939
+ import_material6.TableCell,
15940
+ {
15941
+ colSpan: columnCount,
15942
+ sx: { border: 0, height: paddingTop, p: 0 }
15943
+ }
15944
+ ) }),
15800
15945
  hasRows && virtualItems.map((virtualItem) => {
15801
15946
  const row = displayRows[virtualItem.index];
15802
15947
  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);
15948
+ return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15949
+ SpansDataGridRow,
15950
+ {
15951
+ isNew,
15952
+ onSpanClick,
15953
+ row
15954
+ },
15955
+ row.original.span_id
15956
+ );
15804
15957
  }),
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 } }) }),
15958
+ hasRows && paddingBottom > 0 && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(import_material6.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
15959
+ import_material6.TableCell,
15960
+ {
15961
+ colSpan: columnCount,
15962
+ sx: { border: 0, height: paddingBottom, p: 0 }
15963
+ }
15964
+ ) }),
15806
15965
  hasRows && hasMoreForward && fetchingForward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(LoadingRow, { colSpan: columnCount }),
15807
15966
  hasRows && !hasMoreForward && /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(StreamBoundaryRow, { colSpan: columnCount, label: LABEL_END_OF_STREAM })
15808
15967
  ] })
@@ -15843,8 +16002,12 @@ var SpansList = (props) => {
15843
16002
  licenseLimit: props.licenseLimit,
15844
16003
  onClearSpans: props.onClearSpans,
15845
16004
  onOrderChange: props.onOrderChange,
16005
+ onSearchChange: props.onSearchChange,
15846
16006
  onSpanClick: props.onSpanClick,
15847
16007
  order: props.order,
16008
+ search: props.search,
16009
+ searching: props.searching,
16010
+ searchSupported: props.searchSupported,
15848
16011
  spans: props.spans,
15849
16012
  systemLimit: props.systemLimit,
15850
16013
  totalCount: props.totalCount,
@@ -16255,6 +16418,7 @@ var createPaginationBuffer = (options) => {
16255
16418
  let hasMoreBackward = void 0;
16256
16419
  let fetchingForward = false;
16257
16420
  let fetchingBackward = false;
16421
+ let abortController;
16258
16422
  const reportDataChange = () => {
16259
16423
  options.onDataChange({
16260
16424
  hasMoreBackward: hasMoreBackward ?? false,
@@ -16278,25 +16442,40 @@ var createPaginationBuffer = (options) => {
16278
16442
  if (hasMoreForward === true) {
16279
16443
  return;
16280
16444
  }
16445
+ const controller = new AbortController();
16446
+ abortController = controller;
16281
16447
  fetchingForward = true;
16282
16448
  reportStatusChange();
16283
16449
  try {
16284
- const newPage = await fetchPage(nextForwardToken);
16450
+ const newPage = await fetchPage(nextForwardToken, controller.signal);
16451
+ if (controller.signal.aborted) {
16452
+ return;
16453
+ }
16285
16454
  hasMoreForward = newPage.items.length > 0;
16286
16455
  reportDataChange();
16287
16456
  } catch (error) {
16457
+ if (controller.signal.aborted) {
16458
+ return;
16459
+ }
16288
16460
  reportError(error instanceof Error ? error : new Error("Failed to fetch forward page", { cause: error }));
16289
16461
  } finally {
16290
- fetchingForward = false;
16291
- reportStatusChange();
16462
+ if (!controller.signal.aborted) {
16463
+ fetchingForward = false;
16464
+ reportStatusChange();
16465
+ runPostFetchActions();
16466
+ }
16292
16467
  }
16293
- runPostFetchActions();
16294
16468
  };
16295
16469
  const fetchForward = async () => {
16470
+ const controller = new AbortController();
16471
+ abortController = controller;
16296
16472
  fetchingForward = true;
16297
16473
  reportStatusChange();
16298
16474
  try {
16299
- const newPage = await fetchPage(nextForwardToken);
16475
+ const newPage = await fetchPage(nextForwardToken, controller.signal);
16476
+ if (controller.signal.aborted) {
16477
+ return;
16478
+ }
16300
16479
  if (options.sortComparator === void 0) {
16301
16480
  items = [...items ?? [], ...newPage.items];
16302
16481
  } else {
@@ -16312,18 +16491,28 @@ var createPaginationBuffer = (options) => {
16312
16491
  nextBackwardToken ??= newPage.nextBackwardToken;
16313
16492
  reportDataChange();
16314
16493
  } catch (error) {
16494
+ if (controller.signal.aborted) {
16495
+ return;
16496
+ }
16315
16497
  reportError(error instanceof Error ? error : new Error("Failed to fetch forward page", { cause: error }));
16316
16498
  } finally {
16317
- fetchingForward = false;
16318
- reportStatusChange();
16499
+ if (!controller.signal.aborted) {
16500
+ fetchingForward = false;
16501
+ reportStatusChange();
16502
+ runPostFetchActions();
16503
+ }
16319
16504
  }
16320
- runPostFetchActions();
16321
16505
  };
16322
16506
  const fetchBackward = async () => {
16507
+ const controller = new AbortController();
16508
+ abortController = controller;
16323
16509
  fetchingBackward = true;
16324
16510
  reportStatusChange();
16325
16511
  try {
16326
- const newPage = await fetchPage(nextBackwardToken);
16512
+ const newPage = await fetchPage(nextBackwardToken, controller.signal);
16513
+ if (controller.signal.aborted) {
16514
+ return;
16515
+ }
16327
16516
  if (options.sortComparator === void 0) {
16328
16517
  items = [...newPage.items, ...items ?? []];
16329
16518
  } else {
@@ -16339,12 +16528,17 @@ var createPaginationBuffer = (options) => {
16339
16528
  nextForwardToken ??= newPage.nextForwardToken;
16340
16529
  reportDataChange();
16341
16530
  } catch (error) {
16531
+ if (controller.signal.aborted) {
16532
+ return;
16533
+ }
16342
16534
  reportError(error instanceof Error ? error : new Error("Failed to fetch forward page", { cause: error }));
16343
16535
  } finally {
16344
- fetchingBackward = false;
16345
- reportStatusChange();
16536
+ if (!controller.signal.aborted) {
16537
+ fetchingBackward = false;
16538
+ reportStatusChange();
16539
+ runPostFetchActions();
16540
+ }
16346
16541
  }
16347
- runPostFetchActions();
16348
16542
  };
16349
16543
  const runPostFetchActions = () => {
16350
16544
  if (fetchBackwardLater) {
@@ -16369,11 +16563,16 @@ var createPaginationBuffer = (options) => {
16369
16563
  void checkIfThereAreMoreItemsForward();
16370
16564
  },
16371
16565
  clear: () => {
16566
+ abortController?.abort();
16567
+ abortController = void 0;
16568
+ fetchingForward = false;
16569
+ fetchingBackward = false;
16372
16570
  items = [];
16373
16571
  hasMoreBackward = void 0;
16374
16572
  nextBackwardToken = void 0;
16375
16573
  hasMoreForward = void 0;
16376
16574
  nextForwardToken = void 0;
16575
+ reportStatusChange();
16377
16576
  reportDataChange();
16378
16577
  },
16379
16578
  fetchBackward: async () => {
@@ -16609,9 +16808,9 @@ var createApiClient = ({ localstackEndpoint }) => {
16609
16808
  const endpoint = buildEventsEndpoint(iamEventsRequest);
16610
16809
  return makeRequest({ endpoint, localstackEndpoint });
16611
16810
  },
16612
- getSpans: async (request) => {
16811
+ getSpans: async (request, options) => {
16613
16812
  const endpoint = buildSpansEndpoint(request);
16614
- const response = await makeRequest({ endpoint, localstackEndpoint });
16813
+ const response = await makeRequest({ endpoint, localstackEndpoint, request: { signal: options?.signal } });
16615
16814
  return {
16616
16815
  ...response,
16617
16816
  spans: response.spans.map((span) => ({
@@ -18701,45 +18900,11 @@ var useSpans = () => {
18701
18900
  const [fetchError, setFetchError] = (0, import_react46.useState)();
18702
18901
  const [fetchingForward, setFetchingForward] = (0, import_react46.useState)(false);
18703
18902
  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
- }));
18903
+ const [search, setSearch] = (0, import_react46.useState)("");
18904
+ const searchRef = (0, import_react46.useRef)("");
18905
+ const [searching, setSearching] = (0, import_react46.useState)(false);
18906
+ const searchSeqRef = (0, import_react46.useRef)(0);
18907
+ const paginationBufferRef = (0, import_react46.useRef)();
18743
18908
  const [totalCount, setTotalCount] = (0, import_react46.useState)();
18744
18909
  const [licenseLimit, setLicenseLimit] = (0, import_react46.useState)();
18745
18910
  const [systemLimit, setSystemLimit] = (0, import_react46.useState)();
@@ -18748,14 +18913,71 @@ var useSpans = () => {
18748
18913
  const [hasMoreBackward, setHasMoreBackward] = (0, import_react46.useState)();
18749
18914
  const [hasMoreForward, setHasMoreForward] = (0, import_react46.useState)();
18750
18915
  const fetchForward = (0, import_react46.useCallback)(() => {
18751
- void paginationBufferRef.current.fetchForward();
18916
+ void paginationBufferRef.current?.fetchForward();
18752
18917
  }, []);
18753
18918
  const fetchBackward = (0, import_react46.useCallback)(() => {
18754
- void paginationBufferRef.current.fetchBackward();
18919
+ void paginationBufferRef.current?.fetchBackward();
18920
+ }, []);
18921
+ const applySearch = (0, import_react46.useCallback)((term) => {
18922
+ const normalized = term.trim();
18923
+ if (normalized === searchRef.current) {
18924
+ return;
18925
+ }
18926
+ searchRef.current = normalized;
18927
+ setSearch(normalized);
18928
+ const seq = ++searchSeqRef.current;
18929
+ setSearching(true);
18930
+ paginationBufferRef.current?.clear();
18931
+ void paginationBufferRef.current?.fetchForward().finally(() => {
18932
+ if (searchSeqRef.current === seq) {
18933
+ setSearching(false);
18934
+ }
18935
+ });
18755
18936
  }, []);
18756
18937
  (0, import_react46.useEffect)(() => {
18757
- fetchForward();
18758
- }, [fetchForward]);
18938
+ const buffer = createPaginationBuffer({
18939
+ async fetchPage(token, signal) {
18940
+ const response = await api.getSpans({
18941
+ limit: PAGE_SIZE,
18942
+ pagination_token: token,
18943
+ search: searchRef.current || void 0
18944
+ }, { signal });
18945
+ setTotalCount(response.pagination.total_count);
18946
+ setLicenseLimit(response.limits.span_count_limit_license);
18947
+ setSystemLimit(response.limits.span_count_limit_system);
18948
+ setErrorCount(response.error_count);
18949
+ setWarningCount(response.warning_count);
18950
+ return {
18951
+ hasMoreBackward: response.pagination.has_prev,
18952
+ hasMoreForward: response.pagination.has_next,
18953
+ items: response.spans,
18954
+ nextBackwardToken: response.pagination.prev_cursor ?? void 0,
18955
+ nextForwardToken: response.pagination.next_cursor ?? void 0
18956
+ };
18957
+ },
18958
+ onDataChange: ({ hasMoreBackward: hasMoreBackward2, hasMoreForward: hasMoreForward2, items }) => {
18959
+ setFetchError(void 0);
18960
+ setSpans(items);
18961
+ setHasMoreBackward(hasMoreBackward2);
18962
+ setHasMoreForward(hasMoreForward2);
18963
+ },
18964
+ onError: (error) => {
18965
+ setFetchError(error);
18966
+ },
18967
+ onStatusChange(status) {
18968
+ setFetchingBackward(status.fetchingBackward);
18969
+ setFetchingForward(status.fetchingForward);
18970
+ },
18971
+ // The spans endpoint is sorted by span ingestion time, which might differ from
18972
+ // the span timestamp. In order to accomodate for this, we will sort the spans
18973
+ // in the frontend with the following sort comparator.
18974
+ sortComparator(a3, b3) {
18975
+ return a3.start_time_unix_nano < b3.start_time_unix_nano ? -1 : 1;
18976
+ }
18977
+ });
18978
+ paginationBufferRef.current = buffer;
18979
+ void buffer.fetchForward();
18980
+ }, [api]);
18759
18981
  const [clearingSpans, setClearingSpans] = (0, import_react46.useState)(false);
18760
18982
  const clearSpans = (0, import_react46.useCallback)(async () => {
18761
18983
  if (clearingSpans) {
@@ -18765,8 +18987,11 @@ var useSpans = () => {
18765
18987
  try {
18766
18988
  await api.deleteSpans();
18767
18989
  setTotalCount(void 0);
18768
- paginationBufferRef.current.clear();
18769
- await paginationBufferRef.current.fetchForward();
18990
+ searchRef.current = "";
18991
+ setSearch("");
18992
+ setSearching(false);
18993
+ paginationBufferRef.current?.clear();
18994
+ await paginationBufferRef.current?.fetchForward();
18770
18995
  } catch (error) {
18771
18996
  console.error("Failed to delete spans:", error);
18772
18997
  setFetchError(error instanceof Error ? error : new Error("Failed to delete spans", { cause: error }));
@@ -18776,12 +19001,12 @@ var useSpans = () => {
18776
19001
  }, [clearingSpans, api]);
18777
19002
  (0, import_react46.useEffect)(() => {
18778
19003
  if (hasMoreForward === true && !fetchingForward) {
18779
- void paginationBufferRef.current.fetchForward();
19004
+ void paginationBufferRef.current?.fetchForward();
18780
19005
  }
18781
19006
  }, [hasMoreForward, fetchingForward]);
18782
19007
  (0, import_react46.useEffect)(() => {
18783
19008
  if (hasMoreBackward === true && !fetchingBackward && !fetchingForward) {
18784
- void paginationBufferRef.current.fetchBackward();
19009
+ void paginationBufferRef.current?.fetchBackward();
18785
19010
  }
18786
19011
  }, [hasMoreBackward, fetchingBackward, fetchingForward]);
18787
19012
  useSpansWebSocket({
@@ -18792,7 +19017,7 @@ var useSpans = () => {
18792
19017
  return;
18793
19018
  }
18794
19019
  if (!hasMoreForward) {
18795
- void paginationBufferRef.current.fetchForward();
19020
+ void paginationBufferRef.current?.fetchForward();
18796
19021
  }
18797
19022
  },
18798
19023
  onOpen: () => {
@@ -18816,6 +19041,9 @@ var useSpans = () => {
18816
19041
  hasMoreBackward,
18817
19042
  hasMoreForward,
18818
19043
  licenseLimit,
19044
+ search,
19045
+ searching,
19046
+ setSearch: applySearch,
18819
19047
  spans,
18820
19048
  systemLimit,
18821
19049
  totalCount,
@@ -19816,7 +20044,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
20044
  var Check2 = "M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z";
19817
20045
  var ChevronRight = "M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z";
19818
20046
  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";
20047
+ 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
20048
  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
20049
  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
20050
  var ExpandMore = "M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z";
@@ -19847,7 +20075,7 @@ var CircularArrowsIcon = (props) => {
19847
20075
  };
19848
20076
  var CloseIcon = (props) => {
19849
20077
  return /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(BaseIcon, {
19850
- d: Close,
20078
+ d: Close2,
19851
20079
  ...props
19852
20080
  });
19853
20081
  };
@@ -32095,6 +32323,9 @@ var SpansListPage = () => {
32095
32323
  hasMoreBackward,
32096
32324
  hasMoreForward,
32097
32325
  licenseLimit,
32326
+ search,
32327
+ searching,
32328
+ setSearch,
32098
32329
  spans,
32099
32330
  systemLimit,
32100
32331
  totalCount,
@@ -32106,6 +32337,7 @@ var SpansListPage = () => {
32106
32337
  }, [clearSpans]);
32107
32338
  const localstackVersion = status?.localstackVersion ?? (statusError instanceof AppInspectorNotFoundError ? statusError.localstackVersion : void 0);
32108
32339
  const versionCompatibility = checkEmulatorVersion(localstackVersion);
32340
+ const searchSupported = emulatorSupportsSearch(localstackVersion);
32109
32341
  const [bannerDismissed, setBannerDismissed] = (0, import_react63.useState)(false);
32110
32342
  const [order2, setOrder] = useLocalStorage("spans-order", "oldest_first");
32111
32343
  const [selectedTraceId, setSelectedTraceId] = (0, import_react63.useState)();
@@ -32222,8 +32454,12 @@ var SpansListPage = () => {
32222
32454
  onClearSpans: clearSpansSync,
32223
32455
  onFetchForward: fetchForward,
32224
32456
  onOrderChange: setOrder,
32457
+ onSearchChange: setSearch,
32225
32458
  onSpanClick: navigateToSpan,
32226
32459
  order: order2,
32460
+ search,
32461
+ searching,
32462
+ searchSupported,
32227
32463
  spans,
32228
32464
  systemLimit,
32229
32465
  totalCount,