fastly-spa-yolo
v0.0.2
Published
YOLO deploy tiny SPA builds to Fastly Compute by embedding dist into Rust/Wasm.
Maintainers
Readme
fastly-spa-yolo
Deploy a tiny built SPA to Fastly Compute by embedding the whole output folder into the Rust/Wasm package.
Bad idea. Works great.
This is for prototypes, demos, hackathon apps, vibe-coded experiments, internal previews, and the moment where you already have a dist/ folder and just need a URL.
What it does
From an existing SPA project, this creates an isolated Fastly Compute Rust project in .fastly-spa-yolo/.
my-spa/
package.json
src/
dist/
index.html
assets/app.abcdef12.js
assets/app.css
.fastly-spa-yolo/
Cargo.toml
fastly.toml
rust-toolchain.toml
src/
main.rs
assets.rs
yolo_config.rs
config.jsonThe generated Rust keeps the Fastly handler in src/main.rs and puts the variable generated bits in tiny side files. src/assets.rs owns the rust-embed folder path:
#[derive(Embed)]
#[folder = "../dist/"]
pub struct Asset;Then requests are served directly from the embedded asset map.
/ -> index.html
/index.html -> index.html
/assets/app.abcdef12.js -> embedded JS asset
/dashboard -> index.html, SPA fallback
/assets/missing.js -> 404
/__yolo -> build metadata JSONInstall / initialize
From an existing SPA project root:
npx fastly-spa-yolo initFor no prompts:
npx fastly-spa-yolo init --yesWith explicit output folder:
npx fastly-spa-yolo init --yes --dist buildGenerated npm scripts
By default, init patches your root package.json with:
{
"scripts": {
"yolo:init": "npx --yes fastly-spa-yolo init",
"yolo:build": "npx --yes fastly-spa-yolo build",
"yolo:serve": "npx --yes fastly-spa-yolo serve",
"yolo:deploy": "npx --yes fastly-spa-yolo deploy"
}
}Skip that with:
npx fastly-spa-yolo init --no-scriptsCommands
init
Generates .fastly-spa-yolo/.
npx fastly-spa-yolo initUseful flags:
--yes, -y accept inferred defaults
--force, -f overwrite generated files
--dist <dir> SPA build output folder
--build-command <cmd> app build command
--name <name> Fastly/Cargo package name
--compute-dir <dir> generated project dir
--no-scripts do not patch package.json
--no-spa-fallback disable unknown route fallback to index.htmlbuild
Runs the app build, checks the dist folder, hashes it, then runs fastly compute build inside .fastly-spa-yolo/.
npm run yolo:buildPass Fastly CLI args after --:
npm run yolo:build -- --verboseserve
Runs the app build, then serves locally through Fastly Compute:
npm run yolo:servePass Fastly CLI args after --:
npm run yolo:serve -- --watchdeploy
Runs the app build, then runs fastly compute publish inside .fastly-spa-yolo/.
npm run yolo:deployNon-interactive Fastly defaults:
npm run yolo:deploy -- --accept-defaultshash-dist
Prints the SHA-256 hash of the configured dist folder.
npx fastly-spa-yolo hash-distdoctor
Prints basic environment/project checks.
npx fastly-spa-yolo doctorCache behavior
The generated Compute app uses this deliberately simple policy:
index.html Cache-Control: no-cache
assets with hex hashes Cache-Control: public, max-age=31536000, immutable
other static assets Cache-Control: public, max-age=60Every response gets:
X-Yolo-Build: <dist hash prefix>
ETag: "yolo-<build>-<path>"/__yolo returns something like:
{
"name": "my-app-yolo",
"build": "a1b2c3d4e5f6",
"assets": 3,
"mode": "embedded-spa-yolo"
}Requirements
You need:
- Node.js 18+
- Fastly CLI
- Rust / Cargo / rustup
The generated Compute project pins wasm32-wasip1 and uses a build script that copies the compiled Wasm to bin/main.wasm.
Caveats
This embeds static files into the Compute package. Every asset change means a new Wasm build and Fastly deploy.
That is the point.
For large sites, serious static hosting, or content-only deploys, use a real static pipeline or Fastly's KV-based static publishing tooling instead.
