Skip to content

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

ts
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:

  1. Write wrangler.json (existing behavior — vars resolved against state).
  2. Spawn build.command in the worker's path directory with the resolved vars as environment variables (merged onto process.env).
  3. Run wrangler typeswrangler 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 build on SPA workers in tamer/workers/spa/base.ts.
  • Delete any .env-writer scripts or codegen.spaViteEnv usage.
  • Vite reads VITE_* from process.env at build time (its default behavior when env vars are set). No .env file 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)
ApproachWhy rejected
App-repo .env writer scriptSecond pipeline, derived file, stale-value bugs
codegen.spaViteEnv (Tamer generates a file)File artifact in the app source tree
Runtime /__tamer/public-config.json endpointCouples 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

Released under the Tamer Evaluation License.