/** Server-only, persistence-agnostic storage quota contract. */ export type StorageQuotaContext = Readonly<{ actorUserId: string; requestId: string; idempotencyKey: string; signal?: AbortSignal; }>; export type StorageQuotaReservationRequest = StorageQuotaContext & Readonly<{ bytes: number; }>; export type StorageQuotaReservation = Readonly<{ status: "reserved" | "already_reserved"; reservationId: string; reservedBytes: number; usageBytes: number; limitBytes: number; expiresAt: string; }>; export type StorageQuotaRejection = Readonly<{ status: "rejected"; usageBytes: number; limitBytes: number; requestedBytes: number; }>; export type StorageQuotaCommitRequest = StorageQuotaContext & Readonly<{ reservationId: string; actualBytes: number; }>; export type StorageQuotaReleaseRequest = StorageQuotaContext & Readonly<{ reservationId: string; }>; export type StorageQuotaCommitResult = Readonly<{ status: "committed" | "already_committed"; }>; export type StorageQuotaReleaseResult = Readonly<{ status: "released" | "already_released" | "not_found"; }>; export type StorageQuotaAdapter = Readonly<{ /** Must atomically check usage and create an idempotent reservation. */ reserve(request: StorageQuotaReservationRequest): Promise; commit(request: StorageQuotaCommitRequest): Promise; release(request: StorageQuotaReleaseRequest): Promise; }>; export type StorageQuotaService = Readonly<{ reserve(request: StorageQuotaReservationRequest): Promise; commit(request: StorageQuotaCommitRequest): Promise; release(request: StorageQuotaReleaseRequest): Promise; }>; /** * Verified server-side identity for an attachment write. It is intentionally * richer than the generic quota adapter: attachment storage must be charged * to one workspace, actor and immutable object key before a presign is issued. */ export type AttachmentStorageQuotaContext = Readonly<{ workspaceId: string; actorUserId: string; documentId: string; storageKey: string; proposedSize: number; requestId: string; idempotencyKey: string; signal?: AbortSignal; }>; export type AttachmentStorageQuotaFinalization = Readonly<{ reservationId: string; actualSize: number; }>; /** * OSS invokes this only after it has verified tenant context and document * access. A host provider is never exposed to HTTP or browser code. Every * method MUST be idempotent for `context.idempotencyKey`: repeated reserve * returns the same logical reservation, repeated finalize has one charge, and * repeated release is a successful no-op after the first release. The OSS * durable upload/cleanup saga relies on this contract at crash boundaries. */ export type AttachmentStorageQuotaAdmission = Readonly<{ reserve(context: AttachmentStorageQuotaContext): Promise>; finalize(context: AttachmentStorageQuotaContext, finalization: AttachmentStorageQuotaFinalization): Promise; releaseReservation(context: AttachmentStorageQuotaContext, reservationId: string): Promise; releaseCommitted(context: AttachmentStorageQuotaContext): Promise; }>; export declare class MissingAttachmentStorageQuotaAdmissionError extends Error { readonly code: "ATTACHMENT_STORAGE_QUOTA_ADMISSION_MISSING"; constructor(); } export declare function requireAttachmentStorageQuotaAdmission(admission: AttachmentStorageQuotaAdmission | undefined): AttachmentStorageQuotaAdmission; export declare class StorageQuotaExceededError extends Error { readonly code: "STORAGE_QUOTA_EXCEEDED"; readonly usageBytes: number; readonly limitBytes: number; readonly requestedBytes: number; constructor(rejection: StorageQuotaRejection); } export declare function createStorageQuotaService(adapter: StorageQuotaAdapter): StorageQuotaService;