feat: normalize failures through a stable registry

This commit is contained in:
donghyeon-ka
2026-07-25 20:52:58 +09:00
parent a0ca15da65
commit 7f3569ce3c
4 changed files with 315 additions and 57 deletions
+4 -56
View File
@@ -1,5 +1,9 @@
import { systemClock } from "../../application/ports/clock-port.js";
import { getApiOperation } from "../../contracts/api-operations.js";
import {
createFailure as failure,
kindForStatus as statusKind,
} from "../../contracts/errors.js";
import { retryDelay, shouldRetry } from "./retry-policy.js";
import {
validateEnvelope,
@@ -36,16 +40,6 @@ const noAuthSession =
* { ok: false, error: HttpFailure }} HttpResult
*/
/**
* @typedef {{
* code?: string,
* httpStatus?: number,
* requestId?: string,
* traceId?: string,
* retryAfterMs?: number
* }} FailureDetails
*/
/**
* @param {{
* baseUrl: string,
@@ -378,52 +372,6 @@ async function recoverSession(authSession, operation, originalFailure) {
* @param {FailureDetails} [details]
* @returns {HttpFailure}
*/
function failure(kind, operationId, attempt, details = {}) {
const retryable = new Set([
"NETWORK_UNREACHABLE",
"REQUEST_TIMEOUT",
"RATE_LIMITED",
"SERVER_FAILURE",
]).has(kind);
const action =
kind === "AUTH_REQUIRED"
? "reauth"
: retryable
? "retry"
: kind === "REQUEST_ABORTED"
? "none"
: "contact-support";
return Object.freeze({
kind,
code: details.code ?? kind,
retryable,
operationId,
attemptCount: attempt + 1,
...(details.httpStatus === undefined ? {} : { httpStatus: details.httpStatus }),
...(details.requestId ? { requestId: details.requestId } : {}),
...(details.traceId ? { traceId: details.traceId } : {}),
...(details.retryAfterMs === undefined
? {}
: { retryAfterMs: details.retryAfterMs }),
userMessageKey: `error.${kind.toLowerCase()}`,
action,
});
}
/** @param {number} status */
function statusKind(status) {
if (status === 401) return "AUTH_REQUIRED";
if (status === 403) return "FORBIDDEN";
if (status === 404) return "NOT_FOUND";
if (status === 409) return "CONFLICT";
if (status === 422) return "VALIDATION_REJECTED";
if (status === 429) return "RATE_LIMITED";
if (status >= 500) return "SERVER_FAILURE";
if (status >= 400) return "UNKNOWN_CLIENT_FAILURE";
return "ENVELOPE_MISMATCH";
}
/** @param {unknown} envelope */
function safeBackendCode(envelope) {
if (!envelope || typeof envelope !== "object") return "HTTP_FAILURE";