import { Asset } from './asset'; import { AssetRuntimeLike } from './asset-runtime'; /** * Result of `resolveAssetForServing()` — lets advanced callers * stream bytes themselves while still reusing the access check. */ export interface ResolvedAssetBytes { asset: Asset; data: Buffer; contentType: string; filename: string; size: number; } /** * Options for `serveAsset()` and `resolveAssetForServing()`. */ export interface ServeAssetOptions { /** Shared asset runtime (collection + store). */ runtime: AssetRuntimeLike; /** * Either an asset id to look up or a fully-loaded `Asset` * instance. Passing the instance skips the `collection.get()` call. */ asset: string | Asset; /** * Tenant of the caller. When provided, the asset must either * belong to this tenant or be a global asset (`tenantId = null`) * or a 403 is returned. */ tenantId?: string | null; /** * Optional `Content-Disposition` — `'inline'` (default) or * `'attachment'`. Use `'attachment'` for download links. */ disposition?: 'inline' | 'attachment'; /** * Extra headers merged into the 200 response. Does not affect * 403/404/500 responses. */ headers?: Record; /** * Optional override for the filename used in `Content-Disposition`. * Defaults to the basename of the asset's `sourceUri` or `name`. */ filename?: string; /** * Optional access-check hook. Return `false` to short-circuit with a * 403 before bytes are loaded. Runs after the built-in tenant check. */ canAccess?: (asset: Asset) => boolean | Promise; /** * How to handle assets whose `sourceUri` is an `http(s)` URL rather * than a store-backed path. * * - `'error'` (default): treat a remote URI as an error (`500`). This * is the safe default — `sourceUri` is attacker-settable on many * asset-creation paths, so proxying it blindly is an SSRF vector * (fetching `169.254.169.254` cloud metadata, internal services, * etc.). Opt into `'proxy'` only for trusted sources. * - `'proxy'`: fetch the bytes via `globalThis.fetch` and return them * inline. Keeps the origin URL hidden from the client and preserves * the same tenant/access checks the local path gets. Guarded against * SSRF: the host must resolve to a public IP (private, loopback, * link-local, and cloud-metadata ranges are rejected), only * `http(s)` is allowed, and redirects are re-validated. * - `'redirect'`: return a `302` to `sourceUri` instead of fetching. * Skips the byte round-trip but exposes the origin URL. * * Anything that does not look like `http://` or `https://` is read * through the store as usual. */ remoteMode?: 'proxy' | 'redirect' | 'error'; /** * Custom `fetch` implementation for the `remoteMode: 'proxy'` path. * Defaults to `globalThis.fetch` (Node 18+). */ fetchImpl?: typeof fetch; /** * Maximum number of bytes to buffer when `remoteMode: 'proxy'` fetches * a remote asset. Protects against memory-exhaustion DoS from a hostile * (or compromised) origin streaming an unbounded body. Defaults to * {@link DEFAULT_REMOTE_MAX_BYTES} (50 MiB). */ remoteMaxBytes?: number; /** * Timeout in milliseconds for the `remoteMode: 'proxy'` fetch. Defaults * to {@link DEFAULT_REMOTE_TIMEOUT_MS} (10s). Prevents a slow/hung * origin from pinning the request indefinitely. */ remoteTimeoutMs?: number; /** * Custom `Response`-like constructor, for runtimes that don't have * a global `Response` (pre-Node-18, workers with a custom shim). * Defaults to `globalThis.Response`. */ responseCtor?: ResponseConstructor; } type ResponseConstructor = typeof Response; /** Default cap (50 MiB) on bytes buffered from a proxied remote asset. */ export declare const DEFAULT_REMOTE_MAX_BYTES: number; /** Default timeout (10s) for a proxied remote-asset fetch. */ export declare const DEFAULT_REMOTE_TIMEOUT_MS = 10000; /** * Resolve an asset + bytes for serving, enforcing the same tenant * and access checks as `serveAsset()` but returning the parts so * callers can render their own framework response. * * Throws `AssetServeError` with a status on any error so callers can * map to their HTTP layer. */ export declare function resolveAssetForServing(options: ServeAssetOptions): Promise; /** * Serve an asset as a standard Web `Response`. * * Returns: * - `404` when the asset id doesn't resolve * - `403` when the tenant mismatches or `canAccess` denies * - `302` when `remoteMode: 'redirect'` and the asset's `sourceUri` * is an `http(s)` URL * - `502` when `remoteMode: 'proxy'` and the origin fetch fails * - `500` when bytes fail to read (file missing, provider error) * - `200` with the raw bytes, `Content-Type`, `Content-Length`, and * `Content-Disposition` set otherwise. * * The resulting `Response` is framework-agnostic: SvelteKit `+server.ts` * endpoints, Hono, and plain Node HTTP all accept it via their Web * interop layer. */ export declare function serveAsset(options: ServeAssetOptions): Promise; /** * Error raised by `resolveAssetForServing()` with the HTTP status * callers should use when rendering their own response. * * `message` is the specific, server-side reason — safe to log, never sent to * the client. `clientMessage` is the opaque body {@link serveAsset} returns to * the HTTP client; it defaults to a generic, status-keyed string so the * specific reason can't leak (SSRF rejection cause, upstream status, etc.). */ export declare class AssetServeError extends Error { readonly status: number; /** Opaque body safe to return to the HTTP client. */ readonly clientMessage: string; constructor(message: string, status: number, clientMessage?: string); } export {}; //# sourceMappingURL=asset-serving.d.ts.map