blob: 5b2172e4f946b3532634f33166ae9112dafdb0cd [file]
import { errorMessage as elementsErrorMessage } from '../../../elements-sk/modules/errorMessage';
import { CountMetric, telemetry } from '../telemetry/telemetry';
import { TelemetryErrorOptions } from '../telemetry/types';
/**
* Helper method to convert different error body types into a string.
*/
export const convertToErrorString = (
errorBody: string | { message: string } | { resp: Response } | object
): string => {
if (typeof errorBody === 'string') {
return errorBody;
}
if (typeof errorBody === 'object' && errorBody !== null) {
if ('message' in errorBody) {
return (errorBody as { message: string }).message;
}
if ('resp' in errorBody && (errorBody as { resp: Response }).resp instanceof window.Response) {
return (errorBody as { resp: Response }).resp.statusText;
}
}
try {
return JSON.stringify(errorBody);
} catch (e) {
return `Failed to report log message from frontend: ${(e as Error).message}`;
}
};
/**
* errorMessage dispatches an event with the error message in it.
* It also optionally tracks error occurrences via a telemetry system
* and logs the error to the server if a source is provided.
*
* duration default to 0, which means the toast doesn't close automatically.
*/
export const errorMessage = (
message: string | { message: string } | { resp: Response } | object,
duration: number = 0,
options?: TelemetryErrorOptions
): void => {
if (options) {
const source = options.source || 'default';
const errorCode =
options.errorCode ||
(isMessageWithResponse(message) ? message.resp.status.toString() : '500');
// 1. Log the full high-cardinality metadata to the backend
telemetry.reportErrorToServer(convertToErrorString(message), options);
// 2. Increment the time-series metric counter with safe tags
const metricName = options.countMetricSource || CountMetric.FrontendErrorReported;
telemetry.increaseCounter(metricName, {
source: source,
errorCode: errorCode,
});
}
// 3. Display the UI toast
elementsErrorMessage(message, duration);
};
/**
* Type guard to check if an unknown object contains a valid Fetch API Response.
*/
function isMessageWithResponse(msg: unknown): msg is { resp: Response } {
return (
typeof msg === 'object' &&
msg !== null &&
'resp' in msg &&
(msg as Record<string, unknown>).resp instanceof Response
);
}