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 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 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. Generate the API worker config with tamer wrangler --env local, run wrangler dev for the API, and point Vite at http://127.0.0.1:8787. Tamer does not generate .env for local dev — the consumer's base.ts vars block already has the local values declared. See Local Development.

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.