Files
clean-architecture-frontend…/src/adapters/http/http-execution-v3.ts
T
DongHyeonkaandClaude Opus 5 4e87bacdf3 fix: enforce installed HTTP auth profiles
Install the REST auth profile registry once at composition and make it the
single transport authority for V3. Contract composition now rejects an
unregistered authProfileId, so the executor never resolves a profile at
runtime.

The credential collaborator contributes proof headers only: Fetch credentials
come from the resolved profile, transport-owned and forbidden headers are
rejected, headers outside the profile's allowed set are rejected, and a missing
required header fails closed as AUTH_INTEGRATION_FAILURE with zero fetch calls.
The final invariant re-proves credentials mode and the exact header sets.

Demo mode satisfies the strict bearer profile with a fixed non-secret marker
instead of weakening REFERENCE_EXTERNAL_BEARER. Credential owners now receive
the operation lifetime through AuthOperationContext.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 22:56:44 +09:00

1386 lines
40 KiB
TypeScript

import {
HTTP_EXECUTION_CEILINGS,
invokeValidator,
type InstalledHttpContract,
} from "../../contracts/external-contract-runtime.ts";
import type { CacheScopeSnapshot } from "../../contracts/server-state-scope.ts";
import {
defineMutationIntent,
MUTATION_INTENT_BOUNDS,
type MutationIntent,
} from "../../contracts/mutation-intent.ts";
import {
decodeJsonBytes,
isEffectivelyEmpty,
isJsonMediaType,
probeForbiddenBody,
readBoundedBytes,
} from "./bounded-body-reader.ts";
import {
admitCredentialHeaders,
checkFinalInvariants,
projectRequest,
type CredentialAdmissionFailure,
type CredentialPatchOutcome,
} from "./http-contract-bridge.ts";
import {
INSTALLED_REST_AUTH_PROFILES,
type InstalledRestAuthProfiles,
} from "../../contracts/rest-profiles.ts";
import {
certaintyForAbandonedAttempt,
classifyProblemEffect,
type PhysicalAttemptState,
} from "./http-effect-certainty.ts";
import { parseRetryAfter } from "./retry-policy.ts";
/**
* §7–§8. Descriptor-driven HTTP execution.
*
* This is not a general purpose HTTP client. A caller passes an installed
* contract and typed input; the runtime owns bounds, the total deadline, the
* single retry authority and the effect-certainty verdict.
*/
export type SafeResponseMetadata = Readonly<{
status: number;
correlationReference?: string;
retryAfterMs?: number;
validatorToken?: string;
}>;
export type HttpContractViolationKind =
| "MISSING_IDEMPOTENCY_KEY"
| "UNEXPECTED_IDEMPOTENCY_KEY"
| "UNEXPECTED_STATUS"
| "UNEXPECTED_EMPTY_BODY"
| "UNEXPECTED_BODY"
| "CONTENT_TYPE_MISMATCH"
| "RESPONSE_TOO_LARGE"
| "UTF8_INVALID"
| "JSON_INVALID"
| "SUCCESS_SCHEMA_INVALID"
| "PROBLEM_SCHEMA_INVALID"
| "VALIDATOR_RUNTIME_FAILURE"
| "MAPPING_CONTRACT_VIOLATION"
| "FINAL_REQUEST_INVARIANT_FAILED"
| "SCOPE_FENCED";
export type HttpContractViolation = Readonly<{
kind: HttpContractViolationKind;
operation: "REQUEST" | "RESPONSE" | "VALIDATION" | "MAPPING";
}>;
export type HttpEffectCertainty =
| "NOT_APPLICABLE"
| "NOT_STARTED"
| "NOT_APPLIED"
| "APPLIED_CONFIRMED"
| "MAYBE_APPLIED";
export type HttpTransportFailure = Readonly<{
kind:
| "DNS_OR_CONNECT_FAILURE"
| "TLS_OR_NETWORK_FAILURE"
| "NETWORK_FAILURE"
| "TIMEOUT"
| "RESPONSE_STREAM_FAILURE"
| "ABORTED_BY_SCOPE"
| "ABORTED_BY_CALLER"
| "OVERLOADED_BEFORE_SEND";
retryable: boolean;
}>;
export type HttpExecutionOutcome<Value, Problem> =
| Readonly<{
kind: "SUCCESS";
value: Value;
metadata: SafeResponseMetadata;
effect: "NOT_APPLICABLE" | "APPLIED_CONFIRMED";
}>
| Readonly<{
kind: "PROBLEM";
problem: Problem;
metadata: SafeResponseMetadata;
effect: "NOT_APPLIED" | "APPLIED_CONFIRMED" | "MAYBE_APPLIED";
}>
| Readonly<{
kind: "UNAUTHENTICATED";
effect: "NOT_APPLICABLE" | "NOT_APPLIED" | "MAYBE_APPLIED";
}>
| Readonly<{
kind: "FORBIDDEN";
effect: "NOT_APPLICABLE" | "NOT_APPLIED" | "MAYBE_APPLIED";
}>
| Readonly<{
kind: "RATE_LIMITED";
retryAfterMs?: number;
effect: "NOT_APPLICABLE" | "NOT_APPLIED" | "MAYBE_APPLIED";
}>
| Readonly<{
kind: "CONTRACT_VIOLATION";
violation: HttpContractViolation;
effect: HttpEffectCertainty;
}>
| Readonly<{
kind: "TRANSPORT_FAILURE";
failure: HttpTransportFailure;
effect: "NOT_STARTED" | "MAYBE_APPLIED";
}>
| Readonly<{
kind: "CANCELLED";
effect: "NOT_STARTED" | "MAYBE_APPLIED";
}>
| Readonly<{
kind: "AUTH_INTEGRATION_FAILURE";
reason: AuthIntegrationFailureReason;
effect: "NOT_APPLICABLE" | "NOT_STARTED";
}>;
/**
* §7.7 / VD-23. A configuration or collaborator contract breach, never a user
* session state. `UNAUTHENTICATED` stays reserved for the latter.
*/
export type AuthIntegrationFailureReason =
| "UNKNOWN_AUTH_PROFILE"
| CredentialAdmissionFailure;
/**
* §8.5. Credential collaborators receive the operation lifetime so a
* cooperative owner can abandon its own work; a non-cooperative one is still
* bounded by the executor's race against the same signal.
*/
export type AuthOperationContext = Readonly<{
signal: AbortSignal;
deadlineAtMonotonicMs: number;
}>;
export type CancellationOwner =
| "CALLER"
| "ROUTE_TRANSITION"
| "SCOPE_FENCE"
| "APPLICATION_SHUTDOWN"
| "DEADLINE";
export interface HttpExecutionContext {
/**
* §7.4. The low-cardinality route identity that owns this logical execution.
* It is required at the installed operation-executor boundary so a terminal
* outcome can always be attributed without reconstructing it from a URL.
*/
readonly routeId: string;
readonly signal?: AbortSignal;
readonly scope: CacheScopeSnapshot;
readonly intent?: MutationIntent;
}
export interface ContractHttpExecutor {
execute<Input, WireOutput, Problem>(
operation: InstalledHttpContract<Input, WireOutput, Problem>,
input: Input,
context: HttpExecutionContext,
): Promise<HttpExecutionOutcome<WireOutput, Problem>>;
}
/**
* §7.4 / VD-07. One typed internal record per logical execution. It is not an
* arbitrary context map: the composition root owns the projection into the
* closed diagnostics and telemetry buckets, and raw attempt count, duration and
* status never leave that projection.
*/
export type HttpExecutionObservation = Readonly<{
routeId: string;
operationId: string;
diagnosticsOperation: string;
outcome: HttpExecutionOutcome<unknown, unknown>["kind"];
errorKind: string;
status?: number;
attemptCount: number;
durationMs: number;
effect: HttpEffectCertainty;
cancellationOwner?: CancellationOwner;
/**
* The internal terminal-reason label recorded by the execution site. It is
* evidence for the HTTP scenario catalog only; the composition-root
* projection never forwards it to diagnostics or telemetry.
*/
terminalReason: string;
}>;
/**
* The outcome is the single authority for the observed error kind. The terminal
* reason only distinguishes an internal runtime failure from a transport
* failure, because both surface as the same public outcome.
*/
function observationErrorKind(
outcome: HttpExecutionOutcome<unknown, unknown>,
terminalReason: string,
): string {
switch (outcome.kind) {
case "SUCCESS":
return "NONE";
case "PROBLEM":
return "PROBLEM";
case "UNAUTHENTICATED":
return "UNAUTHENTICATED";
case "FORBIDDEN":
return "FORBIDDEN";
case "RATE_LIMITED":
return "RATE_LIMITED";
case "CONTRACT_VIOLATION":
return outcome.violation.kind;
case "TRANSPORT_FAILURE":
return terminalReason === "RUNTIME_FAILURE"
? "RUNTIME_FAILURE"
: outcome.failure.kind;
case "CANCELLED":
return "REQUEST_ABORTED";
case "AUTH_INTEGRATION_FAILURE":
return outcome.reason;
}
}
function observationStatus(
outcome: HttpExecutionOutcome<unknown, unknown>,
): number | undefined {
return outcome.kind === "SUCCESS" || outcome.kind === "PROBLEM"
? outcome.metadata.status
: undefined;
}
export type ContractHttpExecutorDependencies = Readonly<{
baseUrl: string;
/** §8.2. `MAX_RETRY_ATTEMPTS` from Runtime Config; the ceiling is still 2. */
maxRetryAttempts: number;
/** The installed profile registry; the executor never invents a profile. */
authProfiles?: InstalledRestAuthProfiles;
attachCredentials(
operation: Readonly<{
operationId: string;
authProfileId: string;
method: string;
}>,
context: AuthOperationContext,
): Promise<CredentialPatchOutcome> | CredentialPatchOutcome;
fetcher?: typeof fetch;
/** Adapter seam for the common bounded response reader. */
readBoundedResponseBytes?: typeof readBoundedBytes;
monotonicNow?: () => number;
sleep?: (ms: number, signal: AbortSignal) => Promise<void>;
random?: () => number;
observe?: (observation: HttpExecutionObservation) => void;
}>;
const RETRYABLE_STATUSES: ReadonlySet<number> = new Set([
408, 425, 429, 502, 503, 504,
]);
const RETRY_BASE_DELAY_MS = 250;
const RETRY_MAX_LOCAL_DELAY_MS = 2_000;
const RETRY_AFTER_CEILING_MS = 5_000;
type MutationIntentValidation =
| Readonly<{ ok: true; intent?: MutationIntent }>
| Readonly<{
ok: false;
violation: "MISSING_IDEMPOTENCY_KEY" | "UNEXPECTED_IDEMPOTENCY_KEY";
}>;
function validateMutationIntent(
contract: Readonly<{
operationId: string;
retrySemantics: "SAFE" | "IDEMPOTENT" | "KEYED" | "NEVER";
commandEffect: unknown | null;
}>,
intent: MutationIntent | undefined,
): MutationIntentValidation {
const isCommand = contract.commandEffect !== null;
const requiresKey = contract.retrySemantics === "KEYED";
if (!isCommand) {
return intent === undefined
? Object.freeze({ ok: true })
: Object.freeze({
ok: false,
violation: "UNEXPECTED_IDEMPOTENCY_KEY",
});
}
if (intent === undefined) {
return requiresKey
? Object.freeze({ ok: false, violation: "MISSING_IDEMPOTENCY_KEY" })
: Object.freeze({ ok: true });
}
let validated: MutationIntent;
try {
validated = defineMutationIntent(intent);
} catch {
return Object.freeze({
ok: false,
violation: requiresKey
? "MISSING_IDEMPOTENCY_KEY"
: "UNEXPECTED_IDEMPOTENCY_KEY",
});
}
if (validated.operationId !== contract.operationId) {
return Object.freeze({
ok: false,
violation: requiresKey
? "MISSING_IDEMPOTENCY_KEY"
: "UNEXPECTED_IDEMPOTENCY_KEY",
});
}
const key = validated.idempotencyKey;
if (requiresKey && !validIdempotencyKey(key)) {
return Object.freeze({
ok: false,
violation: "MISSING_IDEMPOTENCY_KEY",
});
}
if (!requiresKey && key !== undefined) {
return Object.freeze({
ok: false,
violation: "UNEXPECTED_IDEMPOTENCY_KEY",
});
}
return Object.freeze({ ok: true, intent: validated });
}
const UTF8 = new TextEncoder();
function validIdempotencyKey(value: unknown): value is string {
return (
typeof value === "string" &&
value.trim().length > 0 &&
UTF8.encode(value).byteLength <=
MUTATION_INTENT_BOUNDS.idempotencyKeyMaxBytes &&
!hasControlCharacter(value)
);
}
function hasControlCharacter(value: string): boolean {
for (const character of value) {
const codePoint = character.codePointAt(0) ?? 0;
if (
codePoint <= 0x1f ||
(codePoint >= 0x7f && codePoint <= 0x9f)
) {
return true;
}
}
return false;
}
export function createContractHttpExecutor(
dependencies: ContractHttpExecutorDependencies,
): ContractHttpExecutor {
const fetcher = dependencies.fetcher ?? fetch;
const authProfiles =
dependencies.authProfiles ?? INSTALLED_REST_AUTH_PROFILES;
const readResponseBytes =
dependencies.readBoundedResponseBytes ?? readBoundedBytes;
const now = dependencies.monotonicNow ?? (() => performance.now());
const random = dependencies.random ?? Math.random;
const sleep =
dependencies.sleep ??
((ms: number, signal: AbortSignal) =>
new Promise<void>((resolve) => {
const timer = setTimeout(resolve, ms);
signal.addEventListener(
"abort",
() => {
clearTimeout(timer);
resolve();
},
{ once: true },
);
}));
async function execute<Input, WireOutput, Problem>(
operation: InstalledHttpContract<Input, WireOutput, Problem>,
input: Input,
context: HttpExecutionContext,
): Promise<HttpExecutionOutcome<WireOutput, Problem>> {
const contract = operation.contract;
const policy = operation.frontend;
const isCommand = contract.commandEffect !== null;
// §8.5. One monotonic deadline covers credential resolution, encoding,
// backoff, every physical attempt, body read and validation.
const startedAt = now();
const deadlineAt = startedAt + policy.totalDeadlineMs;
const remaining = () => deadlineAt - now();
let attemptState: PhysicalAttemptState = "PREPARING";
let attempts = 0;
let terminalCancellation: CancellationOwner | null = null;
const lifetimeController = new AbortController();
const forwardCallerToLifetime = () => {
terminalCancellation ??= "CALLER";
lifetimeController.abort();
};
const forwardScopeToLifetime = () => {
terminalCancellation ??= "SCOPE_FENCE";
lifetimeController.abort();
};
const callerSignal =
context.signal === context.scope.signal ? undefined : context.signal;
if (callerSignal?.aborted) forwardCallerToLifetime();
else {
callerSignal?.addEventListener("abort", forwardCallerToLifetime, {
once: true,
});
}
if (context.scope.signal.aborted) forwardScopeToLifetime();
else {
context.scope.signal.addEventListener("abort", forwardScopeToLifetime, {
once: true,
});
}
const lifetimeDeadlineTimer = setTimeout(() => {
terminalCancellation ??= "DEADLINE";
lifetimeController.abort();
}, policy.totalDeadlineMs);
let lifetimeDisposed = false;
const disposeLifetime = () => {
if (lifetimeDisposed) return;
lifetimeDisposed = true;
clearTimeout(lifetimeDeadlineTimer);
callerSignal?.removeEventListener("abort", forwardCallerToLifetime);
context.scope.signal.removeEventListener("abort", forwardScopeToLifetime);
lifetimeController.abort();
};
const finish = (
outcome: HttpExecutionOutcome<WireOutput, Problem>,
terminalReason: string,
): HttpExecutionOutcome<WireOutput, Problem> => {
disposeLifetime();
try {
const status = observationStatus(outcome);
dependencies.observe?.(
Object.freeze({
routeId: context.routeId,
operationId: contract.operationId,
diagnosticsOperation: policy.diagnosticsOperation,
outcome: outcome.kind,
errorKind: observationErrorKind(outcome, terminalReason),
...(status === undefined ? {} : { status }),
attemptCount: attempts,
durationMs: Math.max(0, now() - startedAt),
effect: outcome.effect,
terminalReason,
...(terminalCancellation === null
? {}
: { cancellationOwner: terminalCancellation }),
}),
);
} catch {
// Observation is outside the execution authority.
}
return outcome;
};
try {
// §7.7. The installed registry is the only source of a profile. Composition
// already rejects unknown identities; this is the runtime fail-close.
const authProfile = authProfiles.get(policy.authProfileId);
if (!authProfile) {
return finish(
authIntegrationFailure("UNKNOWN_AUTH_PROFILE", isCommand),
"AUTH_INTEGRATION_FAILURE",
);
}
// §7.4 step 1-2: capture the scope and verify it is still current.
if (!context.scope.isCurrent()) {
return finish(scopeFenced(preDispatchEffect(isCommand)), "NOT_STARTED");
}
const intentValidation = validateMutationIntent(contract, context.intent);
if (!intentValidation.ok) {
return finish(
violation(intentValidation.violation, "REQUEST", "NOT_STARTED"),
"NOT_STARTED",
);
}
const validated = invokeValidator(contract.inputValidator, input);
if (validated.outcome === "THROWN") {
return finish(
violation(
"VALIDATOR_RUNTIME_FAILURE",
"VALIDATION",
preDispatchEffect(isCommand),
),
"NOT_STARTED",
);
}
if (validated.outcome === "INVALID") {
return finish(
violation(
"FINAL_REQUEST_INVARIANT_FAILED",
"REQUEST",
preDispatchEffect(isCommand),
),
"NOT_STARTED",
);
}
const projected = projectRequest(
operation,
validated.value,
dependencies.baseUrl,
);
if (!projected.ok) {
return finish(
violation(
"FINAL_REQUEST_INVARIANT_FAILED",
"REQUEST",
preDispatchEffect(isCommand),
),
"NOT_STARTED",
);
}
// §7.7 / §8.4. Credentials are resolved before send. A response 401 is
// terminal; there is no hidden refresh-and-replay.
let patchOperation: Promise<CredentialPatchOutcome>;
try {
patchOperation = Promise.resolve(
dependencies.attachCredentials(
{
operationId: contract.operationId,
authProfileId: policy.authProfileId,
method: contract.method,
},
Object.freeze({
signal: lifetimeController.signal,
deadlineAtMonotonicMs: deadlineAt,
}),
),
);
} catch {
return finish(unauthenticated("NOT_STARTED", isCommand), "NOT_STARTED");
}
const patchResult = await awaitWithAbort(
patchOperation,
lifetimeController.signal,
);
if (patchResult === ABORTED) {
if (terminalCancellation === "SCOPE_FENCE") {
return finish(
transportFailure(
"ABORTED_BY_SCOPE",
false,
attemptState,
isCommand,
),
"SCOPE_FENCED",
);
}
return terminalCancellation === "CALLER"
? finish(cancelled("NOT_STARTED"), "CANCELLED")
: finish(
transportFailure(
"TIMEOUT",
false,
attemptState,
isCommand,
),
"TIMEOUT",
);
}
const patch = patchResult;
if (patch.kind === "SCOPE_FENCED") {
return finish(scopeFenced(preDispatchEffect(isCommand)), "NOT_STARTED");
}
if (patch.kind !== "READY") {
// A missing credential never downgrades into an anonymous request.
return finish(unauthenticated("NOT_STARTED", isCommand), "NOT_STARTED");
}
// The idempotency key is contract-owned, so a credential owner supplying it
// stays the more specific request-contract violation.
if (
Object.keys(patch.headers).some(
(name) => name.toLowerCase() === "idempotency-key",
)
) {
return finish(
violation("UNEXPECTED_IDEMPOTENCY_KEY", "REQUEST", "NOT_STARTED"),
"NOT_STARTED",
);
}
// §7.7. The profile, not the patch, decides what may travel. Rejection here
// means zero fetch calls.
const admission = admitCredentialHeaders(patch.headers, authProfile);
if (!admission.ok) {
return finish(
authIntegrationFailure(admission.failure, isCommand),
"AUTH_INTEGRATION_FAILURE",
);
}
// Transport-owned headers are written last so no credential entry can
// shadow Accept or Content-Type through key ordering.
const headers: Record<string, string> = {
...admission.headers,
Accept: "application/json",
};
if (contract.requestBody === "JSON") {
headers["Content-Type"] = "application/json";
}
if (intentValidation.intent?.idempotencyKey !== undefined) {
headers["Idempotency-Key"] = intentValidation.intent.idempotencyKey;
}
const retryCeiling = Math.min(
policy.retryBudget,
dependencies.maxRetryAttempts,
HTTP_EXECUTION_CEILINGS.hardRetryCount,
);
for (let retryIndex = 0; ; retryIndex += 1) {
if (callerSignal?.aborted) {
terminalCancellation ??= "CALLER";
return finish(
cancelled(certaintyForAbandonedAttempt(attemptState, isCommand)),
"CANCELLED",
);
}
if (!context.scope.isCurrent()) {
terminalCancellation ??= "SCOPE_FENCE";
return finish(
scopeFenced(contractViolationEffect(attemptState, isCommand)),
"SCOPE_FENCED",
);
}
const controller = new AbortController();
const budget = remaining();
if (budget <= 0) {
return finish(
transportFailure("TIMEOUT", false, attemptState, isCommand),
"TIMEOUT",
);
}
const deadlineTimer = setTimeout(() => {
terminalCancellation ??= "DEADLINE";
controller.abort();
}, budget);
const forwardLifetime = () => {
controller.abort();
};
if (lifetimeController.signal.aborted) forwardLifetime();
else {
lifetimeController.signal.addEventListener("abort", forwardLifetime, {
once: true,
});
}
const init: RequestInit = {
method: contract.method,
headers,
redirect: "error",
referrerPolicy: "no-referrer",
credentials: authProfile.credentials,
cache: "no-store",
signal: controller.signal,
...(projected.request.bodyBytes
? { body: projected.request.bodyBytes.slice() }
: {}),
};
const invariantFailure = checkFinalInvariants({
request: projected.request,
expectedMethod: contract.method,
baseUrl: dependencies.baseUrl,
init,
headers,
requestByteLimit: policy.requestByteLimit,
deadlineRemainingMs: remaining(),
scopeIsCurrent: context.scope.isCurrent(),
authProfile,
});
if (invariantFailure) {
clearTimeout(deadlineTimer);
lifetimeController.signal.removeEventListener("abort", forwardLifetime);
return finish(
invariantFailure === "SCOPE_FENCED"
? scopeFenced(preDispatchEffect(isCommand))
: violation(
"FINAL_REQUEST_INVARIANT_FAILED",
"REQUEST",
preDispatchEffect(isCommand),
),
"NOT_STARTED",
);
}
let response: Response;
attemptState = "READY_TO_SEND";
try {
attempts += 1;
const pending = fetcher(projected.request.url, init);
attemptState = "DISPATCHED";
response = await pending;
attemptState = "RESPONSE_HEADERS";
} catch {
clearTimeout(deadlineTimer);
lifetimeController.signal.removeEventListener("abort", forwardLifetime);
const owner = terminalCancellation;
if (owner === "CALLER") {
return finish(
cancelled(certaintyForAbandonedAttempt(attemptState, isCommand)),
"CANCELLED",
);
}
if (owner === "SCOPE_FENCE") {
return finish(
transportFailure(
"ABORTED_BY_SCOPE",
false,
attemptState,
isCommand,
),
"SCOPE_FENCED",
);
}
const kind = owner === "DEADLINE" ? "TIMEOUT" : "NETWORK_FAILURE";
if (
owner === null &&
canRetryTransport(contract.retrySemantics, attemptState) &&
retryIndex < retryCeiling &&
remaining() > 0
) {
const delay = jitteredDelay(retryIndex, random);
if (delay < remaining()) {
const slept = await awaitWithAbort(
sleep(delay, lifetimeController.signal),
lifetimeController.signal,
);
if (slept === ABORTED) {
return terminalCancellation === "CALLER"
? finish(
cancelled(
certaintyForAbandonedAttempt(attemptState, isCommand),
),
"CANCELLED",
)
: finish(
transportFailure(
"TIMEOUT",
false,
attemptState,
isCommand,
),
"TIMEOUT",
);
}
continue;
}
}
return finish(
transportFailure(kind, false, attemptState, isCommand),
kind,
);
}
try {
const outcome = await admitResponse(
operation,
response,
context,
attemptState,
readResponseBytes,
);
attemptState = "SETTLED";
if (
outcome.retryHint &&
retryIndex < retryCeiling &&
isRetryableSemantics(contract.retrySemantics)
) {
const delay = retryDelayFor(outcome.retryAfterMs, retryIndex, random);
if (delay !== null && delay < remaining()) {
clearTimeout(deadlineTimer);
lifetimeController.signal.removeEventListener(
"abort",
forwardLifetime,
);
const slept = await awaitWithAbort(
sleep(delay, lifetimeController.signal),
lifetimeController.signal,
);
if (slept === ABORTED) {
return terminalCancellation === "CALLER"
? finish(
cancelled(
certaintyForAbandonedAttempt(attemptState, isCommand),
),
"CANCELLED",
)
: finish(
transportFailure(
"TIMEOUT",
false,
attemptState,
isCommand,
),
"TIMEOUT",
);
}
continue;
}
}
return finish(outcome.result, outcome.certainty);
} finally {
clearTimeout(deadlineTimer);
lifetimeController.signal.removeEventListener("abort", forwardLifetime);
}
}
} catch {
return finish(
transportFailure("NETWORK_FAILURE", false, attemptState, isCommand),
"RUNTIME_FAILURE",
);
} finally {
disposeLifetime();
}
}
return Object.freeze({ execute });
}
type AdmissionOutcome<Value, Problem> = Readonly<{
result: HttpExecutionOutcome<Value, Problem>;
certainty: string;
retryHint: boolean;
retryAfterMs: number | null;
}>;
/**
* §7.9. Admission order is fixed: redirect, status allowlist, media type,
* declared length, bounded stream, body policy, decode, validate, scope fence.
* `response.ok` is never used on its own.
*/
async function admitResponse<Input, WireOutput, Problem>(
operation: InstalledHttpContract<Input, WireOutput, Problem>,
response: Response,
context: HttpExecutionContext,
attemptState: PhysicalAttemptState,
readResponseBytes: typeof readBoundedBytes,
): Promise<AdmissionOutcome<WireOutput, Problem>> {
const contract = operation.contract;
const policy = operation.frontend;
const isCommand = contract.commandEffect !== null;
const status = response.status;
const retryAfterMs = parseRetryAfter(response.headers.get("retry-after"));
if (response.redirected || response.type === "opaqueredirect") {
await response.body?.cancel().catch(() => {});
return settled(
violation(
"UNEXPECTED_STATUS",
"RESPONSE",
postDispatchEffect(isCommand),
),
"CONTRACT_VIOLATION",
);
}
const metadata = safeMetadata(response, retryAfterMs);
if (!contract.acceptedStatuses.includes(status)) {
if (status === 401) {
await response.body?.cancel().catch(() => {});
return settled(
unauthenticated(
certaintyForAbandonedAttempt(attemptState, isCommand),
isCommand,
),
"UNAUTHENTICATED",
);
}
if (status === 403) {
await response.body?.cancel().catch(() => {});
return settled(
forbidden(certaintyForAbandonedAttempt(attemptState, isCommand), isCommand),
"FORBIDDEN",
);
}
if (status === 429) {
await response.body?.cancel().catch(() => {});
return Object.freeze({
result: Object.freeze({
kind: "RATE_LIMITED" as const,
...(retryAfterMs === null ? {} : { retryAfterMs }),
effect: normalizeOptionalEffect(
certaintyForAbandonedAttempt(attemptState, isCommand),
isCommand,
),
}),
certainty: "RATE_LIMITED",
retryHint: true,
retryAfterMs,
});
}
return admitProblem(
operation,
response,
status,
metadata,
attemptState,
readResponseBytes,
);
}
// Success status: body policy first.
if (contract.responseBody === "NONE") {
const probe = await probeForbiddenBody(response);
if (!probe.ok) {
return settled(
transportFailure(
"RESPONSE_STREAM_FAILURE",
false,
attemptState,
isCommand,
),
probe.code,
);
}
if (probe.present) {
return settled(
violation("UNEXPECTED_BODY", "RESPONSE", postDispatchEffect(isCommand)),
"CONTRACT_VIOLATION",
);
}
if (!context.scope.isCurrent()) {
return settled(
scopeFenced(postDispatchEffect(isCommand)),
"SCOPE_FENCED",
);
}
return settled(
Object.freeze({
kind: "SUCCESS" as const,
value: undefined as WireOutput,
metadata,
effect: (isCommand
? "APPLIED_CONFIRMED"
: "NOT_APPLICABLE") as "NOT_APPLICABLE" | "APPLIED_CONFIRMED",
}),
isCommand ? "APPLIED_CONFIRMED" : "NOT_APPLICABLE",
);
}
const emptyAllowed = contract.emptyBodyStatuses.includes(status);
const mediaOk = isJsonMediaType(response.headers.get("content-type"));
const bytes = await readResponseBytes(response, policy.responseByteLimit);
if (!bytes.ok) {
return settled(
bytes.code === "RESPONSE_TOO_LARGE"
? violation(
"RESPONSE_TOO_LARGE",
"RESPONSE",
postDispatchEffect(isCommand),
)
: transportFailure(
"RESPONSE_STREAM_FAILURE",
false,
attemptState,
isCommand,
),
bytes.code,
);
}
const empty = isEffectivelyEmpty(bytes.bytes);
if (empty) {
if (contract.responseBody === "REQUIRED_JSON" || !emptyAllowed) {
return settled(
violation(
"UNEXPECTED_EMPTY_BODY",
"RESPONSE",
postDispatchEffect(isCommand),
),
"CONTRACT_VIOLATION",
);
}
if (!context.scope.isCurrent()) {
return settled(
scopeFenced(postDispatchEffect(isCommand)),
"SCOPE_FENCED",
);
}
return settled(
Object.freeze({
kind: "SUCCESS" as const,
value: undefined as WireOutput,
metadata,
effect: (isCommand
? "APPLIED_CONFIRMED"
: "NOT_APPLICABLE") as "NOT_APPLICABLE" | "APPLIED_CONFIRMED",
}),
isCommand ? "APPLIED_CONFIRMED" : "NOT_APPLICABLE",
);
}
if (!mediaOk) {
return settled(
violation(
"CONTENT_TYPE_MISMATCH",
"RESPONSE",
postDispatchEffect(isCommand),
),
"CONTRACT_VIOLATION",
);
}
const decoded = decodeJsonBytes(bytes.bytes);
if (!decoded.ok) {
return settled(
violation(decoded.code, "RESPONSE", postDispatchEffect(isCommand)),
"CONTRACT_VIOLATION",
);
}
const validated = invokeValidator(contract.outputValidator, decoded.value);
if (validated.outcome === "THROWN") {
return settled(
violation(
"VALIDATOR_RUNTIME_FAILURE",
"VALIDATION",
postDispatchEffect(isCommand),
),
"CONTRACT_VIOLATION",
);
}
if (validated.outcome === "INVALID") {
return settled(
violation(
"SUCCESS_SCHEMA_INVALID",
"VALIDATION",
postDispatchEffect(isCommand),
),
"CONTRACT_VIOLATION",
);
}
// §10.8. A successful response for a stale scope is discarded, not committed.
if (!context.scope.isCurrent()) {
return settled(
scopeFenced(postDispatchEffect(isCommand)),
"SCOPE_FENCED",
);
}
return settled(
Object.freeze({
kind: "SUCCESS" as const,
value: validated.value,
metadata,
effect: (isCommand
? "APPLIED_CONFIRMED"
: "NOT_APPLICABLE") as "NOT_APPLICABLE" | "APPLIED_CONFIRMED",
}),
isCommand ? "APPLIED_CONFIRMED" : "NOT_APPLICABLE",
);
}
const ABORTED = Symbol("http-operation-aborted");
async function awaitWithAbort<Value>(
operation: Promise<Value>,
signal: AbortSignal,
): Promise<Value | typeof ABORTED> {
if (signal.aborted) return ABORTED;
let onAbort: (() => void) | undefined;
const aborted = new Promise<typeof ABORTED>((resolve) => {
onAbort = () => resolve(ABORTED);
signal.addEventListener("abort", onAbort, { once: true });
});
try {
return await Promise.race([operation, aborted]);
} finally {
if (onAbort) signal.removeEventListener("abort", onAbort);
}
}
async function admitProblem<Input, WireOutput, Problem>(
operation: InstalledHttpContract<Input, WireOutput, Problem>,
response: Response,
status: number,
metadata: SafeResponseMetadata,
attemptState: PhysicalAttemptState,
readResponseBytes: typeof readBoundedBytes,
): Promise<AdmissionOutcome<WireOutput, Problem>> {
const contract = operation.contract;
const isCommand = contract.commandEffect !== null;
const retryable = RETRYABLE_STATUSES.has(status);
const bytes = await readResponseBytes(
response,
HTTP_EXECUTION_CEILINGS.problemResponseBytes,
);
if (!bytes.ok || isEffectivelyEmpty(bytes.bytes)) {
// An unclassifiable failure stays uncertain for a command.
return Object.freeze({
result: violation<WireOutput, Problem>(
"PROBLEM_SCHEMA_INVALID",
"VALIDATION",
postDispatchEffect(isCommand),
),
certainty: isCommand ? "MAYBE_APPLIED" : "NOT_STARTED",
retryHint: retryable,
retryAfterMs: metadata.retryAfterMs ?? null,
});
}
if (!isJsonMediaType(response.headers.get("content-type"))) {
return Object.freeze({
result: violation<WireOutput, Problem>(
"CONTENT_TYPE_MISMATCH",
"RESPONSE",
postDispatchEffect(isCommand),
),
certainty: isCommand ? "MAYBE_APPLIED" : "NOT_STARTED",
retryHint: retryable,
retryAfterMs: metadata.retryAfterMs ?? null,
});
}
const decoded = decodeJsonBytes(bytes.bytes);
if (!decoded.ok) {
return Object.freeze({
result: violation<WireOutput, Problem>(
decoded.code,
"RESPONSE",
postDispatchEffect(isCommand),
),
certainty: isCommand ? "MAYBE_APPLIED" : "NOT_STARTED",
retryHint: retryable,
retryAfterMs: metadata.retryAfterMs ?? null,
});
}
const validated = invokeValidator(contract.problemValidator, decoded.value);
if (validated.outcome !== "VALID") {
return Object.freeze({
result: violation<WireOutput, Problem>(
validated.outcome === "THROWN"
? "VALIDATOR_RUNTIME_FAILURE"
: "PROBLEM_SCHEMA_INVALID",
"VALIDATION",
postDispatchEffect(isCommand),
),
certainty: isCommand ? "MAYBE_APPLIED" : "NOT_STARTED",
retryHint: retryable,
retryAfterMs: metadata.retryAfterMs ?? null,
});
}
const classification = classifyProblemEffect({
status,
problem: validated.value,
descriptor: contract.commandEffect,
});
const effect = isCommand
? classification.effect
: certaintyForAbandonedAttempt(attemptState, false);
return Object.freeze({
result: Object.freeze({
kind: "PROBLEM" as const,
problem: validated.value,
metadata,
effect: (effect === "NOT_STARTED"
? "NOT_APPLIED"
: effect) as "NOT_APPLIED" | "APPLIED_CONFIRMED" | "MAYBE_APPLIED",
}),
certainty: effect,
retryHint: retryable && effect !== "MAYBE_APPLIED",
retryAfterMs: metadata.retryAfterMs ?? null,
});
}
/**
* §8.3. `SAFE` and `IDEMPOTENT` may replay the same frozen request. `KEYED`
* must not automatically retry once an attempt was dispatched and its response
* was lost; that path goes to inspect/reconciliation instead. `NEVER` is zero.
*/
function canRetryTransport(
semantics: InstalledHttpContract<
unknown,
unknown,
unknown
>["contract"]["retrySemantics"],
attemptState: PhysicalAttemptState,
): boolean {
if (semantics === "NEVER") return false;
if (semantics === "KEYED") {
return attemptState === "PREPARING" || attemptState === "READY_TO_SEND";
}
return true;
}
function isRetryableSemantics(
semantics: InstalledHttpContract<
unknown,
unknown,
unknown
>["contract"]["retrySemantics"],
): boolean {
return semantics === "SAFE" || semantics === "IDEMPOTENT";
}
/** §8.2. Full jitter over `min(2000, 250 * 2^index)`. */
function jitteredDelay(retryIndex: number, random: () => number): number {
const ceiling = Math.min(
RETRY_MAX_LOCAL_DELAY_MS,
RETRY_BASE_DELAY_MS * 2 ** retryIndex,
);
return Math.floor(random() * ceiling);
}
function retryDelayFor(
retryAfterMs: number | null,
retryIndex: number,
random: () => number,
): number | null {
const local = jitteredDelay(retryIndex, random);
if (retryAfterMs === null) return local;
if (retryAfterMs > RETRY_AFTER_CEILING_MS) return null;
return Math.max(local, retryAfterMs);
}
/** §7.12. No raw header map, URL, cookie, traceparent or ETag value escapes. */
function safeMetadata(
response: Response,
retryAfterMs: number | null,
): SafeResponseMetadata {
const correlation = response.headers.get("x-correlation-id");
return Object.freeze({
status: response.status,
...(correlation && /^[A-Za-z0-9._:-]{1,128}$/.test(correlation)
? { correlationReference: correlation }
: {}),
...(retryAfterMs === null ? {} : { retryAfterMs }),
});
}
function settled<Value, Problem>(
result: HttpExecutionOutcome<Value, Problem>,
certainty: string,
): AdmissionOutcome<Value, Problem> {
return Object.freeze({
result,
certainty,
retryHint: false,
retryAfterMs: null,
});
}
function violation<Value, Problem>(
kind: HttpContractViolationKind,
operation: HttpContractViolation["operation"],
effect: HttpEffectCertainty,
): HttpExecutionOutcome<Value, Problem> {
return Object.freeze({
kind: "CONTRACT_VIOLATION" as const,
violation: Object.freeze({ kind, operation }),
effect,
});
}
function scopeFenced<Value, Problem>(
effect: HttpEffectCertainty,
): HttpExecutionOutcome<Value, Problem> {
return violation("SCOPE_FENCED", "RESPONSE", effect);
}
/**
* §7.7. A credential collaborator or profile-binding breach. It always resolves
* before dispatch, so the command effect is `NOT_STARTED` and fetch count zero.
*/
function authIntegrationFailure<Value, Problem>(
reason: AuthIntegrationFailureReason,
isCommand: boolean,
): HttpExecutionOutcome<Value, Problem> {
return Object.freeze({
kind: "AUTH_INTEGRATION_FAILURE" as const,
reason,
effect: isCommand ? ("NOT_STARTED" as const) : ("NOT_APPLICABLE" as const),
});
}
function preDispatchEffect(isCommand: boolean): HttpEffectCertainty {
return isCommand ? "NOT_STARTED" : "NOT_APPLICABLE";
}
function postDispatchEffect(isCommand: boolean): HttpEffectCertainty {
return isCommand ? "MAYBE_APPLIED" : "NOT_APPLICABLE";
}
function contractViolationEffect(
attemptState: PhysicalAttemptState,
isCommand: boolean,
): HttpEffectCertainty {
if (!isCommand) return "NOT_APPLICABLE";
return attemptState === "PREPARING" || attemptState === "READY_TO_SEND"
? "NOT_STARTED"
: "MAYBE_APPLIED";
}
function unauthenticated<Value, Problem>(
effect: string,
isCommand: boolean,
): HttpExecutionOutcome<Value, Problem> {
return Object.freeze({
kind: "UNAUTHENTICATED" as const,
effect: normalizeOptionalEffect(effect, isCommand),
});
}
function forbidden<Value, Problem>(
effect: string,
isCommand: boolean,
): HttpExecutionOutcome<Value, Problem> {
return Object.freeze({
kind: "FORBIDDEN" as const,
effect: normalizeOptionalEffect(effect, isCommand),
});
}
function cancelled<Value, Problem>(
effect: "NOT_STARTED" | "NOT_APPLIED" | "MAYBE_APPLIED" | "APPLIED_CONFIRMED",
): HttpExecutionOutcome<Value, Problem> {
return Object.freeze({
kind: "CANCELLED" as const,
effect: effect === "MAYBE_APPLIED" ? "MAYBE_APPLIED" : "NOT_STARTED",
});
}
function transportFailure<Value, Problem>(
kind: HttpTransportFailure["kind"],
retryable: boolean,
attemptState: PhysicalAttemptState,
isCommand: boolean,
): HttpExecutionOutcome<Value, Problem> {
const effect = certaintyForAbandonedAttempt(attemptState, isCommand);
return Object.freeze({
kind: "TRANSPORT_FAILURE" as const,
failure: Object.freeze({ kind, retryable }),
effect: effect === "MAYBE_APPLIED" ? "MAYBE_APPLIED" : "NOT_STARTED",
});
}
function normalizeOptionalEffect(
effect: string,
isCommand: boolean,
): "NOT_APPLICABLE" | "NOT_APPLIED" | "MAYBE_APPLIED" {
if (!isCommand) return "NOT_APPLICABLE";
return effect === "MAYBE_APPLIED" ? "MAYBE_APPLIED" : "NOT_APPLIED";
}