export type AssetsOptions = { /** Where they are served from. `/studio` puts the entry document at `/studio`. */ readonly path: string; /** The directory on disk. Absolute. */ readonly root: string; /** * Paths answered from memory rather than from the directory, keyed relative to it. * * A static mount serves what a build wrote, and a build cannot know a fact that is * settled when the server starts. Studio's language manifest is the case this exists * for (ADR-0034): which languages a deployment offers is decided by the application, * by what it added and by what it narrowed the set to, and none of the three is * knowable to whoever ran `vite build` months earlier. * * Checked before the disk, so a document shadows a file of the same name — which is * what makes the bundle's own manifest a default rather than the answer. The value is * serialised as JSON once, at mount, and served `no-cache`: these are small and they * describe the deployment, so a cached one is a stale description of a live thing. */ readonly documents?: Readonly>; /** * What a request for an unknown path answers with. * * A single-page application routes in the browser, so `/studio/pages/42` has to * return the entry document rather than a 404 — the router decides what that URL * means, and only it can. `false` turns it off for a directory of plain files. */ readonly fallback?: string | false; /** * The directory, inside the root, whose files carry a fingerprint in their name. * * A bundler writes its output under one directory and hashes every name in it, and * copies `public/` to the root untouched. So which files may be kept forever is a * fact about *where* they are, and the deployment knows it — `assets/` for Vite, * `_next/static/` for Next.js, `false` for a directory of plain files. * * It used to be guessed from the name, by a pattern looking for a hexadecimal hash. * Rollup fingerprints in base64url, so the guess matched none of the 27 files this * repository builds and every one of them was served `no-cache`. Widening the * alphabet does not fix it: base64url is also the alphabet English is written in, * so `hero-photograph.jpg` and `photo-20260903.jpg` are indistinguishable from a * hash by any rule short of reading the manifest — and a wrong guess in that * direction pins a file in every browser's cache for a year, with no way to reach * it. A directory is a fact rather than a guess, and it is the caller's to state. */ readonly immutable?: string | false; }; export declare const assetContentType: (path: string) => string; /** A year — the longest `max-age` the specification gives any meaning to. */ export declare const IMMUTABLE = "public, max-age=31536000, immutable"; /** * Where a bundler puts what it fingerprints, unless the caller says otherwise. * * Vite's `assetsDir`, and so the directory both of this repository's mounts serve * from. A project on a bundler that chooses differently passes its own. */ export declare const DEFAULT_IMMUTABLE = "assets/"; /** * How long a browser may keep a file without asking again. * * A fingerprinted file may be kept forever, because its name changes when its * contents do. Everything else must be revalidated — above all the entry document, * which is the one file whose name never changes and which names all the others: a * cached one points at the assets of the deploy before this one. * * `no-cache` does not mean "do not store it". It means "store it, and ask before * using it", which with the `ETag` this mount now sends is a 304 and no body. */ export declare const assetCacheControl: (relative: string, immutable: string | false) => string; /** * Refuses a path that would leave the directory, and one naming a dotfile. * * The URL arrives from outside, and `../../etc/passwd` is a URL. Resolving first and * comparing after is what catches the encodings that normalising alone does not. * * This check is lexical, and lexical is not the whole of it: a symlink inside the * root leads out of it without any segment of the URL saying so, which `findAsset` * settles by resolving the real path. Both are needed — this one refuses the request * before anything touches a disk. */ export declare const resolveAsset: (root: string, requested: string) => string; export declare const isCompressible: (contentType: string) => boolean; /** * A validator for the file as it is now. * * Size and modification time, which is what nginx uses and what a file server can * answer without reading the bytes. Hashing the contents would be exact and would * cost a full read of every file on every request — a strong validator for a * property that only matters when the file has *not* changed. * * Strong rather than weak: these are bytes served verbatim, so two responses with * this validator really are identical, which is what lets a range request use it. */ export declare const assetETag: (size: number, modifiedAt: number) => string; export type ServedAsset = { readonly path: string; readonly size: number; readonly contentType: string; readonly cacheControl: string; /** What a conditional request is answered against. */ readonly etag: string; /** The same fact in the older spelling, for a client that sends the older header. */ readonly modifiedAt: Date; /** Whether compressing it would make it smaller. */ readonly compressible: boolean; readonly stream: () => NodeJS.ReadableStream; }; /** * The file a request resolves to, or `undefined` when nothing does. * * A directory resolves to its `index.html`, and an unknown path to the fallback — * that is what makes browser-side routing work behind this. */ export declare const findAsset: (options: AssetsOptions, requested: string) => Promise; //# sourceMappingURL=assets.d.ts.map