Files
clean-architecture-frontend…/src/adapters/http/http-execution-v3.ts
T
DongHyeonkaandClaude Opus 5 f4bfdf0365 fix: close the live V3 authority findings from the adapter re-review
LIVE-01. A credential collaborator that returns UNAVAILABLE, throws, rejects
or answers off-contract is an outage of the auth integration, not evidence
about the user's session. Each of those now closes as AUTH_INTEGRATION_FAILURE
with zero fetches, so the composition root's logout path stays reserved for a
genuinely absent session. The synchronous and asynchronous failure sites share
one classifier.

LIVE-02 / LIVE-03. Object.freeze(new Map(...)) freezes the wrapper, not the
backing store, so an exported registry could still be cleared or replaced after
composition. Both the installed REST auth profile registry and the composed
HTTP/event lookups are now read facades over private stores, and every composed
row is an exact own-data snapshot that rejects accessors, inherited and
symbol-keyed fields.

LIVE-04. The total deadline now bounds the physical waits rather than being
checked between them: dispatch and response admission race the attempt signal,
the bounded reader takes that signal, and an abandoned operation is still
observed once so a late native rejection cannot surface unhandled. A body that
completes after the deadline or the caller owns the execution is no longer
admitted; a stale generation keeps its more specific SCOPE_FENCED verdict.

LIVE-05. DEADLINE is no longer treated as a caller-owned cancellation, so a
timeout reaches api.request.failed exactly once while caller, route, scope and
shutdown aborts stay excluded.

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

1556 lines
47 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,
joinMutationEffectCertainty,
type MutationEffectCertainty,
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"
/**
* LIVE-01. The collaborator answered that the auth system itself cannot serve
* this request. That is an outage of the integration, not a statement about
* the user's session, so it must never reach the composition root's logout
* path.
*/
| "CREDENTIAL_OWNER_UNAVAILABLE"
/** LIVE-01. The collaborator threw, rejected, or answered off-contract. */
| "CREDENTIAL_OWNER_FAILED"
| 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;
/**
* §8.7 / D-01. Per-attempt state stays local to the attempt; this monotonic
* accumulator is the logical execution history. A retry that has not been
* dispatched can never lower what an earlier attempt already established.
*/
let logicalCertainty: MutationEffectCertainty = "NOT_STARTED";
const observeCertainty = (observed: MutationEffectCertainty) => {
logicalCertainty = joinMutationEffectCertainty(logicalCertainty, observed);
return logicalCertainty;
};
/** Pre-dispatch failures read the accumulator, never a fresh attempt. */
const logicalPreDispatchEffect = (): HttpEffectCertainty =>
isCommand ? logicalCertainty : "NOT_APPLICABLE";
const abandonedCertainty = (): MutationEffectCertainty =>
observeCertainty(certaintyForAbandonedAttempt(attemptState, isCommand));
const abandonedTransportFailure = (
kind: HttpTransportFailure["kind"],
): HttpExecutionOutcome<WireOutput, Problem> =>
transportFailure(kind, false, abandonedCertainty());
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.
//
// LIVE-01. A synchronous throw and an asynchronous rejection are the same
// event seen from two call sites, so one classifier owns both. Neither is
// evidence about the user's session.
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(
authIntegrationFailure("CREDENTIAL_OWNER_FAILED", isCommand),
"AUTH_INTEGRATION_FAILURE",
);
}
let patchResult: CredentialPatchOutcome | typeof ABORTED;
try {
patchResult = await awaitWithAbort(
patchOperation,
lifetimeController.signal,
);
} catch {
return finish(
authIntegrationFailure("CREDENTIAL_OWNER_FAILED", isCommand),
"AUTH_INTEGRATION_FAILURE",
);
}
if (patchResult === ABORTED) {
if (terminalCancellation === "SCOPE_FENCE") {
return finish(
abandonedTransportFailure("ABORTED_BY_SCOPE"),
"SCOPE_FENCED",
);
}
return terminalCancellation === "CALLER"
? finish(cancelled("NOT_STARTED"), "CANCELLED")
: finish(
abandonedTransportFailure("TIMEOUT"),
"TIMEOUT",
);
}
const patch = patchResult;
if (patch?.kind === "SCOPE_FENCED") {
return finish(scopeFenced(preDispatchEffect(isCommand)), "NOT_STARTED");
}
if (patch?.kind === "UNAUTHENTICATED") {
// A missing credential never downgrades into an anonymous request.
return finish(unauthenticated("NOT_STARTED", isCommand), "NOT_STARTED");
}
if (patch?.kind === "UNAVAILABLE") {
return finish(
authIntegrationFailure("CREDENTIAL_OWNER_UNAVAILABLE", isCommand),
"AUTH_INTEGRATION_FAILURE",
);
}
if (
patch?.kind !== "READY" ||
patch.headers === null ||
typeof patch.headers !== "object"
) {
// An off-contract answer is a collaborator breach, never a session
// verdict the caller may act on.
return finish(
authIntegrationFailure("CREDENTIAL_OWNER_FAILED", isCommand),
"AUTH_INTEGRATION_FAILURE",
);
}
// 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(abandonedCertainty()),
"CANCELLED",
);
}
if (!context.scope.isCurrent()) {
terminalCancellation ??= "SCOPE_FENCE";
return finish(
scopeFenced(abandonedCertainty()),
"SCOPE_FENCED",
);
}
const controller = new AbortController();
const budget = remaining();
if (budget <= 0) {
return finish(
abandonedTransportFailure("TIMEOUT"),
"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(logicalPreDispatchEffect())
: violation(
"FINAL_REQUEST_INVARIANT_FAILED",
"REQUEST",
logicalPreDispatchEffect(),
),
"NOT_STARTED",
);
}
let response: Response;
attemptState = "READY_TO_SEND";
// LIVE-04. The dispatch wait is raced against the attempt signal, which
// already carries the caller, the scope fence and the total deadline. A
// `fetch` that ignores its own `signal` therefore still cannot outlive
// the operation, and a response that lands late is drained, not admitted.
let dispatch: BoundedRace<Response>;
try {
attempts += 1;
const pending = fetcher(projected.request.url, init);
attemptState = "DISPATCHED";
// D-01. Dispatch is the point of no return for the logical execution.
// No later retry may claim the command never started.
observeCertainty(certaintyForAbandonedAttempt("DISPATCHED", isCommand));
dispatch = await raceTerminal(
pending,
controller.signal,
cancelResponseBody,
);
} catch {
dispatch = REJECTED_RACE;
}
if (dispatch.kind === "VALUE") {
response = dispatch.value;
attemptState = "RESPONSE_HEADERS";
} else {
clearTimeout(deadlineTimer);
lifetimeController.signal.removeEventListener("abort", forwardLifetime);
const owner = terminalCancellation;
if (owner === "CALLER") {
return finish(
cancelled(abandonedCertainty()),
"CANCELLED",
);
}
if (owner === "SCOPE_FENCE") {
return finish(
abandonedTransportFailure("ABORTED_BY_SCOPE"),
"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(abandonedCertainty()),
"CANCELLED",
)
: finish(
abandonedTransportFailure("TIMEOUT"),
"TIMEOUT",
);
}
continue;
}
}
return finish(
abandonedTransportFailure(kind),
kind,
);
}
try {
// LIVE-04. Response admission reads a body, so it is a physical wait
// too. It is bounded by the same signal, the reader is handed that
// signal so a cooperative stream stops early, and an admission that
// completes after the terminal owner fired is discarded.
const admission = await raceTerminal(
admitResponse(
operation,
response,
context,
attemptState,
readResponseBytes,
controller.signal,
),
controller.signal,
() => cancelResponseBody(response),
);
// LIVE-04. Once response headers are in hand the request demonstrably
// reached the server, so a terminal owner that lands during admission
// keeps the dispatched classification: a stale generation stays the
// `SCOPE_FENCED` contract violation it has always been, and only the
// deadline and the caller reclassify the outcome.
const abandonAdmission = ():
| HttpExecutionOutcome<WireOutput, Problem>
| null => {
switch (terminalCancellation) {
case "DEADLINE":
return finish(abandonedTransportFailure("TIMEOUT"), "TIMEOUT");
case "CALLER":
return finish(cancelled(abandonedCertainty()), "CANCELLED");
case "SCOPE_FENCE":
return finish(
scopeFenced(abandonedCertainty()),
"SCOPE_FENCED",
);
default:
return null;
}
};
if (admission.kind !== "VALUE") {
cancelResponseBody(response);
return (
abandonAdmission() ??
finish(
abandonedTransportFailure("NETWORK_FAILURE"),
"NETWORK_FAILURE",
)
);
}
const outcome = admission.value;
attemptState = "SETTLED";
const abandoned = abandonAdmission();
if (abandoned) return abandoned;
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(abandonedCertainty()),
"CANCELLED",
)
: finish(
abandonedTransportFailure("TIMEOUT"),
"TIMEOUT",
);
}
continue;
}
}
return finish(outcome.result, outcome.certainty);
} finally {
clearTimeout(deadlineTimer);
lifetimeController.signal.removeEventListener("abort", forwardLifetime);
}
}
} catch {
return finish(
abandonedTransportFailure("NETWORK_FAILURE"),
"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,
signal: AbortSignal,
): 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,
signal,
);
}
// Success status: body policy first.
if (contract.responseBody === "NONE") {
const probe = await probeForbiddenBody(response);
if (!probe.ok) {
return settled(
transportFailure(
"RESPONSE_STREAM_FAILURE",
false,
certaintyForAbandonedAttempt(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,
signal,
);
if (!bytes.ok) {
return settled(
bytes.code === "RESPONSE_TOO_LARGE"
? violation(
"RESPONSE_TOO_LARGE",
"RESPONSE",
postDispatchEffect(isCommand),
)
: transportFailure(
"RESPONSE_STREAM_FAILURE",
false,
certaintyForAbandonedAttempt(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",
);
}
/**
* LIVE-04. The outcome of a physical wait that the operation's terminal signal
* bounds.
*
* `REJECTED` is kept distinct from `TERMINAL` on purpose: a collaborator's own
* rejection is evidence about the request, and forging it into a cancellation
* state would erase the reason the attempt actually failed.
*/
type BoundedRace<Value> =
| Readonly<{ kind: "VALUE"; value: Value }>
| Readonly<{ kind: "REJECTED" }>
| Readonly<{ kind: "TERMINAL" }>;
const TERMINAL_RACE: BoundedRace<never> = Object.freeze({
kind: "TERMINAL" as const,
});
const REJECTED_RACE: BoundedRace<never> = Object.freeze({
kind: "REJECTED" as const,
});
/**
* LIVE-04. Races a physical operation against the terminal signal so a
* non-cooperative `fetch` or reader cannot hold the port result open past the
* total deadline.
*
* Two properties matter beyond the race itself. A value that arrives while the
* terminal owner has already fired is *late*, so it is compensated rather than
* admitted. And the abandoned operation is still observed exactly once, so a
* late native rejection never surfaces as an unhandled rejection.
*/
async function raceTerminal<Value>(
operation: Promise<Value>,
signal: AbortSignal,
compensate: (value: Value) => void,
): Promise<BoundedRace<Value>> {
let landed: BoundedRace<Value> | null = null;
const settled: Promise<BoundedRace<Value>> = operation.then(
(value) => (landed = Object.freeze({ kind: "VALUE" as const, value })),
() => (landed = REJECTED_RACE),
);
const observeLate = () => {
void settled.then((outcome) => {
if (outcome.kind !== "VALUE") return;
try {
compensate(outcome.value);
} catch {
// Compensation is outside the execution authority.
}
});
};
let onAbort: (() => void) | undefined;
const terminal = new Promise<BoundedRace<Value>>((resolve) => {
if (signal.aborted) {
resolve(TERMINAL_RACE);
return;
}
onAbort = () => resolve(TERMINAL_RACE);
signal.addEventListener("abort", onAbort, { once: true });
});
try {
const winner = await Promise.race([settled, terminal]);
if (winner !== TERMINAL_RACE) return winner;
// The terminal owner reached the await first. Drain the microtask queue
// once so an operation that had *already* settled can still hand over its
// value: a microtask turn cannot be extended by a collaborator that has
// not settled, so a non-cooperative operation is still abandoned here.
for (let turn = 0; turn < 4 && landed === null; turn += 1) {
await Promise.resolve();
}
if (landed !== null) return landed;
observeLate();
return TERMINAL_RACE;
} finally {
if (onAbort) signal.removeEventListener("abort", onAbort);
}
}
function cancelResponseBody(response: Response): void {
void response.body?.cancel().catch(() => {});
}
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,
signal: AbortSignal,
): 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,
signal,
);
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 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,
effect: MutationEffectCertainty,
): HttpExecutionOutcome<Value, Problem> {
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";
}