import type { PresignedDownloadCapability, PresignedDownloadByteSource, PresignedDownloadSourcePort, PresignedTransferCapability, PresignedTransferReplayGuard, PresignedUploadPartCapability, PresignedUploadPartOutcome, PresignedUploadPartPort, } from "../../../application/ports/browser-transfer/presigned-transfer.ts"; import { createAbortableOperation, snapshotAbortTimers, type AbortTimerSnapshot, } from "../../platform/abortable-operation.ts"; import { RESUMABLE_UPLOAD_PROTOCOL } from "../../../application/ports/browser-transfer/resumable-upload.ts"; import type { BrowserDataFailureCode, BrowserDataObserver, BrowserDataOperation, BrowserDataRecovery, BrowserDataResult, } from "../../../application/ports/browser-file-storage/shared.ts"; import { browserDataFailure, browserDataSuccess, observeBrowserData, } from "../../browser-file-storage/result.ts"; import { createStreamingSha256Verifier, type StreamingSha256Verifier, } from "./incremental-sha256.ts"; import type { PresignedCapabilityBinding, PresignedCapabilityVault, } from "./presigned-capability-vault.ts"; const SHA256 = /^[a-f0-9]{64}$/; const RECEIPT_TOKEN = /^[A-Za-z0-9][A-Za-z0-9._~:+/=-]{0,511}$/; type Scheduler = Readonly<{ setTimeout(callback: () => void, milliseconds: number): unknown; clearTimeout(handle: unknown): void; }>; export type PresignedTransferExecutorOptions = Readonly<{ vault: PresignedCapabilityVault; replayGuard: PresignedTransferReplayGuard; hardMaxTransferBytes: number; hardMaxChunkBytes: number; hardMaxUploadResponseBytes: number; minimumRemainingLifetimeMs: number; timeoutMs: number; fetcher?: typeof fetch; now?: () => number; scheduler?: Scheduler; createStreamingVerifier?: ( expectedSha256: string, ) => StreamingSha256Verifier; digestBytes?: ( bytes: Uint8Array, ) => Promise; observer?: BrowserDataObserver; }>; export type PresignedTransferExecutor = Readonly<{ downloadSources: PresignedDownloadSourcePort; uploadParts: PresignedUploadPartPort; }>; /** * Consumes only exact handles issued into the supplied identity vault. Raw * href/query/header values never enter either public executor method. */ export function createPresignedTransferExecutor( options: PresignedTransferExecutorOptions, ): PresignedTransferExecutor { const resolve = options.vault.resolve.bind(options.vault); const consume = options.vault.consume.bind(options.vault); const claim = options.replayGuard.claim.bind(options.replayGuard); const hardMaxTransferBytes = positiveSafeInteger( options.hardMaxTransferBytes, ); const hardMaxChunkBytes = positiveSafeInteger( options.hardMaxChunkBytes, ); if (hardMaxChunkBytes > hardMaxTransferBytes) { throw new TypeError("Presigned chunk limit exceeds transfer limit."); } const hardMaxUploadResponseBytes = positiveSafeInteger( options.hardMaxUploadResponseBytes, ); const minimumRemainingLifetimeMs = positiveSafeInteger( options.minimumRemainingLifetimeMs, ); const timeoutMs = positiveSafeInteger(options.timeoutMs); const fetcher = (options.fetcher ?? fetch).bind(globalThis); const now = options.now ?? Date.now; // X-AUDIT-02. The timer callables are captured once, bound to their // receiver, so replacing a scheduler method after composition cannot change // how work already in flight is bounded. const timers = snapshotAbortTimers( options.scheduler ?? ({ setTimeout: (callback, milliseconds) => globalThis.setTimeout(callback, milliseconds), clearTimeout: (handle) => globalThis.clearTimeout( handle as ReturnType, ), } satisfies Scheduler), ); const createVerifier = options.createStreamingVerifier ?? createStreamingSha256Verifier; if (options.digestBytes === undefined && !globalThis.crypto?.subtle) { throw new TypeError( "WebCrypto SHA-256 is required for presigned uploads.", ); } const digestBytes = options.digestBytes ?? digestSha256WithWebCrypto; const observer = options.observer; async function openDownload( input: Parameters[0], ): Promise> { let resourceId: string; let capability: PresignedDownloadCapability; let signal: AbortSignal; try { resourceId = input.resourceId; capability = input.capability; signal = input.signal; if (!isAbortSignal(signal)) { throw new TypeError("Abort signal is invalid."); } } catch { return browserDataFailure("INVALID_INPUT", "PRESIGNED_TRANSFER"); } if (signal.aborted) { return browserDataFailure("ABORTED", "PRESIGNED_TRANSFER"); } const resolved = resolve(capability); if (!resolved.ok) return resolved; const binding = resolved.value; if ( binding.method !== "GET" || binding.binding.kind !== "DOWNLOAD" || binding.binding.resourceId !== resourceId || capability.binding.kind !== "DOWNLOAD" || capability.binding.resourceId !== resourceId || !validCommonBinding(binding, capability, hardMaxTransferBytes) ) { return browserDataFailure("POLICY_REJECTED", "PRESIGNED_TRANSFER"); } const active = validateExpiry( capability, minimumRemainingLifetimeMs, now(), ); if (!active.ok) return active; const claimed = claim(capability); if (!claimed.ok) return claimed; const consumed = consume(capability); if (!consumed.ok) return consumed; // BT-PRE-01. The lease is lazy and single-start: `open()` performs no // network I/O, so the transfer deadline begins at first consumption and an // unused source can be discarded through `close()` without leaking a body, // a timer or a listener. const source = createDownloadSource({ start: async (scope) => await fetcher(binding.href, { method: "GET", headers: headersFor(binding), credentials: "omit", redirect: "error", referrerPolicy: "no-referrer", cache: "no-store", signal: scope.signal, }), validateResponse: (response) => validateDownloadResponse(response, binding), createScope: (consumerSignal?: AbortSignal) => createAbortScope( signal, timeoutMs, timers, consumerSignal ? [consumerSignal] : [], ), recheckExpiry: () => validateExpiry(capability, minimumRemainingLifetimeMs, now()), binding, capability, externalSignal: signal, hardMaxChunkBytes, createVerifier, observer, }); return browserDataSuccess(source); } async function putUploadPart( input: Parameters[0], ): Promise> { let capability: PresignedUploadPartCapability; let bytes: Uint8Array; let request: Readonly<{ sessionId: string; requestBindingSha256: string; uploadBindingSha256: string; partNumber: number; offset: number; byteLength: number; checksumSha256: string; idempotencyKey: string; signal: AbortSignal; }>; try { capability = input.capability; if (!(input.bytes instanceof Uint8Array)) { throw new TypeError("Upload bytes are invalid."); } if ( input.bytes.byteLength > hardMaxTransferBytes || input.bytes.byteLength !== input.byteLength ) { return browserDataFailure( input.bytes.byteLength > hardMaxTransferBytes ? "LIMIT_EXCEEDED" : "INTEGRITY_FAILED", "PRESIGNED_TRANSFER", ); } if (!isAbortSignal(input.signal)) { throw new TypeError("Abort signal is invalid."); } // Snapshot before hashing or network awaits so caller mutation cannot // alter the verified bytes after the capability check. bytes = input.bytes.slice(); request = Object.freeze({ sessionId: opaqueId(input.sessionId), requestBindingSha256: normalizedSha256( input.requestBindingSha256, ), uploadBindingSha256: normalizedSha256( input.uploadBindingSha256, ), partNumber: positiveSafeInteger(input.partNumber), offset: nonNegativeSafeInteger(input.offset), byteLength: positiveSafeInteger(input.byteLength), checksumSha256: normalizedSha256(input.checksumSha256), idempotencyKey: opaqueId(input.idempotencyKey), signal: input.signal, }); } catch { return browserDataFailure("INVALID_INPUT", "PRESIGNED_TRANSFER"); } if (request.signal.aborted) { return browserDataFailure("ABORTED", "PRESIGNED_TRANSFER"); } const resolved = resolve(capability); if (!resolved.ok) return resolved; const binding = resolved.value; if ( binding.method !== "PUT" || binding.binding.kind !== "UPLOAD_PART" || capability.binding.kind !== "UPLOAD_PART" || binding.binding.protocol !== RESUMABLE_UPLOAD_PROTOCOL || !validCommonBinding(binding, capability, hardMaxTransferBytes) || binding.binding.sessionId !== request.sessionId || binding.binding.requestBindingSha256 !== request.requestBindingSha256 || binding.binding.uploadBindingSha256 !== request.uploadBindingSha256 || binding.binding.partNumber !== request.partNumber || binding.binding.offset !== request.offset || binding.binding.idempotencyKey !== request.idempotencyKey || capability.byteLength !== request.byteLength || capability.expectedSha256 !== request.checksumSha256 || bytes.byteLength !== capability.byteLength ) { return browserDataFailure("POLICY_REJECTED", "PRESIGNED_TRANSFER"); } // TR-RR-02. The abort scope is created first, so the digest — which can be // a long or non-settling computation over a large buffer — is owned by the // caller signal and the deadline like every other step. Computing it before // the scope existed meant an abort or a deadline could not reach it, and a // hash that never settled held the whole `put` open. const scope = createAbortScope(request.signal, timeoutMs, timers); try { const digested = await scope.race( Promise.resolve().then(async () => await digestBytes(bytes)), ); if (digested === SCOPE_ENDED) { return transferFailure(request.signal, scope.timedOut()); } let actualDigest: string; try { actualDigest = normalizedSha256(digested); } catch { return browserDataFailure( "INTEGRITY_FAILED", "PRESIGNED_TRANSFER", ); } if (actualDigest !== capability.expectedSha256) { return browserDataFailure( "INTEGRITY_FAILED", "PRESIGNED_TRANSFER", ); } // The owner is re-checked after the wait: a claim and a network call may // only follow a digest that finished while this operation still held the // execution. if (scope.signal.aborted || request.signal.aborted) { return transferFailure(request.signal, scope.timedOut()); } const active = validateExpiry( capability, minimumRemainingLifetimeMs, now(), ); if (!active.ok) return active; const claimed = claim(capability); if (!claimed.ok) return claimed; const consumed = consume(capability); if (!consumed.ok) return consumed; const raced = await scope.race( fetcher(binding.href, { method: "PUT", headers: headersFor(binding), body: bytes.buffer, credentials: "omit", redirect: "error", referrerPolicy: "no-referrer", cache: "no-store", signal: scope.signal, }), ); if (raced === SCOPE_ENDED) { return transferFailure(request.signal, scope.timedOut()); } const response = raced; const validated = validateUploadResponse( response, binding, hardMaxUploadResponseBytes, ); if (!validated.ok) { cancelBody(response); return validated; } const drained = await drainUploadResponse( response, validated.value.responseByteLength, hardMaxUploadResponseBytes, scope.signal, ); if (!drained.ok) return drained; return browserDataSuccess( Object.freeze({ bytesWritten: bytes.byteLength, checksumSha256: capability.expectedSha256, receiptToken: validated.value.receiptToken, } satisfies PresignedUploadPartOutcome), ); } catch { return transferFailure(request.signal, scope.timedOut()); } finally { scope.release(); } } const downloadSources: PresignedDownloadSourcePort = Object.freeze({ async open( input: Parameters[0], ) { let result: BrowserDataResult; try { result = await openDownload(input); } catch { result = browserDataFailure( "UNAVAILABLE", "PRESIGNED_TRANSFER", { retryable: true, recovery: "REISSUE_CAPABILITY", }, ); } observeTransferResult( observer, "PRESIGNED_TRANSFER", result, result.ok ? result.value.byteLength : safeInputByteLength(input), ); return result; }, }); const uploadParts: PresignedUploadPartPort = Object.freeze({ async put( input: Parameters[0], ) { let result: BrowserDataResult; try { result = await putUploadPart(input); } catch { result = browserDataFailure( "UNAVAILABLE", "PRESIGNED_TRANSFER", { retryable: true, recovery: "REISSUE_CAPABILITY", }, ); } observeTransferResult( observer, "UPLOAD_PART", result, result.ok ? result.value.bytesWritten : safeInputByteLength(input), ); return result; }, }); return Object.freeze({ downloadSources, uploadParts }); } function createDownloadSource(input: Readonly<{ start: ( scope: ReturnType, ) => Promise; validateResponse: (response: Response) => BrowserDataResult; createScope: ( consumerSignal?: AbortSignal, ) => ReturnType; recheckExpiry: () => BrowserDataResult; binding: PresignedCapabilityBinding; capability: PresignedDownloadCapability; externalSignal: AbortSignal; hardMaxChunkBytes: number; createVerifier: ( expectedSha256: string, ) => StreamingSha256Verifier; observer: BrowserDataObserver | undefined; }>): PresignedDownloadByteSource { /** BT-PRE-01. One state machine shared by `stream()` and `close()`. */ let state: "READY" | "STREAMING" | "CLOSED" = "READY"; let activeScope: ReturnType | undefined; let activeResponse: Response | undefined; // TR-RR-01. Releasing the scope only dropped listeners and the timer, so a // fetch or read already in flight kept running after `close()`. The scope is // aborted here, which is what actually ends the physical I/O. const releaseActive = () => { if (activeScope) { activeScope.abort(); } if (activeResponse) { cancelBody(activeResponse); activeResponse = undefined; } if (activeScope) { activeScope.release(); activeScope = undefined; } }; return Object.freeze({ byteLength: input.capability.byteLength, capability: input.capability, integrity: "VERIFIED_ON_SUCCESSFUL_EXHAUSTION" as const, close() { // READY -> CLOSED performs no I/O; STREAMING -> CLOSED cancels once. if (state === "CLOSED") return; state = "CLOSED"; releaseActive(); }, async *stream( consumerSignal: AbortSignal, ): AsyncIterable> { if (!isAbortSignal(consumerSignal)) { state = "CLOSED"; releaseActive(); const failure = browserDataFailure( "INVALID_INPUT", "PRESIGNED_TRANSFER", ); observeTransferResult(input.observer, "DOWNLOAD", failure, 0); yield failure; return; } if (state !== "READY") { const failure = browserDataFailure( "CONFLICT", "PRESIGNED_TRANSFER", { recovery: "REISSUE_CAPABILITY", }, ); observeTransferResult(input.observer, "DOWNLOAD", failure, 0); yield failure; return; } state = "STREAMING"; // The capability may have expired while the lease sat unused. const stillActive = input.recheckExpiry(); if (!stillActive.ok) { state = "CLOSED"; observeTransferResult( input.observer, "DOWNLOAD", stillActive, 0, ); yield stillActive as BrowserDataResult; return; } // TR-RR-01. The consumer's stream signal is part of this operation's // ownership from the start. Composing it only after the fetch had begun // meant an already-aborted consumer still caused one network request. const scope = input.createScope(consumerSignal); activeScope = scope; if (scope.signal.aborted) { state = "CLOSED"; releaseActive(); const failure = transferFailure( input.externalSignal, scope.timedOut(), ); observeTransferResult(input.observer, "DOWNLOAD", failure, 0); yield failure; return; } let response: Response; try { // BT-PRE-03. A fetch that ignores its signal cannot outlive the scope. const started = await scope.race(input.start(scope)); if (started === SCOPE_ENDED) { state = "CLOSED"; releaseActive(); const failure = transferFailure( input.externalSignal, scope.timedOut(), ); observeTransferResult(input.observer, "DOWNLOAD", failure, 0); yield failure; return; } response = started; } catch { state = "CLOSED"; releaseActive(); const failure = transferFailure( input.externalSignal, scope.timedOut(), ); observeTransferResult(input.observer, "DOWNLOAD", failure, 0); yield failure; return; } activeResponse = response; const validated = input.validateResponse(response); if (!validated.ok) { state = "CLOSED"; releaseActive(); observeTransferResult(input.observer, "DOWNLOAD", validated, 0); yield validated as BrowserDataResult; return; } let reader: | ReadableStreamDefaultReader | undefined; let completed = false; let transferred = 0; let terminalFailureCode: BrowserDataFailureCode | undefined; const fail = ( code: BrowserDataFailureCode, options: Readonly<{ retryable?: boolean; recovery?: BrowserDataRecovery; }> = {}, ): BrowserDataResult => { terminalFailureCode = code; return browserDataFailure(code, "PRESIGNED_TRANSFER", options); }; try { const verifier = input.createVerifier( input.capability.expectedSha256, ); if (!response.body) { let verified = false; try { verified = input.capability.byteLength === 0 && verifier.verify(); } catch { verified = false; } if (verified) { completed = true; return; } yield fail("INTEGRITY_FAILED"); return; } reader = response.body.getReader(); while (true) { if ( input.externalSignal.aborted || consumerSignal.aborted ) { yield fail("ABORTED"); return; } if (scope.timedOut()) { yield fail("UNAVAILABLE", { retryable: true, recovery: "REISSUE_CAPABILITY", }); return; } const result = await readWithSignal( reader, scope.signal, ); if (result.done) break; const chunk = result.value; if (!(chunk instanceof Uint8Array)) { yield fail("CORRUPT_DATA"); return; } const chunkEnd = transferred + chunk.byteLength; if ( !Number.isSafeInteger(chunkEnd) || chunkEnd > input.capability.maxBytes ) { yield fail("LIMIT_EXCEEDED"); return; } if (chunkEnd > input.capability.byteLength) { yield fail("INTEGRITY_FAILED"); return; } // Browser fetch chunk sizing is implementation-defined. Re-slice into // owned bounded chunks instead of rejecting a valid large chunk. for ( let offset = 0; offset < chunk.byteLength; offset += input.hardMaxChunkBytes ) { if ( input.externalSignal.aborted || consumerSignal.aborted ) { yield fail("ABORTED"); return; } if (scope.timedOut()) { yield fail("UNAVAILABLE", { retryable: true, recovery: "REISSUE_CAPABILITY", }); return; } const owned = chunk .subarray( offset, Math.min( chunk.byteLength, offset + input.hardMaxChunkBytes, ), ) .slice(); try { verifier.update(owned); } catch { yield fail("INTEGRITY_FAILED"); return; } transferred += owned.byteLength; yield browserDataSuccess(owned); } } let verified = false; try { verified = verifier.verify(); } catch { verified = false; } if ( transferred !== input.capability.byteLength || !verified ) { yield fail("INTEGRITY_FAILED"); return; } completed = true; } catch { if ( input.externalSignal.aborted || consumerSignal.aborted ) { yield fail("ABORTED"); } else if (scope.timedOut()) { yield fail("UNAVAILABLE", { retryable: true, recovery: "REISSUE_CAPABILITY", }); } else { yield fail("NOT_READABLE", { retryable: true, recovery: "REISSUE_CAPABILITY", }); } } finally { if (!completed) { if (reader) cancelReader(reader); else cancelBody(response); } try { reader?.releaseLock(); } catch { // Reader cleanup cannot change stream success or failure. } // The lease is terminal once its single stream ends; cleanup runs once. state = "CLOSED"; activeResponse = undefined; activeScope = undefined; scope.release(); observeBrowserData(input.observer, { operation: "DOWNLOAD", outcome: completed ? "SUCCEEDED" : terminalFailureCode ? "FAILED" : "DEGRADED", ...(terminalFailureCode ? { failureCode: terminalFailureCode } : {}), byteBucket: byteBucket(transferred), }); } }, }); } function validateDownloadResponse( response: Response, binding: PresignedCapabilityBinding, ): BrowserDataResult { if ( response.redirected || response.type === "opaqueredirect" || !responseUrlMatches( response, binding.href, ) ) { return browserDataFailure("POLICY_REJECTED", "PRESIGNED_TRANSFER"); } if (response.status !== binding.expectedStatus) { return statusFailure(response.status); } if (!validateRequiredHeaders(response, binding)) { return browserDataFailure( "INTEGRITY_FAILED", "PRESIGNED_TRANSFER", ); } const mediaType = response.headers .get("content-type") ?.trim() .toLowerCase(); const declaredLength = response.headers.get("content-length"); const digestHeader = binding.digestResponseHeader; const contentEncoding = response.headers.get("content-encoding"); if ( mediaType !== binding.mediaType || declaredLength === null || Number(declaredLength) !== binding.byteLength || digestHeader === null || response.headers.get(digestHeader)?.trim().toLowerCase() !== binding.expectedSha256 || (contentEncoding !== null && contentEncoding.trim().toLowerCase() !== "identity") || response.headers.has("content-range") || (binding.byteLength > 0 && !response.body) ) { return browserDataFailure( "INTEGRITY_FAILED", "PRESIGNED_TRANSFER", ); } return browserDataSuccess(true); } function validateUploadResponse( response: Response, binding: PresignedCapabilityBinding, hardMaxUploadResponseBytes: number, ): BrowserDataResult< Readonly<{ receiptToken: string; responseByteLength: number; }> > { if ( response.redirected || response.type === "opaqueredirect" || !responseUrlMatches( response, binding.href, ) ) { return browserDataFailure("POLICY_REJECTED", "PRESIGNED_TRANSFER"); } if (response.status !== binding.expectedStatus) { return statusFailure(response.status); } if (!validateRequiredHeaders(response, binding)) { return browserDataFailure( "INTEGRITY_FAILED", "PRESIGNED_TRANSFER", ); } const headerName = binding.receiptResponseHeader; const expectedResponseByteLength = binding.expectedResponseByteLength; const declaredLength = normalizedContentLength( response.headers.get("content-length"), response.status === 204 && expectedResponseByteLength === 0, ); if ( !headerName || expectedResponseByteLength === null || expectedResponseByteLength > hardMaxUploadResponseBytes || declaredLength === null || declaredLength !== expectedResponseByteLength || (expectedResponseByteLength > 0 && !response.body) ) { return browserDataFailure("POLICY_REJECTED", "PRESIGNED_TRANSFER"); } const receipt = normalizeReceiptToken( response.headers.get(headerName), ); return receipt ? browserDataSuccess( Object.freeze({ receiptToken: receipt, responseByteLength: expectedResponseByteLength, }), ) : browserDataFailure("INTEGRITY_FAILED", "PRESIGNED_TRANSFER"); } async function drainUploadResponse( response: Response, expectedByteLength: number, hardMaxBytes: number, signal: AbortSignal, ): Promise> { if (!response.body) { return expectedByteLength === 0 ? browserDataSuccess(true) : browserDataFailure( "INTEGRITY_FAILED", "PRESIGNED_TRANSFER", ); } const reader = response.body.getReader(); let completed = false; let total = 0; try { while (true) { const result = await readWithSignal(reader, signal); if (result.done) break; if (!(result.value instanceof Uint8Array)) { return browserDataFailure( "CORRUPT_DATA", "PRESIGNED_TRANSFER", ); } total += result.value.byteLength; if (!Number.isSafeInteger(total) || total > hardMaxBytes) { return browserDataFailure( "LIMIT_EXCEEDED", "PRESIGNED_TRANSFER", ); } if (total > expectedByteLength) { return browserDataFailure( "INTEGRITY_FAILED", "PRESIGNED_TRANSFER", ); } } if (total !== expectedByteLength) { return browserDataFailure( "INTEGRITY_FAILED", "PRESIGNED_TRANSFER", ); } completed = true; return browserDataSuccess(true); } finally { if (!completed) cancelReader(reader); try { reader.releaseLock(); } catch { // Reader cleanup cannot alter the already classified result. } } } function validateRequiredHeaders( response: Response, binding: PresignedCapabilityBinding, ): boolean { return binding.requiredResponseHeaders.every( (header) => response.headers.get(header.name) === header.value, ); } function normalizedContentLength( value: string | null, allowImplicitZero: boolean, ): number | null { if (value === null) return allowImplicitZero ? 0 : null; const trimmed = value.trim(); if (!/^(?:0|[1-9][0-9]{0,15})$/.test(trimmed)) return null; const length = Number(trimmed); return Number.isSafeInteger(length) ? length : null; } function headersFor(binding: PresignedCapabilityBinding): Headers { const headers = new Headers(); for (const header of binding.requestHeaders) { headers.set(header.name, header.value); } return headers; } function validCommonBinding( binding: PresignedCapabilityBinding, capability: PresignedTransferCapability, hardMaxTransferBytes: number, ): boolean { return ( binding.capability === capability && binding.capabilityReceipt === capability.capabilityReceipt && binding.method === capability.method && binding.binding === capability.binding && binding.mediaType === capability.mediaType && binding.byteLength === capability.byteLength && binding.maxBytes === capability.maxBytes && binding.expectedSha256 === capability.expectedSha256 && binding.expiresAtEpochMs === capability.expiresAtEpochMs && capability.byteLength >= 0 && capability.byteLength <= capability.maxBytes && capability.maxBytes <= hardMaxTransferBytes && SHA256.test(capability.expectedSha256) && Object.isFrozen(capability) && Object.isFrozen(capability.binding) ); } function validateExpiry( capability: PresignedTransferCapability, minimumRemainingLifetimeMs: number, nowEpochMs: number, ): BrowserDataResult { const remainingLifetimeMs = capability.expiresAtEpochMs - nowEpochMs; if ( !Number.isSafeInteger(nowEpochMs) || nowEpochMs < 0 || !Number.isSafeInteger(remainingLifetimeMs) ) { return browserDataFailure("POLICY_REJECTED", "PRESIGNED_TRANSFER"); } return remainingLifetimeMs >= minimumRemainingLifetimeMs ? browserDataSuccess(true) : browserDataFailure( "EXPIRED_RESOURCE", "PRESIGNED_TRANSFER", { recovery: "REISSUE_CAPABILITY" }, ); } function statusFailure( status: number, ): BrowserDataResult { let code: BrowserDataFailureCode = "UNAVAILABLE"; let recovery: BrowserDataRecovery = "REISSUE_CAPABILITY"; let retryable = status === 429 || status >= 500; if (status === 401 || status === 403) { code = "PERMISSION_DENIED"; recovery = "REISSUE_CAPABILITY"; retryable = false; } else if (status === 404) { code = "NOT_FOUND"; recovery = "NONE"; retryable = false; } else if (status === 409) { code = "CONFLICT"; retryable = false; } else if (status === 410) { code = "EXPIRED_RESOURCE"; retryable = false; } else if (status === 413) { code = "LIMIT_EXCEEDED"; recovery = "NONE"; retryable = false; } else if (status >= 400 && status < 500 && status !== 429) { code = "POLICY_REJECTED"; recovery = "NONE"; retryable = false; } return browserDataFailure(code, "PRESIGNED_TRANSFER", { retryable, recovery, }); } function transferFailure( externalSignal: AbortSignal, timedOut: boolean, ): BrowserDataResult { if (externalSignal.aborted) { return browserDataFailure("ABORTED", "PRESIGNED_TRANSFER"); } return browserDataFailure( timedOut ? "UNAVAILABLE" : "NOT_READABLE", "PRESIGNED_TRANSFER", { retryable: true, recovery: "REISSUE_CAPABILITY", }, ); } /** BT-PRE-03. The scope ended before the task settled. */ const SCOPE_ENDED = Symbol("presigned-scope-ended"); function compensateLateResponse(task: Promise): void { void task .then(async (value) => { const body = (value as { body?: { cancel(): Promise } | null }) ?.body; await body?.cancel(); }) .catch(() => undefined); } /** * TR-RR-01 / TR-RR-05. A projection of the shared `createAbortableOperation` * primitive into this subsystem's vocabulary, replacing a second hand-written * copy of the same mechanics. * * `additionalSignals` lets a consumer's stream signal join the operation's * ownership before any I/O begins, and `abort()` ends the physical work rather * than only releasing bookkeeping. */ function createAbortScope( external: AbortSignal, timeoutMs: number, timers: AbortTimerSnapshot, additionalSignals: readonly AbortSignal[] = [], ) { const operation = createAbortableOperation({ signal: external, timeoutMs, setTimer: timers.setTimer, clearTimer: timers.clearTimer, }); const releases: (() => void)[] = []; for (const extra of additionalSignals) { if (!extra) continue; if (extra.aborted) { operation.close(); continue; } const onAbort = () => operation.close(); try { extra.addEventListener("abort", onAbort, { once: true }); releases.push(() => { try { extra.removeEventListener("abort", onAbort); } catch { // A hostile signal facade cannot block the rest of cleanup. } }); } catch { operation.close(); } } const releaseExtras = () => { for (const release of releases.splice(0)) release(); }; return Object.freeze({ signal: operation.signal, timedOut: () => operation.terminal() === "DEADLINE", async race(task: Promise): Promise { const outcome = await operation.race(task, (value) => { const body = ( value as { body?: { cancel(): Promise } | null } | null )?.body; void body?.cancel().catch(() => undefined); }); if (outcome.kind === "VALUE") return outcome.value; if (outcome.kind === "REJECTED") throw outcome.reason; return SCOPE_ENDED; }, abort() { operation.close(); releaseExtras(); }, release() { operation.close(); releaseExtras(); }, }); } function cancelBody(response: Response): void { try { void response.body?.cancel().catch(() => { // Cancellation is best effort after the result is classified. }); } catch { // A response already classified as failure/success is not reclassified by // best-effort body cancellation. } } function cancelReader( reader: ReadableStreamDefaultReader, ): void { try { void reader.cancel().catch(() => { // Cancellation is best effort after the result is classified. }); } catch { // Reader cancellation cannot alter the terminal closed result. } } function readWithSignal( reader: ReadableStreamDefaultReader, signal: AbortSignal, ): Promise> { if (signal.aborted) { return Promise.reject(new DOMException("Aborted", "AbortError")); } return new Promise((resolve, reject) => { const onAbort = () => { reject(new DOMException("Aborted", "AbortError")); }; signal.addEventListener("abort", onAbort, { once: true }); reader.read().then( (result) => { signal.removeEventListener("abort", onAbort); resolve(result); }, (error: unknown) => { signal.removeEventListener("abort", onAbort); reject(error); }, ); }); } function normalizeReceiptToken(value: string | null): string | null { if (!value) return null; const trimmed = value.trim(); const unquoted = trimmed.length >= 2 && trimmed.startsWith("\"") && trimmed.endsWith("\"") ? trimmed.slice(1, -1) : trimmed; return RECEIPT_TOKEN.test(unquoted) && !unquoted.includes("://") ? unquoted : null; } function responseUrlMatches( response: Response, expectedHref: string, ): boolean { if (response.url.length === 0) return false; try { return new URL(response.url).href === new URL(expectedHref).href; } catch { return false; } } function normalizedSha256(value: unknown): string { if (typeof value !== "string") throw new TypeError("SHA-256 is invalid."); const normalized = value.toLowerCase(); if (!SHA256.test(normalized)) throw new TypeError("SHA-256 is invalid."); return normalized; } function opaqueId(value: unknown): string { if ( typeof value !== "string" || value.length === 0 || value.length > 256 || !/^[a-z0-9][a-z0-9._:-]*$/i.test(value) ) { throw new TypeError("Opaque identifier is invalid."); } return value; } function positiveSafeInteger(value: unknown): number { if (!Number.isSafeInteger(value) || (value as number) <= 0) { throw new TypeError("Expected a positive safe integer."); } return value as number; } function nonNegativeSafeInteger(value: unknown): number { if (!Number.isSafeInteger(value) || (value as number) < 0) { throw new TypeError("Expected a non-negative safe integer."); } return value as number; } async function digestSha256WithWebCrypto( bytes: Uint8Array, ): Promise { if (!(bytes instanceof Uint8Array) || !globalThis.crypto?.subtle) { throw new TypeError("WebCrypto SHA-256 is unavailable."); } const digest = await globalThis.crypto.subtle.digest( "SHA-256", bytes.slice(), ); return Array.from(new Uint8Array(digest), (value) => value.toString(16).padStart(2, "0"), ).join(""); } function isAbortSignal(value: unknown): value is AbortSignal { try { if (!value || typeof value !== "object") return false; const abortedGetter = Object.getOwnPropertyDescriptor( AbortSignal.prototype, "aborted", )?.get; return Boolean( abortedGetter && typeof abortedGetter.call(value) === "boolean" && typeof (value as AbortSignal).addEventListener === "function" && typeof (value as AbortSignal).removeEventListener === "function", ); } catch { return false; } } function safeInputByteLength(value: unknown): number | undefined { try { if (!value || typeof value !== "object") return undefined; const input = value as Readonly<{ byteLength?: unknown; bytes?: unknown; capability?: unknown; }>; if ( Number.isSafeInteger(input.byteLength) && (input.byteLength as number) >= 0 ) { return input.byteLength as number; } if (input.bytes instanceof Uint8Array) { return input.bytes.byteLength; } if ( input.capability && typeof input.capability === "object" && Number.isSafeInteger( (input.capability as Readonly<{ byteLength?: unknown }>) .byteLength, ) ) { const byteLength = ( input.capability as Readonly<{ byteLength: number }> ).byteLength; return byteLength >= 0 ? byteLength : undefined; } } catch { return undefined; } return undefined; } function observeTransferResult( observer: BrowserDataObserver | undefined, operation: BrowserDataOperation, result: BrowserDataResult, byteLength?: number, ): void { const bucket = byteBucket(byteLength); observeBrowserData(observer, { operation, outcome: result.ok ? "SUCCEEDED" : "FAILED", ...(!result.ok ? { failureCode: result.error.code } : {}), ...(bucket !== undefined ? { byteBucket: bucket } : {}), }); } function byteBucket( byteLength: number | undefined, ): | "ZERO" | "LT1MIB" | "1_TO_9MIB" | "10_TO_99MIB" | "GTE100MIB" | undefined { if ( byteLength === undefined || !Number.isSafeInteger(byteLength) || byteLength < 0 ) { return undefined; } if (byteLength === 0) return "ZERO"; if (byteLength < 1_048_576) return "LT1MIB"; if (byteLength < 10_485_760) return "1_TO_9MIB"; if (byteLength < 104_857_600) return "10_TO_99MIB"; return "GTE100MIB"; }