//#region src/storage.d.ts
/** A stored object's metadata (no body). */
interface StorageObjectHead {
  key: string;
  size: number;
  etag: string;
  /** Content-Type as stored (set at put; a HOST that validated bytes stores the DETECTED type). */
  contentType?: string;
  customMetadata?: Record<string, string>;
  /** Last-modified epoch ms, when the backend reports it. */
  uploadedAt?: number;
}
/** A stored object WITH its body. */
interface StorageObject extends StorageObjectHead {
  body: ReadableStream<Uint8Array>;
}
interface PutOptions {
  contentType?: string;
  cacheControl?: string;
  customMetadata?: Record<string, string>;
}
interface PutResult {
  key: string;
  size: number;
  etag: string;
}
interface ListOptions {
  prefix?: string;
  cursor?: string;
  limit?: number;
}
interface ListResult {
  objects: StorageObjectHead[];
  /** Present iff more pages remain; pass back as ListOptions.cursor. */
  cursor?: string;
}
interface PresignOptions {
  /** REQUIRED, bounded — a presigned URL is a time-boxed capability, never standing. */
  expiresIn: number;
  contentType?: string;
}
interface PresignedUrl {
  url: string;
  method: 'PUT' | 'GET';
  headers: Record<string, string>;
  expiresIn: number;
}
/** Body shapes a put accepts — matches the r2-binding leaf so the R2 adapter wraps it directly. */
type StorageBody = ReadableStream | ArrayBuffer | ArrayBufferView | Blob | Uint8Array;
/**
 * Host-agnostic object store. R2 is the FIRST reference adapter, not the lock
 * (CLAUDE.md §5). A new host's adapter slots in here without touching any consumer.
 */
interface StorageAdapter {
  put(key: string, body: StorageBody, opts?: PutOptions): Promise<PutResult>;
  /** Body + metadata. null when the key is absent (NEVER throw on absence — no enumeration oracle). */
  get(key: string): Promise<StorageObject | null>;
  /** Metadata only. null when absent. */
  head(key: string): Promise<StorageObjectHead | null>;
  /** Idempotent — deleting an absent key resolves, never throws. */
  delete(key: string): Promise<void>;
  list(opts?: ListOptions): Promise<ListResult>;
  /** Mint a browser-direct PUT capability for ONE exact key (never prefix/wildcard). */
  presignPut(key: string, opts: PresignOptions): Promise<PresignedUrl>;
  /** OPTIONAL — signed read for private buckets. Public-bucket hosts omit it. */
  presignGet?(key: string, opts: PresignOptions): Promise<PresignedUrl>;
}
type StorageErrorCode = 'invalid_key' | 'not_found' | 'backend';
interface StorageErrorShape {
  readonly isStorageError: true;
  readonly code: StorageErrorCode;
  readonly message: string;
}
declare class StorageError extends Error implements StorageErrorShape {
  readonly isStorageError: true;
  readonly code: StorageErrorCode;
  constructor(code: StorageErrorCode, message: string);
}
/** Cross-package-safe guard — use this, NOT instanceof. */
declare function isStorageError(e: unknown): e is StorageErrorShape;
/**
 * Shared exact-key guard. Throws StorageError('invalid_key') on prefix/wildcard/empty/trailing-slash.
 * Both adapters call this so presignPut enforces the single-exact-key floor identically.
 */
declare function assertExactStorageKey(key: string): void;
/** Shared expiry guard. Throws RangeError when expiresIn is not a positive finite number of seconds. */
declare function assertPresignExpiry(expiresIn: number): void;
//#endregion
export { ListOptions, ListResult, PresignOptions, PresignedUrl, PutOptions, PutResult, StorageAdapter, StorageBody, StorageError, StorageErrorCode, StorageErrorShape, StorageObject, StorageObjectHead, assertExactStorageKey, assertPresignExpiry, isStorageError };