All checks were successful
continuous-integration/drone/push Build is passing
133 lines
4.0 KiB
TypeScript
133 lines
4.0 KiB
TypeScript
export type ErrorCatalog = Record<string, string>;
|
|
|
|
type ErrorLike = {
|
|
code?: unknown;
|
|
status?: unknown;
|
|
message?: unknown;
|
|
rawMessage?: unknown;
|
|
};
|
|
|
|
export type ResolveErrorMessageOptions = {
|
|
code?: string | null;
|
|
status?: number | null;
|
|
context?: string;
|
|
fallbackMessage?: string | null;
|
|
};
|
|
|
|
export type CreateErrorResolverConfig = {
|
|
catalog: ErrorCatalog;
|
|
fallbackCode?: string;
|
|
defaultContext?: string;
|
|
contextOverrides?: Record<string, Partial<Record<string, string>>>;
|
|
inferCodeFromStatus?: (status?: number | null) => string | undefined;
|
|
inferCodeFromLegacyMessage?: (message?: string | null) => string | undefined;
|
|
};
|
|
|
|
export function createErrorResolver(config: CreateErrorResolverConfig) {
|
|
const {
|
|
catalog,
|
|
fallbackCode,
|
|
defaultContext = 'default',
|
|
contextOverrides = {},
|
|
inferCodeFromStatus,
|
|
inferCodeFromLegacyMessage,
|
|
} = config;
|
|
|
|
const knownCodes = new Set(Object.keys(catalog));
|
|
|
|
function isKnownErrorCode(value: string): boolean {
|
|
return knownCodes.has(value);
|
|
}
|
|
|
|
function normalizeErrorCode(code?: string | null): string | undefined {
|
|
if (!code) {
|
|
return undefined;
|
|
}
|
|
return isKnownErrorCode(code) ? code : undefined;
|
|
}
|
|
|
|
function inferErrorCodeFromStatus(status?: number | null): string | undefined {
|
|
return inferCodeFromStatus?.(status);
|
|
}
|
|
|
|
function resolveErrorMessage(options: ResolveErrorMessageOptions): string {
|
|
const { code, status, context = defaultContext, fallbackMessage } = options;
|
|
|
|
const resolvedCode =
|
|
normalizeErrorCode(code) ??
|
|
inferCodeFromLegacyMessage?.(fallbackMessage) ??
|
|
inferErrorCodeFromStatus(status);
|
|
|
|
if (resolvedCode) {
|
|
const contextMessage = contextOverrides[context]?.[resolvedCode];
|
|
if (contextMessage) {
|
|
return contextMessage;
|
|
}
|
|
const catalogMessage = catalog[resolvedCode];
|
|
if (catalogMessage) {
|
|
return catalogMessage;
|
|
}
|
|
}
|
|
|
|
const statusCode = inferErrorCodeFromStatus(status);
|
|
if (statusCode) {
|
|
const contextMessage = contextOverrides[context]?.[statusCode];
|
|
if (contextMessage) {
|
|
return contextMessage;
|
|
}
|
|
const catalogMessage = catalog[statusCode];
|
|
if (catalogMessage) {
|
|
return catalogMessage;
|
|
}
|
|
}
|
|
|
|
if (fallbackCode && catalog[fallbackCode]) {
|
|
return catalog[fallbackCode];
|
|
}
|
|
|
|
if (fallbackMessage) {
|
|
return fallbackMessage;
|
|
}
|
|
|
|
return 'Request failed. Please try again.';
|
|
}
|
|
|
|
function resolveOptionalErrorMessage(
|
|
code?: string | null,
|
|
context: string = defaultContext,
|
|
): string | undefined {
|
|
if (!code) {
|
|
return undefined;
|
|
}
|
|
return resolveErrorMessage({ code, context });
|
|
}
|
|
|
|
function toErrorMessage(err: unknown, context: string = defaultContext): string {
|
|
if (err && typeof err === 'object') {
|
|
const errorLike = err as ErrorLike;
|
|
const code = typeof errorLike.code === 'string' ? errorLike.code : undefined;
|
|
const status = typeof errorLike.status === 'number' ? errorLike.status : undefined;
|
|
const rawMessage =
|
|
typeof errorLike.rawMessage === 'string' ? errorLike.rawMessage : undefined;
|
|
const message = typeof errorLike.message === 'string' ? errorLike.message : undefined;
|
|
|
|
return resolveErrorMessage({
|
|
code,
|
|
status,
|
|
context,
|
|
fallbackMessage: rawMessage ?? message,
|
|
});
|
|
}
|
|
|
|
return resolveErrorMessage({ context });
|
|
}
|
|
|
|
return {
|
|
isKnownErrorCode,
|
|
inferErrorCodeFromStatus,
|
|
resolveErrorMessage,
|
|
resolveOptionalErrorMessage,
|
|
toErrorMessage,
|
|
};
|
|
}
|