export declare const CACHE_ENTRY_SCHEMA_VERSION = 1; export interface CacheEntry { schemaVersion: number; url: string; fetchedAt: string; status: number; headers: Record; body: string; } export interface RedirectPointerEntry { schemaVersion: number; redirectsTo: string; fetchedAt: string; status: number; } export type AnyCacheEntry = CacheEntry | RedirectPointerEntry; export declare function isRedirectPointer(entry: AnyCacheEntry): entry is RedirectPointerEntry; /** * Pluggable cache storage. The default is {@link FilesystemCacheBackend} * (dir-based); a host can supply its own (e.g. an R2-backed store) to persist * the cache across ephemeral-filesystem runs. The freshness / revalidation / * redirect / negative-cache logic in cachedFetch is backend-agnostic — a * backend is dumb `get`/`set` storage and nothing more. */ export interface CacheBackend { get(url: string): Promise; set(url: string, entry: AnyCacheEntry): Promise; } export declare function cacheKeyFor(url: string): string; export declare function readCacheEntry(dir: string, url: string): Promise; export declare function writeCacheEntry(dir: string, requestUrl: string, entry: AnyCacheEntry): Promise; /** Default cache backend — the original dir-based filesystem store. */ export declare class FilesystemCacheBackend implements CacheBackend { private readonly dir; constructor(dir: string); get(url: string): Promise; set(url: string, entry: AnyCacheEntry): Promise; } export declare const NEGATIVE_CACHE_TTL_MS: number; export declare function isCacheEntryFresh(fetchedAtIso: string, ttlMs: number, now?: number): boolean; export declare function shouldNegativeCache(status: number): boolean; export type Fetcher = (url: string, init?: RequestInit) => Promise; export interface CacheConfig { /** Filesystem cache dir — used by the default FilesystemCacheBackend when no `backend` is given. */ dir?: string; /** Custom storage backend; overrides `dir` (e.g. an R2-backed store on ephemeral-fs serverless). */ backend?: CacheBackend; ttlMs: number; } export interface CachedFetchOptions { timeoutMs: number; cache: CacheConfig | null; fetcher?: Fetcher; method?: "GET"; /** * External abort signal, e.g. from an audit-level AbortController. Combined * with the per-request timeout — whichever fires first cancels the fetch. */ signal?: AbortSignal; /** * Per-hop URL validator. Called before the initial fetch AND before every * redirect hop. Throw to refuse the fetch (e.g. SSRF guard rejecting a 302 * to a private IP). Use cases: DNS-resolved SSRF check, tenant host allow- * list, per-request budget enforcement. */ validateHop?: (url: string) => Promise; /** * When false, 3xx responses are returned as-is (status + Location header) * instead of followed. Default: true. */ followRedirects?: boolean; /** * Per-fetch observation sink. Called exactly once per fetch (final hop for * redirected requests). Wall-clock duration reflects the actual origin * round-trip even when the cache revalidated (304). Pure cache hits are * reported with `fromCache: true`, `durationMs` ≈ 0, and `revalidated: false`. */ onObservation?: (obs: import("./fetch-observer.js").FetchObservation) => void; } export interface CachedFetchResult { url: string; status: number; headers: Record; body: string; fromCache: boolean; redirectChain: string[]; } export declare function cachedFetch(url: string, opts: CachedFetchOptions): Promise; /** * SSRF-guarded fetch for non-audit callers. Validates the source URL (and every * redirect hop) against the private-range blocklist via `validateTargetHost`, * applies the pseolint bot UA, and honours a per-request timeout + external * abort signal. * * Throws: * - `SSRFError` if the target (or any redirect hop) resolves to a private * / reserved range. * - `DnsResolutionError` if the hostname doesn't resolve. * - Network / timeout errors from the underlying fetch. * * Returns the same shape as `cachedFetch` (no cache is used; every call goes * to the origin). Intended for SaaS hosts that want an SSRF-safe fetch outside * the audit pipeline (metadata lookups, favicon fetches, screenshot URL * validation, etc.). */ export interface CacheSizeInfo { bytes: number; fileCount: number; } export interface PruneResult { before: CacheSizeInfo; after: CacheSizeInfo; removedEntries: number; removedTmpFiles: number; } /** Total bytes + file count of valid cache entries in `dir`. Ignores `.tmp` and unrelated files. */ export declare function getCacheSizeInfo(dir: string): Promise; /** * Prune cache dir so total size stays under `maxBytes`. Evicts oldest-mtime * entries first (approximate LRU — `writeCacheEntry` touches mtime on fetch * and on 304 revalidation, so hot entries keep their mtime fresh; cold entries * that are never re-crawled age out). Also sweeps leftover `.tmp` files from * crashed writes. * * `maxBytes <= 0` disables size-based eviction (tmp sweep still runs). */ export declare function pruneCache(dir: string, maxBytes: number): Promise; /** Remove every cache entry (and any leftover `.tmp` files). Dir itself is kept. */ export declare function clearCache(dir: string): Promise<{ removed: number; }>; export declare function safeFetch(url: string, options?: { timeoutMs?: number; signal?: AbortSignal; followRedirects?: boolean; fetcher?: Fetcher; }): Promise; //# sourceMappingURL=cache.d.ts.map