Menu
Public repository / 11133e70d7ecc0416eabf8ca4bb82fd3dd873147
AkurAI-Framework / site/content/docs/assets.md
Latest change 9df51040bf1687e4dc8a91557f02df52e8d5cf3a - feat(assets): content-hash fingerprinting + conservative minifiers by Ólafur Búi Ólafsson
# Asset pipeline: fingerprinting & minification
Static assets — CSS, JS, images — want two things before they ship: a
**cache-busting name** so browsers can cache them forever, and **fewer bytes** so
they download fast. The `akurai-assets` crate does both in pure Rust, zero
dependencies, correctness first.
## Fingerprinting (cache-busting)
A *fingerprint* is a short content hash of an asset's bytes. Same bytes → same
hash; change a single byte → a different hash. Fold that hash into the filename
and the URL itself becomes the cache key:
```rust
use akurai_assets::{fingerprint, hashed_name};
let bytes = b".box{color:red}";
let hash = fingerprint(bytes); // e.g. "9a1c0f3e7b2d8c41" (16 hex chars)
let name = hashed_name("app.css", bytes); // "app.9a1c0f3e7b2d8c41.css"
```
The hash is inserted **before the extension** (`app.css` → `app.<hash>.css`);
multi-dot names keep their stem (`vendor.min.js` → `vendor.min.<hash>.js`) and
extensionless names just get it appended (`LICENSE` → `LICENSE.<hash>`).
Because the name changes whenever the content changes, a fingerprinted file can
be served with an **immutable, far-future cache header** and never re-validated:
```
Cache-Control: public, max-age=31536000, immutable
```
The hash is a 64-bit [FNV-1a](https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function)
digest rendered as lowercase hex. It is **not cryptographic** and does not need
to be — cache-busting only needs determinism and good change-sensitivity, not
collision resistance against an adversary.
## Minification (fewer bytes)
Both minifiers are **conservative**: they only remove bytes that are provably
insignificant, and they never reorder, rename, or rewrite code. Correctness beats
ratio.
### `minify_css`
```rust
let out = akurai_assets::minify_css(".box {\n margin: 0 24px 60px;\n}");
// ".box{margin:0 24px 60px}"
```
- Strips `/* … */` comments.
- Collapses whitespace runs and drops the space where it's redundant (next to
`{ } : ; ,` or at the edges).
- Drops the optional `;` immediately before `}`.
- **Never touches values:** the spaces inside multi-value declarations like
`0 24px 60px` shadows are preserved, and the contents of string literals and
`url(…)` (including `data:` URIs with their own `;`, `,`, `:`) pass through
byte for byte.
### `minify_js`
```rust
let out = akurai_assets::minify_js("let a = 1; // a comment");
// "let a = 1;"
```
A deliberately minimal pass:
- Strips `// line` and `/* block */` comments.
- Trims trailing whitespace and drops blank lines.
- Does **not** join lines or collapse intra-line whitespace, so Automatic
Semicolon Insertion is unaffected.
- String and template-literal contents are copied verbatim, so a `//` inside a
string (e.g. a URL) is preserved.
**Limits (by design):** no regex-literal awareness and no `${…}` interpolation
parsing — full JS minification is risky, so this stays a safe whitespace/comment
pass. For aggressive minification, pre-minify upstream and let this layer only
fingerprint.
## How the CLI will use it
On build (or `dev` startup), the static server will hash each asset, optionally
minify CSS/JS, write the asset under its fingerprinted name, and rewrite
references in served HTML to the hashed URLs. Fingerprinted responses get
`Cache-Control: public, max-age=31536000, immutable`; the unhashed entry HTML
stays uncached so new deploys are picked up immediately.
## Status
Fingerprinting and both minifiers are implemented and tested (`akurai-assets`).
Wiring into `crates/cli` (build-time hashing + immutable cache headers) is the
next step.