import type { BrowserDataResult, TransferProgress, } from "./shared.ts"; import type { AuthorizedDownloadCapability } from "../browser-transfer/authorized-download.ts"; /** * Application-owned browser-file contracts. * * Native File, Blob, FileList, FileSystemHandle, Response and ReadableStream * intentionally do not cross this boundary. A transient object URL may cross * only through the presentation-local PreviewLease below; it must never enter * domain state, persistence, diagnostics or a general application cache. */ declare const localFileRefBrand: unique symbol; declare const fileVerificationReceiptBrand: unique symbol; declare const filePolicyKeyBrand: unique symbol; declare const filePolicyIntentionBrand: unique symbol; declare const browserManagedCapabilityReceiptBrand: unique symbol; export type LocalFileRef = string & { readonly [localFileRefBrand]: "LocalFileRef"; }; export type FileVerificationReceipt = string & { readonly [fileVerificationReceiptBrand]: "FileVerificationReceipt"; }; /** * Registry-issued, non-semantic identifiers. A feature receives a frozen * reference from its composition root; presentation must not construct policy * definitions or select another feature's registered policy. */ export type FilePolicyKey = string & { readonly [filePolicyKeyBrand]: "FilePolicyKey"; }; export type FilePolicyIntention = string & { readonly [filePolicyIntentionBrand]: "FilePolicyIntention"; }; export type FilePolicyReference = Readonly<{ policyKey: FilePolicyKey; intention: FilePolicyIntention; }>; export type FileSelectionSource = | "NATIVE_INPUT" | "SYSTEM_PICKER" | "DROP"; export type FileSelectionLimitReduction = Readonly<{ maxCount?: number; maxFileBytes?: number; maxTotalBytes?: number; }>; export type FileCandidate = Readonly<{ ref: LocalFileRef; /** * Untrusted, potentially personal display metadata. It must never be used as * a resource identifier or diagnostics attribute. */ displayName: string; sizeBytes: number; reportedMediaType: string | null; lastModifiedEpochMs: number | null; source: FileSelectionSource; }>; export type FileSelectionOutcome = | Readonly<{ kind: "SELECTED"; files: readonly FileCandidate[] }> | Readonly<{ kind: "DISMISSED" }>; export type FilePickerSupport = Readonly<{ nativeInput: true; systemOpenPicker: boolean; systemSavePicker: boolean; }>; export interface FilePickerPort { readonly support: FilePickerSupport; /** * Must be invoked as the first browser action in a trusted user activation. * A dismissed picker is a successful DISMISSED outcome, not an error. */ select(input: { policy: FilePolicyReference; limits?: FileSelectionLimitReduction; signal?: AbortSignal; }): Promise>; release(ref: LocalFileRef): void; } export type FileSignatureResult = | "MATCHED" | "MISMATCHED" | "UNKNOWN"; export type FileInspection = Readonly<{ byteLength: number; reportedMediaType: string | null; detectedMediaType: string | null; normalizedExtension: string | null; signature: FileSignatureResult; /** * Issued only for a matched signature and bound inside the transient vault * to this file snapshot and inspection policy. */ verificationReceipt: FileVerificationReceipt | null; }>; /** * File-capability byte stream with a closed failure channel. Implementations * must convert native exceptions to BrowserDataResult and never throw a raw * DOMException across the application boundary. */ export interface FileByteSource { readonly byteLength: number | null; stream( signal: AbortSignal, ): AsyncIterable>; } export interface FileContentPort { inspect(input: { ref: LocalFileRef; policy: FilePolicyReference; maxInspectionBytes?: number; signal: AbortSignal; }): Promise>; readRange(input: { ref: LocalFileRef; offset: number; length: number; signal: AbortSignal; }): Promise>; openSource(input: { ref: LocalFileRef; signal: AbortSignal; }): Promise>; release(ref: LocalFileRef): void; } export type PreviewLease = Readonly<{ url: string; mediaType: string; release(): void; }>; export interface TransientPreviewPort { create(input: { ref: LocalFileRef; verificationReceipt: FileVerificationReceipt; policy: FilePolicyReference; maxPreviewBytes?: number; signal: AbortSignal; }): Promise>; dispose(): void; } export type BrowserManagedDownloadCapabilityReceipt = string & { readonly [browserManagedCapabilityReceiptBrand]: "BrowserManagedDownloadCapabilityReceipt"; }; export type DownloadSource = | Readonly<{ kind: "BROWSER_MANAGED_RESOURCE"; resourceId: string; capabilityReceipt: BrowserManagedDownloadCapabilityReceipt; }> | Readonly<{ kind: "AUTHORIZED_STREAM_RESOURCE"; resourceId: string; /** * Exact provider-issued handle. Raw href/query/header values are never * caller inputs and an equal-looking fabricated handle must be rejected. */ capability: AuthorizedDownloadCapability; }> | Readonly<{ kind: "GENERATED"; bytes: FileByteSource; expectedSha256?: string; }>; export type DownloadStrategy = | "BROWSER_MANAGED" | "PROMPT_AND_STREAM" | "BOUNDED_OBJECT_URL"; export type DownloadOutcome = | Readonly<{ kind: "BROWSER_HANDOFF"; transferId: string; }> | Readonly<{ kind: "SAVED"; transferId: string; bytesWritten: number; integrity: "VERIFIED" | "NOT_PROVIDED"; }> | Readonly<{ kind: "DISMISSED" }>; export interface DownloadDeliveryPort { deliver(input: { policy: FilePolicyReference; source: DownloadSource; suggestedFileName: string; /** * Optional reductions of the composition-owned policy ceiling. These * values can never raise the registered or absolute runtime limits. */ maxTransferBytes?: number; maxBufferedBytes?: number; signal: AbortSignal; onProgress(progress: TransferProgress): void; }): Promise>; } /** * Synchronously resolved, server-enforced handoff capability. The endpoint * behind href must bind and enforce every field, including expiry and the * optional digest; the browser adapter cannot observe navigation bytes. */ export type BrowserManagedDownloadCapability = Readonly<{ capabilityReceipt: BrowserManagedDownloadCapabilityReceipt; href: string; resourceId: string; mediaType: string; safeExtension: string; maxBytes: number; expectedSha256?: string; expiresAtEpochMs: number; }>; export interface BrowserManagedDownloadCapabilityResolver { resolve(input: Readonly<{ resourceId: string; capabilityReceipt: BrowserManagedDownloadCapabilityReceipt; }>): BrowserDataResult; }