Skip to content

Versioned assets fix worker env selection#3504

Closed
scamiv wants to merge 2 commits intomainfrom
versioned-assets-fix-worker
Closed

Versioned assets fix worker env selection#3504
scamiv wants to merge 2 commits intomainfrom
versioned-assets-fix-worker

Conversation

@scamiv
Copy link
Contributor

@scamiv scamiv commented Mar 24, 2026

Description

This is a hotfix for the worker regression introduced by the /api/env removal / HTML-bootstrap rework.

The main thread can read window.BOOTSTRAP_CONFIG, but the game worker cannot.
This PR keeps the current runtime behavior for the main thread and restores worker initialization by making the client config loader worker-safe.

What Changed

  • getServerConfigFromClient() now:
    • uses window.BOOTSTRAP_CONFIG?.gameEnv when window exists
    • falls back to bundled process.env.GAME_ENV when running in the worker
  • adds test coverage for the no-bootstrap fallback path

Why This Hotfix Uses This Approach

This branch intentionally does not switch the whole client config path to process.env.GAME_ENV.
A fully “clean” switch to process.env.GAME_ENV everywhere was tested https://github.com/openfrontio/OpenFrontIO/compare/fix-worker-config-env?expand=1 , but it changed behavior on test/staging deployments.

The client-side process.env.GAME_ENV value is derived from the Vite build mode. That means production builds resolve to prod even when the runtime deployment should behave as staging, which caused test-domain deployments to use the production API / Turnstile configuration.

This hotfix avoids that behavior change by preserving the existing runtime bootstrap path on the main thread and only using the bundled env where it is actually needed: inside the worker.

This hotfix preserves the current environment-selection behavior on the main thread:

  • main thread: runtime HTML bootstrap
  • worker: bundled fallback only because window is unavailable there

That keeps the deployed behavior aligned with the existing staging/preprod setup while fixing the worker crash.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

DISCORD_USERNAME

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 24, 2026

Walkthrough

The configuration loader was updated to handle non-browser execution contexts. It now conditionally reads from window.BOOTSTRAP_CONFIG?.gameEnv only when window is available, falling back to process.env.GAME_ENV otherwise. A corresponding test case was added to verify the fallback behavior works correctly.

Changes

Cohort / File(s) Summary
Configuration Loading
src/core/configuration/ConfigLoader.ts
Updated getServerConfigFromClient() to check if window is defined before accessing window.BOOTSTRAP_CONFIG, with fallback to process.env.GAME_ENV for non-browser contexts. Error message updated for clarity.
Configuration Tests
tests/core/configuration/ConfigLoader.test.ts
Added test case verifying config loader correctly uses process.env.GAME_ENV when window.BOOTSTRAP_CONFIG is unavailable. Added setup and teardown logic to safely manage process.env.GAME_ENV mutations across tests.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🌍 From browser to server, the config now roams,
Checking for window—when absent, finds home,
In process.env's arms, a fallback so neat,
Two paths, one loader, the circle's complete! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title refers to a real aspect of the change (worker env selection), but it is not clearly descriptive of the main change and uses vague terminology that may not communicate the core fix to a teammate scanning history. Consider a more specific title like 'Make client config loader worker-safe with fallback to bundled env' to clarify that this fixes worker initialization by handling missing window context.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is clearly related to the changeset, explaining the worker regression, the root cause, and the specific approach taken in the fix with context about why alternatives were rejected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/core/configuration/ConfigLoader.test.ts (1)

28-34: Add one test that truly covers the worker branch (window undefined).

This case currently keeps window available, so it does not validate the typeof window === "undefined" path in ConfigLoader.

Possible test shape
+  test("falls back to bundled env in worker context (no window)", async () => {
+    const originalWindow = (globalThis as { window?: Window }).window;
+    delete (globalThis as { window?: Window }).window;
+    process.env.GAME_ENV = "prod";
+
+    const config = await getServerConfigFromClient();
+    expect(config.env()).toBe(GameEnv.Prod);
+
+    (globalThis as { window?: Window }).window = originalWindow;
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/core/configuration/ConfigLoader.test.ts` around lines 28 - 34, The
current test doesn't exercise the worker branch where typeof window ===
"undefined"; add a test (or modify the "falls back to bundled env when bootstrap
config is unavailable" case) that temporarily removes the global window
reference before calling getServerConfigFromClient (e.g., save const
originalWindow = (global as any).window, delete (global as any).window or set it
to undefined), call getServerConfigFromClient and assert env() === GameEnv.Prod,
then restore the originalWindow in a finally/after block to avoid side effects;
target the ConfigLoader code path that checks typeof window === "undefined" so
the worker branch is executed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/core/configuration/ConfigLoader.ts`:
- Around line 42-53: The code currently uses bootstrapGameEnv ?? bundledGameEnv
for gameEnv which allows the build-time bundledGameEnv to be used in browser
contexts when bootstrap is missing; change the logic so browsers (typeof window
!== "undefined") always use bootstrapGameEnv only, and only non-browser runtimes
(workers/Node) fall back to bundledGameEnv. Update the assignment for gameEnv to
prefer bootstrapGameEnv in browser contexts and to use bootstrapGameEnv ??
bundledGameEnv when window is undefined, then continue to call
getServerConfig(gameEnv) and assign cachedSC as before (references:
bootstrapGameEnv, bundledGameEnv, gameEnv, getServerConfig, cachedSC).

---

Nitpick comments:
In `@tests/core/configuration/ConfigLoader.test.ts`:
- Around line 28-34: The current test doesn't exercise the worker branch where
typeof window === "undefined"; add a test (or modify the "falls back to bundled
env when bootstrap config is unavailable" case) that temporarily removes the
global window reference before calling getServerConfigFromClient (e.g., save
const originalWindow = (global as any).window, delete (global as any).window or
set it to undefined), call getServerConfigFromClient and assert env() ===
GameEnv.Prod, then restore the originalWindow in a finally/after block to avoid
side effects; target the ConfigLoader code path that checks typeof window ===
"undefined" so the worker branch is executed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cdf34b96-c095-4db3-82ae-9c821273670f

📥 Commits

Reviewing files that changed from the base of the PR and between 496f100 and 7c3e77f.

📒 Files selected for processing (2)
  • src/core/configuration/ConfigLoader.ts
  • tests/core/configuration/ConfigLoader.test.ts

Comment on lines +42 to +53
const bootstrapGameEnv =
typeof window !== "undefined"
? window.BOOTSTRAP_CONFIG?.gameEnv
: undefined;
// Vite replaces this at build time for browser and worker bundles.
const bundledGameEnv = process.env.GAME_ENV;
const gameEnv = bootstrapGameEnv ?? bundledGameEnv;
if (!gameEnv) {
throw new Error("Missing client server config");
}

cachedSC = getServerConfig(bootstrapGameEnv);
cachedSC = getServerConfig(gameEnv);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Inspecting client env resolution branch in ConfigLoader:"
rg -n -C3 'bootstrapGameEnv|bundledGameEnv|gameEnv =|typeof window' src/core/configuration/ConfigLoader.ts

echo
echo "Inspecting Vite define mapping for process.env.GAME_ENV:"
rg -n -C3 '"process.env.GAME_ENV"' vite.config.ts

Repository: openfrontio/OpenFrontIO

Length of output: 1154


Restrict bundled environment fallback to worker runtimes only.

At line 48, bootstrapGameEnv ?? bundledGameEnv falls back to build-time config even in browser contexts when bootstrap is missing. This masks bootstrap failures and can route staging/test environments to the production build-time value.

Suggested fix
-  const bootstrapGameEnv =
-    typeof window !== "undefined"
-      ? window.BOOTSTRAP_CONFIG?.gameEnv
-      : undefined;
-  // Vite replaces this at build time for browser and worker bundles.
-  const bundledGameEnv = process.env.GAME_ENV;
-  const gameEnv = bootstrapGameEnv ?? bundledGameEnv;
+  const gameEnv =
+    typeof window !== "undefined"
+      ? window.BOOTSTRAP_CONFIG?.gameEnv
+      : process.env.GAME_ENV; // worker / non-window runtime
   if (!gameEnv) {
     throw new Error("Missing client server config");
   }

This ensures browsers always use runtime bootstrap config and only workers fall back to the build-time environment variable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/configuration/ConfigLoader.ts` around lines 42 - 53, The code
currently uses bootstrapGameEnv ?? bundledGameEnv for gameEnv which allows the
build-time bundledGameEnv to be used in browser contexts when bootstrap is
missing; change the logic so browsers (typeof window !== "undefined") always use
bootstrapGameEnv only, and only non-browser runtimes (workers/Node) fall back to
bundledGameEnv. Update the assignment for gameEnv to prefer bootstrapGameEnv in
browser contexts and to use bootstrapGameEnv ?? bundledGameEnv when window is
undefined, then continue to call getServerConfig(gameEnv) and assign cachedSC as
before (references: bootstrapGameEnv, bundledGameEnv, gameEnv, getServerConfig,
cachedSC).

@github-project-automation github-project-automation bot moved this from Triage to Development in OpenFront Release Management Mar 24, 2026
@scamiv
Copy link
Contributor Author

scamiv commented Mar 24, 2026

This fixes the regression, but the config model is still mixed between runtime bootstrap and bundled env fallback.

The cleaner long-term fix would be to unify client config loading around a single runtime source of truth and pass that explicitly, instead of relying on two different env sources.

@scamiv scamiv marked this pull request as draft March 24, 2026 15:25
@scamiv scamiv closed this Mar 24, 2026
@github-project-automation github-project-automation bot moved this from Development to Complete in OpenFront Release Management Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Complete

Development

Successfully merging this pull request may close these issues.

1 participant