@localstack/appinspector-ui 1.0.173 → 1.0.175

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.d.cts CHANGED
@@ -40,6 +40,12 @@ interface AppInspectorContextProviderProps {
40
40
  * The prefix for the event studio routes.
41
41
  */
42
42
  routePrefix: string;
43
+ /**
44
+ * URL of the standalone worker shipped as `@localstack/appinspector-ui/spans-worker.js`,
45
+ * served by the host from its own origin. When omitted, the worker is started from an
46
+ * inlined `blob:` URL (needs `worker-src blob:` in the CSP).
47
+ */
48
+ spansWorkerUrl?: string;
43
49
  }
44
50
  declare const AppInspectorContextProvider: (props: PropsWithChildren<AppInspectorContextProviderProps>) => react_jsx_runtime.JSX.Element;
45
51
 
package/dist/index.d.ts CHANGED
@@ -40,6 +40,12 @@ interface AppInspectorContextProviderProps {
40
40
  * The prefix for the event studio routes.
41
41
  */
42
42
  routePrefix: string;
43
+ /**
44
+ * URL of the standalone worker shipped as `@localstack/appinspector-ui/spans-worker.js`,
45
+ * served by the host from its own origin. When omitted, the worker is started from an
46
+ * inlined `blob:` URL (needs `worker-src blob:` in the CSP).
47
+ */
48
+ spansWorkerUrl?: string;
43
49
  }
44
50
  declare const AppInspectorContextProvider: (props: PropsWithChildren<AppInspectorContextProviderProps>) => react_jsx_runtime.JSX.Element;
45
51
 
package/dist/index.js CHANGED
@@ -16984,76 +16984,14 @@ var toSpanFilterProjection = (span) => ({
16984
16984
  });
16985
16985
 
16986
16986
  // src/hooks/use-spans-compute.ts
16987
- import { useCallback as useCallback5, useEffect as useEffect14, useRef as useRef8, useState as useState10 } from "react";
16987
+ import { useCallback as useCallback6, useEffect as useEffect15, useRef as useRef9, useState as useState10 } from "react";
16988
16988
 
16989
- // inline-worker:/home/runner/work/localstack-appinspector-ui/localstack-appinspector-ui/src/workers/spans-worker.worker.ts
16990
- var source = '(() => {\n // src/types/error-levels.ts\n var ErrorLevel = {\n ERROR: 3,\n // General error\n IAM_PERMISSION: 5,\n // IAM permission issues\n OK: 2,\n // OK\n // Standard OTEL status codes\n UNSET: 1,\n // Unset\n // Extended custom levels\n WARNING: 4\n // Warning\n };\n\n // src/utils/iam-utils.ts\n function determinePermissionStatus(payload) {\n if (payload.explicit_deny_count && payload.explicit_deny_count > 0) {\n return "explicitly_denied";\n }\n if (payload.is_allowed && payload.explicit_allow_count && payload.explicit_allow_count > 0) {\n return "explicitly_allowed";\n }\n if (payload.is_allowed) {\n return "implicitly_allowed";\n }\n return "implicitly_denied";\n }\n function parseIAMEvent(payloadString) {\n try {\n const payload = JSON.parse(payloadString);\n const permission = determinePermissionStatus(payload);\n const actions = [];\n const resources = [];\n if (payload.explicit_allows) {\n for (const allow of payload.explicit_allows) {\n if (!actions.includes(allow.action)) actions.push(allow.action);\n if (!resources.includes(allow.resource)) resources.push(allow.resource);\n }\n }\n if (payload.explicit_denies) {\n for (const deny of payload.explicit_denies) {\n if (!actions.includes(deny.action)) actions.push(deny.action);\n if (!resources.includes(deny.resource)) resources.push(deny.resource);\n }\n }\n if (payload.implicit_denies) {\n for (const deny of payload.implicit_denies) {\n if (!actions.includes(deny.action)) actions.push(deny.action);\n if (!resources.includes(deny.resource)) resources.push(deny.resource);\n }\n }\n return {\n details: {\n actions,\n explicitAllows: payload.explicit_allow_count,\n explicitDenies: payload.explicit_deny_count,\n implicitDenies: payload.implicit_deny_count,\n resources\n },\n operation: payload.operation,\n permission,\n principal: payload.principal_arn,\n service: payload.service\n };\n } catch (error) {\n console.error("Error parsing IAM event payload:", error);\n return void 0;\n }\n }\n\n // src/utils/error-level.ts\n var calculateHighestErrorLevel = (span) => {\n if (span.operation_status) {\n switch (span.operation_status) {\n case "error": {\n return ErrorLevel.ERROR;\n }\n case "iam_explicit_deny":\n case "iam_explicit_warn":\n case "iam_implicit_deny":\n case "iam_implicit_warn": {\n return ErrorLevel.IAM_PERMISSION;\n }\n default: {\n return ErrorLevel.OK;\n }\n }\n }\n const hasIamDenial = span.events.some((event) => {\n if (!event.event_type?.toLowerCase().includes("iam")) return false;\n if (typeof event.attributes?.payload !== "string") return false;\n const parsed = parseIAMEvent(event.attributes.payload);\n return parsed?.permission === "explicitly_denied" || parsed?.permission === "implicitly_denied";\n });\n if (hasIamDenial) return ErrorLevel.IAM_PERMISSION;\n if (span.status_code === 2) return ErrorLevel.ERROR;\n const hasErrorInErrors = span.errors?.some((error) => error.level === "error");\n if (hasErrorInErrors) return ErrorLevel.ERROR;\n const hasWarning = span.errors?.some((error) => error.level === "warning");\n if (hasWarning) return ErrorLevel.WARNING;\n return span.status_code || ErrorLevel.UNSET;\n };\n\n // src/utils/spans-filters.ts\n var EMPTY_FILTERS = {\n consumerService: [],\n producerService: [],\n status: []\n };\n var EXTERNAL_PRODUCER_VALUE = "__external__";\n function isFiltersEmpty(filters) {\n return filters.consumerService.length === 0 && filters.producerService.length === 0 && filters.status.length === 0;\n }\n var collapseStatusValue = (status) => {\n if (status === "iam_explicit_deny" || status === "iam_implicit_deny") return "iam_deny";\n if (status === "iam_explicit_warn" || status === "iam_implicit_warn") return "iam_warn";\n return status;\n };\n function applyFilters(spans, filters) {\n if (isFiltersEmpty(filters)) return spans;\n return spans.filter((span) => {\n if (filters.consumerService.length > 0 && !filters.consumerService.includes(span.service_name)) {\n return false;\n }\n if (filters.producerService.length > 0) {\n const includesExternal = filters.producerService.includes(EXTERNAL_PRODUCER_VALUE);\n const namedServices = filters.producerService.filter((value) => value !== EXTERNAL_PRODUCER_VALUE);\n const matchesExternal = includesExternal && !span.parent_service_name;\n const matchesNamed = namedServices.length > 0 && !!span.parent_service_name && namedServices.includes(span.parent_service_name);\n if (!matchesExternal && !matchesNamed) return false;\n }\n if (filters.status.length > 0) {\n if (!span.operation_status) return false;\n if (!filters.status.includes(collapseStatusValue(span.operation_status))) return false;\n }\n return true;\n });\n }\n\n // src/workers/spans-reducer.ts\n var compareByStartTime = (a, b) => a.start_time_unix_nano < b.start_time_unix_nano ? -1 : 1;\n var insertSorted = (array, item, compareFunction, hint) => {\n let left;\n let right;\n if (hint === "back") {\n left = array.length;\n right = array.length;\n let step = 1;\n while (left > 0 && compareFunction(array[left - 1], item) > 0) {\n right = left;\n left = Math.max(0, left - step);\n step *= 2;\n }\n } else {\n left = 0;\n right = 0;\n let step = 1;\n while (right < array.length && compareFunction(array[right], item) <= 0) {\n left = right + 1;\n right = Math.min(array.length, right + step);\n step *= 2;\n }\n }\n while (left < right) {\n const mid = left + right >>> 1;\n if (compareFunction(array[mid], item) <= 0) {\n left = mid + 1;\n } else {\n right = mid;\n }\n }\n array.splice(left, 0, item);\n };\n var createSpansReducer = () => {\n let corpus = [];\n let filters = EMPTY_FILTERS;\n const computeResult = () => {\n const filtered = applyFilters(corpus, filters);\n let errorCount = 0;\n let warningCount = 0;\n const orderedFilteredIds = [];\n for (const entry of filtered) {\n orderedFilteredIds.push(entry.span_id);\n if (entry.errorLevel === ErrorLevel.ERROR || entry.errorLevel === ErrorLevel.IAM_PERMISSION) {\n errorCount++;\n } else if (entry.errorLevel === ErrorLevel.WARNING) {\n warningCount++;\n }\n }\n const producers = /* @__PURE__ */ new Set();\n const consumers = /* @__PURE__ */ new Set();\n const statuses = /* @__PURE__ */ new Set();\n let hasExternalProducer = false;\n for (const entry of corpus) {\n if (entry.parent_service_name) producers.add(entry.parent_service_name);\n else hasExternalProducer = true;\n consumers.add(entry.service_name);\n if (entry.operation_status) statuses.add(collapseStatusValue(entry.operation_status));\n }\n return {\n consumerValues: [...consumers].toSorted(),\n errorCount,\n hasExternalProducer,\n orderedFilteredIds,\n producerValues: [...producers].toSorted(),\n statusValues: [...statuses].toSorted(),\n warningCount\n };\n };\n return {\n addPage: (direction, projections) => {\n const hint = direction === "forward" ? "back" : "front";\n const sorted = projections.toSorted(compareByStartTime);\n for (const projection of sorted) {\n insertSorted(corpus, { ...projection, errorLevel: calculateHighestErrorLevel(projection) }, compareByStartTime, hint);\n }\n return computeResult();\n },\n clear: () => {\n corpus = [];\n filters = EMPTY_FILTERS;\n return computeResult();\n },\n setFilters: (next) => {\n filters = next;\n return computeResult();\n }\n };\n };\n\n // src/workers/spans-worker.worker.ts\n var context = globalThis;\n var reducer = createSpansReducer();\n context.addEventListener("message", (event) => {\n const request = event.data;\n const result = request.op === "addPage" ? reducer.addPage(request.direction, request.projections) : request.op === "setFilters" ? reducer.setFilters(request.filters) : reducer.clear();\n context.postMessage({ result, seq: request.seq });\n });\n})();\n';
16991
- function createInlineWorker() {
16992
- const url = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
16993
- try {
16994
- return new Worker(url);
16995
- } finally {
16996
- URL.revokeObjectURL(url);
16997
- }
16998
- }
16999
-
17000
- // src/hooks/use-spans-compute.ts
17001
- var EMPTY_RESULT = {
17002
- consumerValues: [],
17003
- errorCount: 0,
17004
- hasExternalProducer: false,
17005
- orderedFilteredIds: [],
17006
- producerValues: [],
17007
- statusValues: [],
17008
- warningCount: 0
17009
- };
17010
- var useSpansCompute = () => {
17011
- const [result, setResult] = useState10(EMPTY_RESULT);
17012
- const [workerError, setWorkerError] = useState10();
17013
- const [retryCount, setRetryCount] = useState10(0);
17014
- const workerRef = useRef8();
17015
- const seqRef = useRef8(0);
17016
- const lastAppliedRef = useRef8(0);
17017
- useEffect14(() => {
17018
- try {
17019
- const worker = createInlineWorker();
17020
- worker.addEventListener("message", (event) => {
17021
- const { result: nextResult, seq } = event.data;
17022
- if (seq < lastAppliedRef.current) return;
17023
- lastAppliedRef.current = seq;
17024
- setResult(nextResult);
17025
- });
17026
- worker.addEventListener("error", (event) => {
17027
- setWorkerError(new Error(event.message || "Spans filter worker crashed."));
17028
- });
17029
- workerRef.current = worker;
17030
- return () => {
17031
- worker.terminate();
17032
- workerRef.current = void 0;
17033
- };
17034
- } catch (error) {
17035
- setWorkerError(error instanceof Error ? error : new Error("Failed to start the spans filter worker."));
17036
- return;
17037
- }
17038
- }, [retryCount]);
17039
- const retryWorker = useCallback5(() => {
17040
- setWorkerError(void 0);
17041
- setRetryCount((count2) => count2 + 1);
17042
- }, []);
17043
- const addPage = useCallback5((direction, projections) => {
17044
- workerRef.current?.postMessage({ direction, op: "addPage", projections, seq: ++seqRef.current });
17045
- }, []);
17046
- const setFilters = useCallback5((filters) => {
17047
- workerRef.current?.postMessage({ filters, op: "setFilters", seq: ++seqRef.current });
17048
- }, []);
17049
- const clear = useCallback5(() => {
17050
- workerRef.current?.postMessage({ op: "clear", seq: ++seqRef.current });
17051
- }, []);
17052
- return { addPage, clear, result, retryWorker, setFilters, workerError };
17053
- };
16989
+ // src/context.tsx
16990
+ import { createContext as createContext9, useContext as useContext15 } from "react";
16991
+ import { useCallback as useCallback5, useMemo as useMemo5 } from "react";
17054
16992
 
17055
- // src/hooks/use-spans-ws.tsx
17056
- import { useCallback as useCallback7, useEffect as useEffect16, useRef as useRef10, useState as useState11 } from "react";
16993
+ // src/api/client.ts
16994
+ var import_semver2 = __toESM(require_semver2(), 1);
17057
16995
 
17058
16996
  // src/config.ts
17059
16997
  var APPINSPECTOR_API_PREFIX = "/_localstack/appinspector";
@@ -17081,13 +17019,6 @@ var getAppInspectorApiUrl = (localstackEndpoint, endpoint) => {
17081
17019
  };
17082
17020
  var LOCALSTACK_INFO_PATH = "/_localstack/info";
17083
17021
 
17084
- // src/context.tsx
17085
- import { createContext as createContext9, useContext as useContext15 } from "react";
17086
- import { useCallback as useCallback6, useMemo as useMemo5 } from "react";
17087
-
17088
- // src/api/client.ts
17089
- var import_semver2 = __toESM(require_semver2(), 1);
17090
-
17091
17022
  // src/constants/error-messages.ts
17092
17023
  var ERROR_MESSAGES = {
17093
17024
  APP_INSPECTOR_DISABLED: "App Inspector is currently disabled.",
@@ -17352,7 +17283,7 @@ var createApiClient = ({ localstackEndpoint }) => {
17352
17283
  };
17353
17284
 
17354
17285
  // src/hooks/use-appinspector-open-analytics.ts
17355
- import { useEffect as useEffect15, useRef as useRef9 } from "react";
17286
+ import { useEffect as useEffect14, useRef as useRef8 } from "react";
17356
17287
 
17357
17288
  // node_modules/.pnpm/ua-parser-js@2.0.9/node_modules/ua-parser-js/src/main/ua-parser.mjs
17358
17289
  var LIBVERSION = "2.0.9";
@@ -19234,8 +19165,8 @@ var useAppInspectorOpenAnalytics = () => {
19234
19165
  const api = useAppInspectorApi();
19235
19166
  const { deploymentContainer } = useAppInspector();
19236
19167
  const { status, statusError } = useAppInspectorStatus();
19237
- const firedRef = useRef9(false);
19238
- useEffect15(() => {
19168
+ const firedRef = useRef8(false);
19169
+ useEffect14(() => {
19239
19170
  if (firedRef.current) {
19240
19171
  return;
19241
19172
  }
@@ -19273,7 +19204,7 @@ var AppInspectorContextProvider = (props) => {
19273
19204
  () => createApiClient({ localstackEndpoint: props.localstackEndpoint }),
19274
19205
  [props.localstackEndpoint]
19275
19206
  );
19276
- const resolveLink = useCallback6(
19207
+ const resolveLink = useCallback5(
19277
19208
  (options) => {
19278
19209
  if (options.to === "settings") {
19279
19210
  return `${props.routePrefix}/settings`;
@@ -19295,14 +19226,83 @@ var AppInspectorContextProvider = (props) => {
19295
19226
  headerHeight: props.headerHeight ?? 0,
19296
19227
  linkComponent: props.linkComponent,
19297
19228
  localstackEndpoint: props.localstackEndpoint,
19298
- resolveLink
19229
+ resolveLink,
19230
+ spansWorkerUrl: props.spansWorkerUrl
19299
19231
  }),
19300
- [deploymentContainer, props.headerHeight, props.linkComponent, props.localstackEndpoint, resolveLink]
19232
+ [deploymentContainer, props.headerHeight, props.linkComponent, props.localstackEndpoint, props.spansWorkerUrl, resolveLink]
19301
19233
  );
19302
19234
  return /* @__PURE__ */ jsx132(ApiProvider, { api, children: /* @__PURE__ */ jsx132(StatusProvider, { children: /* @__PURE__ */ jsx132(AppInspectorContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx132(AppInspectorAnalyticsBoundary, { children: props.children }) }) }) });
19303
19235
  };
19304
19236
 
19237
+ // inline-worker:/home/runner/work/localstack-appinspector-ui/localstack-appinspector-ui/src/workers/spans-worker.worker.ts
19238
+ var source = '(() => {\n // src/types/error-levels.ts\n var ErrorLevel = {\n ERROR: 3,\n // General error\n IAM_PERMISSION: 5,\n // IAM permission issues\n OK: 2,\n // OK\n // Standard OTEL status codes\n UNSET: 1,\n // Unset\n // Extended custom levels\n WARNING: 4\n // Warning\n };\n\n // src/utils/iam-utils.ts\n function determinePermissionStatus(payload) {\n if (payload.explicit_deny_count && payload.explicit_deny_count > 0) {\n return "explicitly_denied";\n }\n if (payload.is_allowed && payload.explicit_allow_count && payload.explicit_allow_count > 0) {\n return "explicitly_allowed";\n }\n if (payload.is_allowed) {\n return "implicitly_allowed";\n }\n return "implicitly_denied";\n }\n function parseIAMEvent(payloadString) {\n try {\n const payload = JSON.parse(payloadString);\n const permission = determinePermissionStatus(payload);\n const actions = [];\n const resources = [];\n if (payload.explicit_allows) {\n for (const allow of payload.explicit_allows) {\n if (!actions.includes(allow.action)) actions.push(allow.action);\n if (!resources.includes(allow.resource)) resources.push(allow.resource);\n }\n }\n if (payload.explicit_denies) {\n for (const deny of payload.explicit_denies) {\n if (!actions.includes(deny.action)) actions.push(deny.action);\n if (!resources.includes(deny.resource)) resources.push(deny.resource);\n }\n }\n if (payload.implicit_denies) {\n for (const deny of payload.implicit_denies) {\n if (!actions.includes(deny.action)) actions.push(deny.action);\n if (!resources.includes(deny.resource)) resources.push(deny.resource);\n }\n }\n return {\n details: {\n actions,\n explicitAllows: payload.explicit_allow_count,\n explicitDenies: payload.explicit_deny_count,\n implicitDenies: payload.implicit_deny_count,\n resources\n },\n operation: payload.operation,\n permission,\n principal: payload.principal_arn,\n service: payload.service\n };\n } catch (error) {\n console.error("Error parsing IAM event payload:", error);\n return void 0;\n }\n }\n\n // src/utils/error-level.ts\n var calculateHighestErrorLevel = (span) => {\n if (span.operation_status) {\n switch (span.operation_status) {\n case "error": {\n return ErrorLevel.ERROR;\n }\n case "iam_explicit_deny":\n case "iam_explicit_warn":\n case "iam_implicit_deny":\n case "iam_implicit_warn": {\n return ErrorLevel.IAM_PERMISSION;\n }\n default: {\n return ErrorLevel.OK;\n }\n }\n }\n const hasIamDenial = span.events.some((event) => {\n if (!event.event_type?.toLowerCase().includes("iam")) return false;\n if (typeof event.attributes?.payload !== "string") return false;\n const parsed = parseIAMEvent(event.attributes.payload);\n return parsed?.permission === "explicitly_denied" || parsed?.permission === "implicitly_denied";\n });\n if (hasIamDenial) return ErrorLevel.IAM_PERMISSION;\n if (span.status_code === 2) return ErrorLevel.ERROR;\n const hasErrorInErrors = span.errors?.some((error) => error.level === "error");\n if (hasErrorInErrors) return ErrorLevel.ERROR;\n const hasWarning = span.errors?.some((error) => error.level === "warning");\n if (hasWarning) return ErrorLevel.WARNING;\n return span.status_code || ErrorLevel.UNSET;\n };\n\n // src/utils/spans-filters.ts\n var EMPTY_FILTERS = {\n consumerService: [],\n producerService: [],\n status: []\n };\n var EXTERNAL_PRODUCER_VALUE = "__external__";\n function isFiltersEmpty(filters) {\n return filters.consumerService.length === 0 && filters.producerService.length === 0 && filters.status.length === 0;\n }\n var collapseStatusValue = (status) => {\n if (status === "iam_explicit_deny" || status === "iam_implicit_deny") return "iam_deny";\n if (status === "iam_explicit_warn" || status === "iam_implicit_warn") return "iam_warn";\n return status;\n };\n function applyFilters(spans, filters) {\n if (isFiltersEmpty(filters)) return spans;\n return spans.filter((span) => {\n if (filters.consumerService.length > 0 && !filters.consumerService.includes(span.service_name)) {\n return false;\n }\n if (filters.producerService.length > 0) {\n const includesExternal = filters.producerService.includes(EXTERNAL_PRODUCER_VALUE);\n const namedServices = filters.producerService.filter((value) => value !== EXTERNAL_PRODUCER_VALUE);\n const matchesExternal = includesExternal && !span.parent_service_name;\n const matchesNamed = namedServices.length > 0 && !!span.parent_service_name && namedServices.includes(span.parent_service_name);\n if (!matchesExternal && !matchesNamed) return false;\n }\n if (filters.status.length > 0) {\n if (!span.operation_status) return false;\n if (!filters.status.includes(collapseStatusValue(span.operation_status))) return false;\n }\n return true;\n });\n }\n\n // src/workers/spans-reducer.ts\n var compareByStartTime = (a, b) => a.start_time_unix_nano < b.start_time_unix_nano ? -1 : 1;\n var insertSorted = (array, item, compareFunction, hint) => {\n let left;\n let right;\n if (hint === "back") {\n left = array.length;\n right = array.length;\n let step = 1;\n while (left > 0 && compareFunction(array[left - 1], item) > 0) {\n right = left;\n left = Math.max(0, left - step);\n step *= 2;\n }\n } else {\n left = 0;\n right = 0;\n let step = 1;\n while (right < array.length && compareFunction(array[right], item) <= 0) {\n left = right + 1;\n right = Math.min(array.length, right + step);\n step *= 2;\n }\n }\n while (left < right) {\n const mid = left + right >>> 1;\n if (compareFunction(array[mid], item) <= 0) {\n left = mid + 1;\n } else {\n right = mid;\n }\n }\n array.splice(left, 0, item);\n };\n var createSpansReducer = () => {\n let corpus = [];\n let filters = EMPTY_FILTERS;\n const computeResult = () => {\n const filtered = applyFilters(corpus, filters);\n let errorCount = 0;\n let warningCount = 0;\n const orderedFilteredIds = [];\n for (const entry of filtered) {\n orderedFilteredIds.push(entry.span_id);\n if (entry.errorLevel === ErrorLevel.ERROR || entry.errorLevel === ErrorLevel.IAM_PERMISSION) {\n errorCount++;\n } else if (entry.errorLevel === ErrorLevel.WARNING) {\n warningCount++;\n }\n }\n const producers = /* @__PURE__ */ new Set();\n const consumers = /* @__PURE__ */ new Set();\n const statuses = /* @__PURE__ */ new Set();\n let hasExternalProducer = false;\n for (const entry of corpus) {\n if (entry.parent_service_name) producers.add(entry.parent_service_name);\n else hasExternalProducer = true;\n consumers.add(entry.service_name);\n if (entry.operation_status) statuses.add(collapseStatusValue(entry.operation_status));\n }\n return {\n consumerValues: [...consumers].toSorted(),\n errorCount,\n hasExternalProducer,\n orderedFilteredIds,\n producerValues: [...producers].toSorted(),\n statusValues: [...statuses].toSorted(),\n warningCount\n };\n };\n return {\n addPage: (direction, projections) => {\n const hint = direction === "forward" ? "back" : "front";\n const sorted = projections.toSorted(compareByStartTime);\n for (const projection of sorted) {\n insertSorted(corpus, { ...projection, errorLevel: calculateHighestErrorLevel(projection) }, compareByStartTime, hint);\n }\n return computeResult();\n },\n clear: () => {\n corpus = [];\n filters = EMPTY_FILTERS;\n return computeResult();\n },\n setFilters: (next) => {\n filters = next;\n return computeResult();\n }\n };\n };\n\n // src/workers/spans-worker.worker.ts\n var context = globalThis;\n var reducer = createSpansReducer();\n context.addEventListener("message", (event) => {\n const request = event.data;\n const result = request.op === "addPage" ? reducer.addPage(request.direction, request.projections) : request.op === "setFilters" ? reducer.setFilters(request.filters) : reducer.clear();\n context.postMessage({ result, seq: request.seq });\n });\n})();\n';
19239
+ function createInlineWorker() {
19240
+ const url = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
19241
+ try {
19242
+ return new Worker(url);
19243
+ } finally {
19244
+ URL.revokeObjectURL(url);
19245
+ }
19246
+ }
19247
+
19248
+ // src/hooks/use-spans-compute.ts
19249
+ var EMPTY_RESULT = {
19250
+ consumerValues: [],
19251
+ errorCount: 0,
19252
+ hasExternalProducer: false,
19253
+ orderedFilteredIds: [],
19254
+ producerValues: [],
19255
+ statusValues: [],
19256
+ warningCount: 0
19257
+ };
19258
+ var useSpansCompute = () => {
19259
+ const { spansWorkerUrl } = useAppInspector();
19260
+ const [result, setResult] = useState10(EMPTY_RESULT);
19261
+ const [workerError, setWorkerError] = useState10();
19262
+ const [retryCount, setRetryCount] = useState10(0);
19263
+ const workerRef = useRef9();
19264
+ const seqRef = useRef9(0);
19265
+ const lastAppliedRef = useRef9(0);
19266
+ useEffect15(() => {
19267
+ try {
19268
+ const worker = spansWorkerUrl ? new Worker(spansWorkerUrl) : createInlineWorker();
19269
+ worker.addEventListener("message", (event) => {
19270
+ const { result: nextResult, seq } = event.data;
19271
+ if (seq < lastAppliedRef.current) return;
19272
+ lastAppliedRef.current = seq;
19273
+ setResult(nextResult);
19274
+ });
19275
+ worker.addEventListener("error", (event) => {
19276
+ setWorkerError(new Error(event.message || "Spans filter worker crashed."));
19277
+ });
19278
+ workerRef.current = worker;
19279
+ return () => {
19280
+ worker.terminate();
19281
+ workerRef.current = void 0;
19282
+ };
19283
+ } catch (error) {
19284
+ setWorkerError(error instanceof Error ? error : new Error("Failed to start the spans filter worker."));
19285
+ return;
19286
+ }
19287
+ }, [retryCount, spansWorkerUrl]);
19288
+ const retryWorker = useCallback6(() => {
19289
+ setWorkerError(void 0);
19290
+ setRetryCount((count2) => count2 + 1);
19291
+ }, []);
19292
+ const addPage = useCallback6((direction, projections) => {
19293
+ workerRef.current?.postMessage({ direction, op: "addPage", projections, seq: ++seqRef.current });
19294
+ }, []);
19295
+ const setFilters = useCallback6((filters) => {
19296
+ workerRef.current?.postMessage({ filters, op: "setFilters", seq: ++seqRef.current });
19297
+ }, []);
19298
+ const clear = useCallback6(() => {
19299
+ workerRef.current?.postMessage({ op: "clear", seq: ++seqRef.current });
19300
+ }, []);
19301
+ return { addPage, clear, result, retryWorker, setFilters, workerError };
19302
+ };
19303
+
19305
19304
  // src/hooks/use-spans-ws.tsx
19305
+ import { useCallback as useCallback7, useEffect as useEffect16, useRef as useRef10, useState as useState11 } from "react";
19306
19306
  var wsLogger = createScopedLogger("WS");
19307
19307
  var isLegacySpanFields = (value) => {
19308
19308
  return typeof value.span_id === "string" && typeof value.trace_id === "string" && typeof value.timestamp === "string";