SPA Build
How Tamer compiles a single-page app per environment so compile-time values (like VITE_*) reach the bundle without .env files or codegen steps.
The pattern
Compile-time per env. The SPA is rebuilt per env with the API URL baked into the bundle. The SPA Worker stays a pure assets handler (env.ASSETS.fetch) — no runtime config endpoint, no fetch handler, no key-allowlist security boundary.
Tamer is the build runner. tamer deploy --env X spawns the SPA build with the worker's resolved vars (references resolved against state) passed as environment variables. Vite reads VITE_* keys by its own convention; the rest are ignored. No .env file, no codegen step, no app-repo script.
Vars are uniform. Every worker — API or SPA — has the same vars shape. Tamer does not model "compile-time vs runtime" vars; that distinction is Vite's, not Tamer's. All vars land in wrangler.json for the runtime Worker; the SPA build additionally consumes them at compile time via the spawned process environment.
Declare build on SPA workers
defineWorker({
build: { command: "vite build" },
assets: { directory: "dist", not_found_handling: "single-page-application" },
vars: {
ENVIRONMENT: "local",
VITE_API_CLIENT_URL: "http://127.0.0.1:8993/v1",
// ...
},
env: {
dev: { vars: { VITE_API_CLIENT_URL: "https://dev.api.app.example.com/v1" } },
},
});During tamer deploy --env X, for each worker with build set:
- Write
wrangler.json(existing behavior — vars resolved against state). - Spawn
build.commandin the worker'spathdirectory with the resolvedvarsas environment variables (merged ontoprocess.env). - Run
wrangler types→wrangler deploy(existing behavior).
No file artifacts. No .env. No wrangler.json read-back. The values Tamer holds in memory are passed directly to the build subprocess.
What consumers do
- Declare
buildon SPA workers intamer/workers/spa/base.ts. - Delete any
.env-writer scripts orcodegen.spaViteEnvusage. - Vite reads
VITE_*fromprocess.envat build time (its default behavior when env vars are set). No.envfile required.
Local dev
vite dev (standalone HMR) is outside Tamer's scope. Consumers configure Vite's dev proxy or run wrangler dev (which serves assets + worker). Tamer does not generate .env for local dev — the consumer's base.ts vars block already has the local values declared.
Why not other approaches (design history)
| Approach | Why rejected |
|---|---|
App-repo .env writer script | Second pipeline, derived file, stale-value bugs |
codegen.spaViteEnv (Tamer generates a file) | File artifact in the app source tree |
Runtime /__tamer/public-config.json endpoint | Couples SPA Worker to config serving, adds a fetch handler + security boundary |
buildVars (separate key for compile-time vars) | Special-cases SPA workers; leaks build-system knowledge into Tamer's config shape |