import { PRESIGNED_TRANSFER_PROTOCOL } from "../../src/application/ports/browser-transfer/presigned-transfer.ts"; import type { PresignedDownloadByteSource, } from "../../src/application/ports/browser-transfer/presigned-transfer.ts"; import { RESUMABLE_UPLOAD_PROTOCOL } from "../../src/application/ports/browser-transfer/resumable-upload.ts"; import type { BrowserDataObservation } from "../../src/application/ports/browser-file-storage/shared.ts"; import { createPresignedCapabilityVault, createSingleUsePresignedReplayGuard, } from "../../src/adapters/browser-transfer/presigned/presigned-capability-vault.ts"; import { createPresignedCapabilityHttpProvider } from "../../src/adapters/browser-transfer/presigned/presigned-capability-http-provider.ts"; import { createPresignedTransferExecutor, type PresignedTransferExecutorOptions, } from "../../src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts"; import { sha256Hex } from "../../src/adapters/browser-transfer/presigned/incremental-sha256.ts"; export const NOW = 1_000_000; export const CONTROL_ENDPOINT = "https://api.example/capabilities"; export const DATA_ORIGIN = "https://objects.example"; export const DOWNLOAD_PATH = "/files/resource-1"; export const DOWNLOAD_HREF = `${DATA_ORIGIN}${DOWNLOAD_PATH}?sig=do-not-log-this`; export const POLICY_HEADER = "x-policy-version"; export const DIGEST_HEADER = "x-content-sha256"; export const CHECKSUM_HEADER = "x-checksum-sha256"; export const UPLOAD_SESSION_ID = "upload-session-1"; export const REQUEST_BINDING_SHA256 = "c".repeat(64); export function downloadCapabilityPayload( bytes: Uint8Array, overrides: Readonly> = {}, ) { const digest = sha256Hex(bytes); return { protocol: PRESIGNED_TRANSFER_PROTOCOL, capabilityReceipt: "capability-download-1", method: "GET", binding: { kind: "DOWNLOAD", resourceId: "resource-1", }, href: DOWNLOAD_HREF, origin: DATA_ORIGIN, path: DOWNLOAD_PATH, allowedQueryParameters: ["sig"], requestHeaders: [ { name: "accept", value: "application/octet-stream" }, ], requiredResponseHeaders: [ { name: POLICY_HEADER, value: "v1" }, ], digestRequestHeader: null, digestResponseHeader: DIGEST_HEADER, receiptResponseHeader: null, expectedStatus: 200, expectedResponseByteLength: null, mediaType: "application/octet-stream", byteLength: bytes.byteLength, maxBytes: 64, expectedSha256: digest, expiresAtEpochMs: NOW + 30_000, singleUse: true, ...overrides, }; } export type CapabilityPayload = ReturnType; export function uploadCapabilityPayload(input: Readonly<{ bytes: Uint8Array; checksum: string; }>, overrides: Readonly> = {}) { return { protocol: PRESIGNED_TRANSFER_PROTOCOL, capabilityReceipt: "capability-upload-1", method: "PUT", binding: { kind: "UPLOAD_PART", protocol: RESUMABLE_UPLOAD_PROTOCOL, sessionId: UPLOAD_SESSION_ID, requestBindingSha256: REQUEST_BINDING_SHA256, uploadBindingSha256: "b".repeat(64), partNumber: 1, offset: 0, idempotencyKey: "part-attempt-1", }, href: `${DATA_ORIGIN}/uploads/session-1/part-1?sig=secret`, origin: DATA_ORIGIN, path: "/uploads/session-1/part-1", allowedQueryParameters: ["sig"], requestHeaders: [ { name: "content-type", value: "application/octet-stream" }, { name: CHECKSUM_HEADER, value: input.checksum }, ], requiredResponseHeaders: [ { name: POLICY_HEADER, value: "v1" }, ], digestRequestHeader: CHECKSUM_HEADER, digestResponseHeader: null, receiptResponseHeader: "etag", expectedStatus: 200, expectedResponseByteLength: 0, mediaType: "application/octet-stream", byteLength: input.bytes.byteLength, maxBytes: 64, expectedSha256: input.checksum, expiresAtEpochMs: NOW + 30_000, singleUse: true, ...overrides, }; } export function jsonResponse( value: unknown, url = CONTROL_ENDPOINT, ): Response { return responseWithUrl( new Response(JSON.stringify(value), { status: 200, headers: { "Content-Type": "application/json" }, }), url, ); } export function downloadResponse( body: BodyInit | null, payload: CapabilityPayload, headers: Record = {}, ): Response { return responseWithUrl( new Response(body, { status: payload.expectedStatus as number, headers: { "Content-Type": String(payload.mediaType), "Content-Length": String(payload.byteLength), [DIGEST_HEADER]: String(payload.expectedSha256), [POLICY_HEADER]: "v1", ...headers, }, }), String(payload.href), ); } export function responseWithUrl(response: Response, href: string): Response { Object.defineProperty(response, "url", { configurable: true, value: href, }); return response; } export function createHarness(input: Readonly<{ fetcher: typeof fetch; maxActiveCapabilities?: number; now?: () => number; digestBytes?: PresignedTransferExecutorOptions["digestBytes"]; scheduler?: PresignedTransferExecutorOptions["scheduler"]; observer?: Readonly<{ record(observation: BrowserDataObservation): void; }>; }>) { const now = input.now ?? (() => NOW); const vault = createPresignedCapabilityVault({ maxActiveCapabilities: input.maxActiveCapabilities ?? 16, now, }); const replayGuard = createSingleUsePresignedReplayGuard(); const provider = createPresignedCapabilityHttpProvider({ endpoint: CONTROL_ENDPOINT, vault, allowedDataOrigins: [DATA_ORIGIN], allowedDataPathPrefixes: ["/files/", "/uploads/"], allowedQueryParameters: ["sig"], allowedRequestHeaders: [ "accept", "content-type", CHECKSUM_HEADER, ], allowedResponseHeaders: [ POLICY_HEADER, DIGEST_HEADER, "etag", ], hardMaxTransferBytes: 64, hardMaxUploadResponseBytes: 16, maxCapabilityTtlMs: 60_000, minimumRemainingLifetimeMs: 1_000, timeoutMs: 5_000, fetcher: input.fetcher, now, scheduler: input.scheduler, observer: input.observer, }); const executor = createPresignedTransferExecutor({ vault, replayGuard, hardMaxTransferBytes: 64, hardMaxChunkBytes: 2, hardMaxUploadResponseBytes: 16, minimumRemainingLifetimeMs: 1_000, timeoutMs: 5_000, fetcher: input.fetcher, now, scheduler: input.scheduler, digestBytes: input.digestBytes, observer: input.observer, }); return { provider, executor, vault }; } export async function collect( source: PresignedDownloadByteSource, signal = new AbortController().signal, ) { const results = []; for await (const result of source.stream(signal)) { results.push(result); } return results; } /** * BT-PRE-01. The download lease is lazy, so a response-shape rejection is * observed on first consumption rather than at open(). */ export async function firstStreamResult( opened: Awaited< ReturnType< ReturnType["executor"]["downloadSources"]["open"] > >, ): Promise { if (!opened.ok) return opened; try { for await (const chunk of opened.value.stream( new AbortController().signal, )) { if (!chunk.ok) return chunk; } return { ok: true }; } finally { opened.value.close(); } }