Skip to content

fix(env): assign window.__ENV directly instead of queueing it - #6494

Merged
waleedlatif1 merged 1 commit into
stagingfrom
fix/runtime-env-injection
Aug 10, 2026
Merged

fix(env): assign window.__ENV directly instead of queueing it#6494
waleedlatif1 merged 1 commit into
stagingfrom
fix/runtime-env-injection

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • <EnvScript> defaults to Next's <Script strategy='beforeInteractive'>, which never assigns window.__ENV — it pushes the assignment onto self.__next_s. appBootstrap is that queue's only consumer, reads it once, and short-circuits to hydrate() when it's empty.
  • The bootstrap chunk's <script async> tag sits ~13KB earlier in the document than the env tag (byte ~790 vs ~13,900), so whenever that chunk runs first the queue drains empty, nothing drains it again, and window.__ENV stays undefined for the entire life of the document — every getEnv read empty until a reload happens to win the race.
  • disableNextScript emits a plain inline <script> that assigns unconditionally. When it's parsed before the bootstrap chunk it lands strictly earlier than the drain would have; when it isn't, the value arrives a few milliseconds late instead of never.
  • Records whether the assignment was still missing at module-init, so the residual — module-scope reads in env-flags (isHosted et al) that freeze whatever they see — is measured rather than assumed.

This is what killed the workflow editor: with __ENV absent, getBaseUrl() throws inside a useMemo (workflow-block.tsx:399 for webhook trigger URLs, deploy-modal.tsx:237), taking the route down. The misrouted websocket in the report — wss://www.sim.ai/socket.io/ instead of NEXT_PUBLIC_SOCKET_URL — is the same missing global, and is the cheapest tell that it happened.

Render-phase reads are safe once the assignment is unconditional: nothing renders before the RSC payload, which streams from <body> — after the <head> script. Only module-scope reads lack that ordering, which is what the metric measures.

Type of Change

  • Bug fix

Testing

public-env-script.test.tsx asserts the emitted markup assigns window.__ENV and contains no __next_s push; verified it fails when disableNextScript is removed. Type-check green, lint clean, 974 tests passing across the touched areas.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

`<EnvScript>` defaults to Next's `<Script strategy='beforeInteractive'>`, which
does not assign `window.__ENV` — it pushes the assignment onto `self.__next_s`.
That queue has one consumer, `appBootstrap`, which reads it once and
short-circuits to `hydrate()` when empty. The bootstrap chunk's `<script async>`
tag sits ~13KB earlier in the document than the env tag, so when that chunk runs
first the queue drains empty, nothing drains it again, and `window.__ENV` stays
undefined for the life of the document — every `getEnv` read empty until a
reload wins the race.

`disableNextScript` emits a plain inline `<script>` that assigns
unconditionally, so a lost race costs a few milliseconds instead of the session.

Also records whether the assignment was still missing at module-init so the
residual (module-scope reads in `env-flags`, which freeze what they see) is
measured rather than assumed.
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 10, 2026 5:01pm

Request Review

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes early document bootstrap ordering for all deployments; incorrect markup could break every client NEXT_PUBLIC_* read, though tests and the direct-assignment approach reduce that risk.

Overview
Fixes a race where window.__ENV could stay undefined for the whole page load, breaking getEnv reads (workflow editor crashes, wrong websocket URLs, etc.).

<EnvScript> now uses disableNextScript on both hosted (PublicEnvScript) and self-hosted (RuntimePublicEnvScript in layout.tsx). That stops Next’s beforeInteractive path, which only queued the assignment on self.__next_s and could lose it when the bootstrap chunk ran first—so the inline script assigns window.__ENV directly when the parser hits it.

Adds publicEnvMissingAtModuleInit in env.ts to record when __ENV was still missing at first client module evaluation (frozen env-flags at import time). PostHog emits runtime_env_missing_at_module_init when that flag is set.

Tests now assert rendered markup contains a direct window['__ENV'] = assignment and no __next_s deferral.

Reviewed by Cursor Bugbot for commit d80e45b. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR changes hosted and self-hosted public-environment injection to emit a direct inline window.__ENV assignment, avoiding the one-shot Next.js bootstrap queue race. It also records whether the environment global was absent during client module initialization.

  • Passes disableNextScript on both environment-script paths.
  • Updates static-markup tests to verify direct assignment and absence of __next_s.
  • Adds a PostHog event for environment state observed at module initialization.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete changed-code defects identified.

Both deployment paths use the library-supported direct-script option, the repository CSP permits the resulting inline script, and the telemetry addition does not establish a reachable application failure.

Important Files Changed

Filename Overview
apps/sim/app/_shell/public-env-script.tsx Hosted environment output now uses the library-supported plain-script mode to assign the global directly.
apps/sim/app/layout.tsx The self-hosted runtime environment component now uses the same direct-assignment mode.
apps/sim/lib/core/config/env.ts Adds a browser-only module-initialization snapshot indicating whether the public environment global was absent.
apps/sim/app/_shell/providers/posthog-provider.tsx Emits telemetry after PostHog initialization when the module-initialization snapshot indicates the environment was missing.
apps/sim/app/_shell/public-env-script.test.tsx Replaces component-implementation assertions with emitted-markup checks for direct assignment and queue avoidance.

Sequence Diagram

sequenceDiagram
  participant Parser as HTML Parser
  participant Env as Inline Env Script
  participant Boot as Next Bootstrap
  participant App as Client App
  Parser->>Env: Parse plain script
  Env->>Env: Assign window.__ENV directly
  Parser->>Boot: Load/execute async bootstrap
  Boot->>App: Hydrate application
  App->>Env: Read public environment values
Loading

Reviews (1): Last reviewed commit: "fix(env): assign window.__ENV directly i..." | Re-trigger Greptile

@waleedlatif1
waleedlatif1 merged commit 5da48d0 into staging Aug 10, 2026
30 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/runtime-env-injection branch August 10, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant