@localstack/appinspector-ui 1.0.174 → 1.0.176
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 +86 -86
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +81 -81
- package/dist/index.js.map +1 -1
- package/dist/spans-worker.js +242 -0
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -16960,76 +16960,14 @@ var toSpanFilterProjection = (span) => ({
|
|
|
16960
16960
|
});
|
|
16961
16961
|
|
|
16962
16962
|
// src/hooks/use-spans-compute.ts
|
|
16963
|
-
var
|
|
16964
|
-
|
|
16965
|
-
// inline-worker:/home/runner/work/localstack-appinspector-ui/localstack-appinspector-ui/src/workers/spans-worker.worker.ts
|
|
16966
|
-
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';
|
|
16967
|
-
function createInlineWorker() {
|
|
16968
|
-
const url = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
|
|
16969
|
-
try {
|
|
16970
|
-
return new Worker(url);
|
|
16971
|
-
} finally {
|
|
16972
|
-
URL.revokeObjectURL(url);
|
|
16973
|
-
}
|
|
16974
|
-
}
|
|
16963
|
+
var import_react46 = require("react");
|
|
16975
16964
|
|
|
16976
|
-
// src/
|
|
16977
|
-
var
|
|
16978
|
-
|
|
16979
|
-
errorCount: 0,
|
|
16980
|
-
hasExternalProducer: false,
|
|
16981
|
-
orderedFilteredIds: [],
|
|
16982
|
-
producerValues: [],
|
|
16983
|
-
statusValues: [],
|
|
16984
|
-
warningCount: 0
|
|
16985
|
-
};
|
|
16986
|
-
var useSpansCompute = () => {
|
|
16987
|
-
const [result, setResult] = (0, import_react43.useState)(EMPTY_RESULT);
|
|
16988
|
-
const [workerError, setWorkerError] = (0, import_react43.useState)();
|
|
16989
|
-
const [retryCount, setRetryCount] = (0, import_react43.useState)(0);
|
|
16990
|
-
const workerRef = (0, import_react43.useRef)();
|
|
16991
|
-
const seqRef = (0, import_react43.useRef)(0);
|
|
16992
|
-
const lastAppliedRef = (0, import_react43.useRef)(0);
|
|
16993
|
-
(0, import_react43.useEffect)(() => {
|
|
16994
|
-
try {
|
|
16995
|
-
const worker = createInlineWorker();
|
|
16996
|
-
worker.addEventListener("message", (event) => {
|
|
16997
|
-
const { result: nextResult, seq } = event.data;
|
|
16998
|
-
if (seq < lastAppliedRef.current) return;
|
|
16999
|
-
lastAppliedRef.current = seq;
|
|
17000
|
-
setResult(nextResult);
|
|
17001
|
-
});
|
|
17002
|
-
worker.addEventListener("error", (event) => {
|
|
17003
|
-
setWorkerError(new Error(event.message || "Spans filter worker crashed."));
|
|
17004
|
-
});
|
|
17005
|
-
workerRef.current = worker;
|
|
17006
|
-
return () => {
|
|
17007
|
-
worker.terminate();
|
|
17008
|
-
workerRef.current = void 0;
|
|
17009
|
-
};
|
|
17010
|
-
} catch (error) {
|
|
17011
|
-
setWorkerError(error instanceof Error ? error : new Error("Failed to start the spans filter worker."));
|
|
17012
|
-
return;
|
|
17013
|
-
}
|
|
17014
|
-
}, [retryCount]);
|
|
17015
|
-
const retryWorker = (0, import_react43.useCallback)(() => {
|
|
17016
|
-
setWorkerError(void 0);
|
|
17017
|
-
setRetryCount((count2) => count2 + 1);
|
|
17018
|
-
}, []);
|
|
17019
|
-
const addPage = (0, import_react43.useCallback)((direction, projections) => {
|
|
17020
|
-
workerRef.current?.postMessage({ direction, op: "addPage", projections, seq: ++seqRef.current });
|
|
17021
|
-
}, []);
|
|
17022
|
-
const setFilters = (0, import_react43.useCallback)((filters) => {
|
|
17023
|
-
workerRef.current?.postMessage({ filters, op: "setFilters", seq: ++seqRef.current });
|
|
17024
|
-
}, []);
|
|
17025
|
-
const clear = (0, import_react43.useCallback)(() => {
|
|
17026
|
-
workerRef.current?.postMessage({ op: "clear", seq: ++seqRef.current });
|
|
17027
|
-
}, []);
|
|
17028
|
-
return { addPage, clear, result, retryWorker, setFilters, workerError };
|
|
17029
|
-
};
|
|
16965
|
+
// src/context.tsx
|
|
16966
|
+
var import_react44 = require("react");
|
|
16967
|
+
var import_react45 = require("react");
|
|
17030
16968
|
|
|
17031
|
-
// src/
|
|
17032
|
-
var
|
|
16969
|
+
// src/api/client.ts
|
|
16970
|
+
var import_semver2 = __toESM(require_semver2(), 1);
|
|
17033
16971
|
|
|
17034
16972
|
// src/config.ts
|
|
17035
16973
|
var APPINSPECTOR_API_PREFIX = "/_localstack/appinspector";
|
|
@@ -17057,13 +16995,6 @@ var getAppInspectorApiUrl = (localstackEndpoint, endpoint) => {
|
|
|
17057
16995
|
};
|
|
17058
16996
|
var LOCALSTACK_INFO_PATH = "/_localstack/info";
|
|
17059
16997
|
|
|
17060
|
-
// src/context.tsx
|
|
17061
|
-
var import_react45 = require("react");
|
|
17062
|
-
var import_react46 = require("react");
|
|
17063
|
-
|
|
17064
|
-
// src/api/client.ts
|
|
17065
|
-
var import_semver2 = __toESM(require_semver2(), 1);
|
|
17066
|
-
|
|
17067
16998
|
// src/constants/error-messages.ts
|
|
17068
16999
|
var ERROR_MESSAGES = {
|
|
17069
17000
|
APP_INSPECTOR_DISABLED: "App Inspector is currently disabled.",
|
|
@@ -17328,7 +17259,7 @@ var createApiClient = ({ localstackEndpoint }) => {
|
|
|
17328
17259
|
};
|
|
17329
17260
|
|
|
17330
17261
|
// src/hooks/use-appinspector-open-analytics.ts
|
|
17331
|
-
var
|
|
17262
|
+
var import_react43 = require("react");
|
|
17332
17263
|
|
|
17333
17264
|
// node_modules/.pnpm/ua-parser-js@2.0.9/node_modules/ua-parser-js/src/main/ua-parser.mjs
|
|
17334
17265
|
var LIBVERSION = "2.0.9";
|
|
@@ -19210,8 +19141,8 @@ var useAppInspectorOpenAnalytics = () => {
|
|
|
19210
19141
|
const api = useAppInspectorApi();
|
|
19211
19142
|
const { deploymentContainer } = useAppInspector();
|
|
19212
19143
|
const { status, statusError } = useAppInspectorStatus();
|
|
19213
|
-
const firedRef = (0,
|
|
19214
|
-
(0,
|
|
19144
|
+
const firedRef = (0, import_react43.useRef)(false);
|
|
19145
|
+
(0, import_react43.useEffect)(() => {
|
|
19215
19146
|
if (firedRef.current) {
|
|
19216
19147
|
return;
|
|
19217
19148
|
}
|
|
@@ -19232,9 +19163,9 @@ var useAppInspectorOpenAnalytics = () => {
|
|
|
19232
19163
|
|
|
19233
19164
|
// src/context.tsx
|
|
19234
19165
|
var import_jsx_runtime132 = require("react/jsx-runtime");
|
|
19235
|
-
var AppInspectorContext = (0,
|
|
19166
|
+
var AppInspectorContext = (0, import_react44.createContext)(void 0);
|
|
19236
19167
|
var useAppInspector = () => {
|
|
19237
|
-
const context = (0,
|
|
19168
|
+
const context = (0, import_react44.useContext)(AppInspectorContext);
|
|
19238
19169
|
if (!context) {
|
|
19239
19170
|
throw new Error("@localstack-studio/ui: components must be used within a <AppInspectorContextProvider>");
|
|
19240
19171
|
}
|
|
@@ -19245,11 +19176,11 @@ var AppInspectorAnalyticsBoundary = ({ children }) => {
|
|
|
19245
19176
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(import_jsx_runtime132.Fragment, { children });
|
|
19246
19177
|
};
|
|
19247
19178
|
var AppInspectorContextProvider = (props) => {
|
|
19248
|
-
const api = (0,
|
|
19179
|
+
const api = (0, import_react45.useMemo)(
|
|
19249
19180
|
() => createApiClient({ localstackEndpoint: props.localstackEndpoint }),
|
|
19250
19181
|
[props.localstackEndpoint]
|
|
19251
19182
|
);
|
|
19252
|
-
const resolveLink = (0,
|
|
19183
|
+
const resolveLink = (0, import_react45.useCallback)(
|
|
19253
19184
|
(options) => {
|
|
19254
19185
|
if (options.to === "settings") {
|
|
19255
19186
|
return `${props.routePrefix}/settings`;
|
|
@@ -19261,24 +19192,93 @@ var AppInspectorContextProvider = (props) => {
|
|
|
19261
19192
|
},
|
|
19262
19193
|
[props.routePrefix]
|
|
19263
19194
|
);
|
|
19264
|
-
const deploymentContainer = (0,
|
|
19195
|
+
const deploymentContainer = (0, import_react45.useMemo)(
|
|
19265
19196
|
() => props.deploymentContainer ?? { source: "web" },
|
|
19266
19197
|
[props.deploymentContainer]
|
|
19267
19198
|
);
|
|
19268
|
-
const contextValue = (0,
|
|
19199
|
+
const contextValue = (0, import_react45.useMemo)(
|
|
19269
19200
|
() => ({
|
|
19270
19201
|
deploymentContainer,
|
|
19271
19202
|
headerHeight: props.headerHeight ?? 0,
|
|
19272
19203
|
linkComponent: props.linkComponent,
|
|
19273
19204
|
localstackEndpoint: props.localstackEndpoint,
|
|
19274
|
-
resolveLink
|
|
19205
|
+
resolveLink,
|
|
19206
|
+
spansWorkerUrl: props.spansWorkerUrl
|
|
19275
19207
|
}),
|
|
19276
|
-
[deploymentContainer, props.headerHeight, props.linkComponent, props.localstackEndpoint, resolveLink]
|
|
19208
|
+
[deploymentContainer, props.headerHeight, props.linkComponent, props.localstackEndpoint, props.spansWorkerUrl, resolveLink]
|
|
19277
19209
|
);
|
|
19278
19210
|
return /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(ApiProvider, { api, children: /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(StatusProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(AppInspectorContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(AppInspectorAnalyticsBoundary, { children: props.children }) }) }) });
|
|
19279
19211
|
};
|
|
19280
19212
|
|
|
19213
|
+
// inline-worker:/home/runner/work/localstack-appinspector-ui/localstack-appinspector-ui/src/workers/spans-worker.worker.ts
|
|
19214
|
+
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';
|
|
19215
|
+
function createInlineWorker() {
|
|
19216
|
+
const url = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
|
|
19217
|
+
try {
|
|
19218
|
+
return new Worker(url);
|
|
19219
|
+
} finally {
|
|
19220
|
+
URL.revokeObjectURL(url);
|
|
19221
|
+
}
|
|
19222
|
+
}
|
|
19223
|
+
|
|
19224
|
+
// src/hooks/use-spans-compute.ts
|
|
19225
|
+
var EMPTY_RESULT = {
|
|
19226
|
+
consumerValues: [],
|
|
19227
|
+
errorCount: 0,
|
|
19228
|
+
hasExternalProducer: false,
|
|
19229
|
+
orderedFilteredIds: [],
|
|
19230
|
+
producerValues: [],
|
|
19231
|
+
statusValues: [],
|
|
19232
|
+
warningCount: 0
|
|
19233
|
+
};
|
|
19234
|
+
var useSpansCompute = () => {
|
|
19235
|
+
const { spansWorkerUrl } = useAppInspector();
|
|
19236
|
+
const [result, setResult] = (0, import_react46.useState)(EMPTY_RESULT);
|
|
19237
|
+
const [workerError, setWorkerError] = (0, import_react46.useState)();
|
|
19238
|
+
const [retryCount, setRetryCount] = (0, import_react46.useState)(0);
|
|
19239
|
+
const workerRef = (0, import_react46.useRef)();
|
|
19240
|
+
const seqRef = (0, import_react46.useRef)(0);
|
|
19241
|
+
const lastAppliedRef = (0, import_react46.useRef)(0);
|
|
19242
|
+
(0, import_react46.useEffect)(() => {
|
|
19243
|
+
try {
|
|
19244
|
+
const worker = spansWorkerUrl ? new Worker(spansWorkerUrl) : createInlineWorker();
|
|
19245
|
+
worker.addEventListener("message", (event) => {
|
|
19246
|
+
const { result: nextResult, seq } = event.data;
|
|
19247
|
+
if (seq < lastAppliedRef.current) return;
|
|
19248
|
+
lastAppliedRef.current = seq;
|
|
19249
|
+
setResult(nextResult);
|
|
19250
|
+
});
|
|
19251
|
+
worker.addEventListener("error", (event) => {
|
|
19252
|
+
setWorkerError(new Error(event.message || "Spans filter worker crashed."));
|
|
19253
|
+
});
|
|
19254
|
+
workerRef.current = worker;
|
|
19255
|
+
return () => {
|
|
19256
|
+
worker.terminate();
|
|
19257
|
+
workerRef.current = void 0;
|
|
19258
|
+
};
|
|
19259
|
+
} catch (error) {
|
|
19260
|
+
setWorkerError(error instanceof Error ? error : new Error("Failed to start the spans filter worker."));
|
|
19261
|
+
return;
|
|
19262
|
+
}
|
|
19263
|
+
}, [retryCount, spansWorkerUrl]);
|
|
19264
|
+
const retryWorker = (0, import_react46.useCallback)(() => {
|
|
19265
|
+
setWorkerError(void 0);
|
|
19266
|
+
setRetryCount((count2) => count2 + 1);
|
|
19267
|
+
}, []);
|
|
19268
|
+
const addPage = (0, import_react46.useCallback)((direction, projections) => {
|
|
19269
|
+
workerRef.current?.postMessage({ direction, op: "addPage", projections, seq: ++seqRef.current });
|
|
19270
|
+
}, []);
|
|
19271
|
+
const setFilters = (0, import_react46.useCallback)((filters) => {
|
|
19272
|
+
workerRef.current?.postMessage({ filters, op: "setFilters", seq: ++seqRef.current });
|
|
19273
|
+
}, []);
|
|
19274
|
+
const clear = (0, import_react46.useCallback)(() => {
|
|
19275
|
+
workerRef.current?.postMessage({ op: "clear", seq: ++seqRef.current });
|
|
19276
|
+
}, []);
|
|
19277
|
+
return { addPage, clear, result, retryWorker, setFilters, workerError };
|
|
19278
|
+
};
|
|
19279
|
+
|
|
19281
19280
|
// src/hooks/use-spans-ws.tsx
|
|
19281
|
+
var import_react47 = require("react");
|
|
19282
19282
|
var wsLogger = createScopedLogger("WS");
|
|
19283
19283
|
var isLegacySpanFields = (value) => {
|
|
19284
19284
|
return typeof value.span_id === "string" && typeof value.trace_id === "string" && typeof value.timestamp === "string";
|