import { ProviderError } from './index'; export declare class StorageError extends ProviderError { readonly status?: number | undefined; constructor(code: string, message: string, status?: number | undefined); } /** Single-PUT ceiling. Above this a real implementation needs multipart. */ export declare const MAX_SINGLE_PUT_BYTES: number; export interface S3Config { endpoint: string; region: string; bucket: string; /** * `path` → `host/bucket/key`, `virtual` → `bucket.host/key`. * * Defaults to `path` because that is what every S3-compatible store other * than AWS expects, and getting it wrong presents as a 404 rather than as a * configuration error. */ addressing?: 'path' | 'virtual'; fetch?: typeof globalThis.fetch; timeoutMs?: number; } export interface S3Credentials { accessKeyId: string; secretAccessKey: string; /** STS / temporary credentials. Carried in `x-amz-security-token` when present. */ sessionToken?: string; } /** * Percent-encode a path segment per SigV4's rules. * * `encodeURIComponent` leaves `!'()*` alone and S3 does not, so a key containing * any of them signs correctly on our side and is rejected as a signature * mismatch by the vendor — an error that points at credentials rather than at * the filename that actually caused it. */ export declare function encodeSegment(segment: string): string; /** `20260731T140233Z` and `20260731`, the two forms every part of SigV4 wants. */ export declare function amzDate(at: Date): { full: string; short: string; }; export interface SignedRequest { url: string; method: string; headers: Record; } /** * AWS Signature Version 4. * * `at` is a parameter rather than read from the clock so the test suite can * check the signature against AWS's own published example vectors — the only * way to know the implementation is correct rather than merely self-consistent. */ export declare function signRequest(args: { method: string; url: URL; region: string; service?: string; credentials: S3Credentials; payloadHash: string; headers?: Record; at: Date; }): Promise; export interface PutObjectArgs { key: string; body: Uint8Array | string; contentType?: string; /** Written as `x-amz-meta-*`. Never a place for anything secret — see below. */ metadata?: Record; } export interface ObjectSummary { key: string; size: number; etag?: string; lastModified?: string; } export declare function createS3Client(config: S3Config, credentials: S3Credentials): { putObject(args: PutObjectArgs): Promise<{ etag?: string; }>; getObject(key: string): Promise; headObject(key: string): Promise; deleteObject(key: string): Promise; /** * List, following continuation tokens. * * S3 caps a response at 1000 keys and says so only by returning a token. * A client that ignores it silently lists the first thousand — which is * correct in every test and wrong in every production bucket. */ listObjects(prefix?: string, limit?: number): Promise; /** * A URL that works without our credentials, for a bounded time. * * The signature is in the query string, so anyone holding the URL can use * it — it is a bearer token that survives a browser history, a referrer * header and a support screenshot. Expiries are therefore short by default * and the caller has to ask for longer. */ presign(args: { key: string; expiresInSec?: number; method?: "GET" | "PUT"; }): Promise; bucket: string; endpoint: string; }; export type S3Client = ReturnType; export declare function parseListXml(xml: string): ObjectSummary[]; export type StorageRequest = { op: 'put'; key: string; body: Uint8Array | string; contentType?: string; metadata?: Record; } | { op: 'get'; key: string; } | { op: 'head'; key: string; } | { op: 'delete'; key: string; } | { op: 'list'; prefix?: string; limit?: number; } | { op: 'presign'; key: string; expiresInSec?: number; method?: 'GET' | 'PUT'; }; export declare const s3: import("./index").ProviderDefinition>>; export declare const storageProviders: readonly [import("./index").ProviderDefinition>>];