Skip to content

refactor: migrate jumpToState to Astro.licals to prevent build-time state leakage - #1271

Open
riya-saharan wants to merge 4 commits into
processing:v1from
riya-saharan:main
Open

refactor: migrate jumpToState to Astro.licals to prevent build-time state leakage#1271
riya-saharan wants to merge 4 commits into
processing:v1from
riya-saharan:main

Conversation

@riya-saharan

Copy link
Copy Markdown

This PR addresses the issue of state leakage across different locales during parallel static builds.

The Problem:
Previously, the jumpToState (used for the "Jump To" sidebar) was managed via a global singleton in src/globals/state.ts. During high-concurrency builds, pages from different locales (e.g., Korean and Chinese) would overwrite this global variable, leading to incorrect translations appearing on the wrong pages.

The Solution:
I have migrated the state management to Astro.locals, which provides an isolated data store for every unique request/page build.

Key Changes:

State Isolation: Moved jumpToState into App.Locals via env.d.ts.

Component Updates: Refactored Nav.astro, NavPanels.tsx, and Settings.tsx to consume state from the request context.

Thread-Safety: Removed the global variable and setter from state.ts.

Lifecycle Management: Updated all layouts and localized page entry points to explicitly set or reset their own jumpToState within the Astro.locals scope.

Verification:
Successfully passed npm run build with 0 errors. Verified language isolation in a local preview environment across multiple localized routes.

@limzykenneth

Copy link
Copy Markdown
Member

@riya-saharan There is a merge conflict, do try to fix it and I can merge after that. Thanks.

@lirenjie95 lirenjie95 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hi @riya-saharan, thanks for the PR! Moving jumpToState from a global singleton to Astro.locals makes sense for build-time isolation.

A few requests:

1. Missing trailing newlines
Most modified files are missing their final newline. Please add them back to reduce diff noise.

2. Link to #1207 and resolve conflicts
Could you update the PR description to include Fixes #1207 (or Closes #1207) and resolve the current merge conflicts with main?

3. Hard-to-reproduce bug needs more confidence
Since the issue in #1207 only appears randomly in production and is difficult to reproduce locally, could you add some extra verification? For example, a short note in the PR description explaining why Astro.locals guarantees per-request isolation during SSG would help reviewers be confident this actually fixes the race condition.

Please let me know once these are addressed. Thanks again!

@Nwakaego-Ego

Copy link
Copy Markdown
Contributor

Hi @riya-saharan, just checking in on this PR. Both @limzykenneth and @lirenjie95 left feedback that needs to be addressed, specifically the merge conflicts and the points raised in the May review. Are you still working on this? Please let us know if you need help or are no longer able to continue. Thank you.

@riya-saharan

Copy link
Copy Markdown
Author

Hi @Nwakaego-Ego, thanks for checking in — I am still working on this. Currently resolving the merge conflicts locally and will address the review feedback (trailing newlines, linking #1207, and the isolation explanation) in the same push. Will update here once it's ready for re-review.

Copilot AI lite review requested due to automatic review settings August 15, 2026 11:35
@riya-saharan

Copy link
Copy Markdown
Author

Thanks for the review! I've resolved the merge conflicts with v1 and fixed a few build issues that surfaced during the merge (missing imports, a duplicate state declaration). Ran a full production build afterward — 6129 pages built successfully with 0 errors.

Fixes #1207

Re: isolation guarantee during SSG — per Astro's own middleware docs: "locals is an object that lives and dies within a single Astro route; when your route page is rendered, locals won't exist anymore and a new one will be created," and this holds "even when this middleware runs at build time." So even with our build.concurrency: 2 setting running pages in parallel, each page gets its own isolated locals object — there's no shared mutable state for concurrent locale builds to race on, unlike the old module-level singleton in state.ts.

One thing I want to flag directly: Astro's docs describe locals as intended to be set via middleware, not directly in page/layout frontmatter (see withastro/astro#11174). This PR sets it directly in layouts instead. It works correctly and passes build/preview testing, but wanted to be upfront in case it's worth discussing — happy to move to a middleware-based approach if you'd prefer.

Working on the trailing newlines next.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the website’s “Jump To” sidebar state handling to avoid locale cross-contamination during parallel/static builds by moving jumpToState from a global singleton to per-request Astro.locals.

Changes:

  • Migrated jump-to state storage from src/globals/state.ts runtime globals to Astro.locals with App.Locals typing in src/env.d.ts.
  • Updated pages/layouts/components to set/read Astro.locals.jumpToState and removed most jumpToState prop plumbing through BaseLayout/Nav.
  • Removed the global jumpToState variable and setJumpToState setter, leaving only the shared types.

Reviewed changes

Copilot reviewed 30 out of 32 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/pages/index.astro Clears Astro.locals.jumpToState for the homepage route.
src/pages/education-resources/index.astro Sets page-specific Astro.locals.jumpToState links for education resources.
src/pages/about.astro Clears Astro.locals.jumpToState for about route.
src/pages/[locale]/index.astro Clears Astro.locals.jumpToState for localized homepage route.
src/pages/[locale]/education-resources/index.astro Sets localized education-resources jump-to links via Astro.locals.
src/pages/[locale]/about.astro Clears Astro.locals.jumpToState for localized about route.
src/layouts/TutorialsLayout.astro Sets tutorials page jump-to links into Astro.locals and stops passing prop.
src/layouts/TutorialLayout.astro Stores generated jump-to state in Astro.locals and removes prop passing.
src/layouts/TextDetailLayout.astro Clears Astro.locals.jumpToState and removes jumpToState={null} prop.
src/layouts/SketchLayout.astro Clears Astro.locals.jumpToState and removes jumpToState={null} prop.
src/layouts/SketchesLayout.astro Clears Astro.locals.jumpToState and removes jumpToState={null} prop.
src/layouts/SearchLayout.astro Clears Astro.locals.jumpToState and removes jumpToState={null} prop.
src/layouts/ReferenceLayout.astro Sets reference directory jump-to state into Astro.locals and removes prop.
src/layouts/ReferenceItemLayout.astro Sets per-reference-item jump-to state into Astro.locals and removes prop.
src/layouts/PeopleLayout.astro Clears Astro.locals.jumpToState and removes jumpToState={null} prop.
src/layouts/LibrariesLayout.astro Sets libraries jump-to state into Astro.locals and removes prop.
src/layouts/HomepageLayout.astro Clears Astro.locals.jumpToState and removes jumpToState={null} prop.
src/layouts/ExamplesLayout.astro Sets examples jump-to state into Astro.locals and removes prop.
src/layouts/ExampleLayout.astro Stores generated jump-to state in Astro.locals and removes prop passing.
src/layouts/EventsLayout.astro Clears Astro.locals.jumpToState and removes jumpToState={null} prop.
src/layouts/EventLayout.astro Clears Astro.locals.jumpToState and removes jumpToState={null} prop.
src/layouts/ContributorDocLayout.astro Sets contributor-doc headings jump-to state into Astro.locals and removes prop.
src/layouts/ContributeLayout.astro Sets contribute page jump-to state into Astro.locals and removes prop.
src/layouts/CommunityLayout.astro Sets community page jump-to state into Astro.locals and removes prop.
src/layouts/BaseLayout.astro Writes jumpToState prop into Astro.locals and renders <Nav /> without props.
src/layouts/AboutLayout.astro Sets about page jump-to state into Astro.locals and removes global setter usage.
src/globals/state.ts Removes global mutable state + setter; keeps only shared types.
src/env.d.ts Adds App.Locals.jumpToState typing for Astro.locals.
src/components/Settings/index.astro Reads jump-to state from Astro.locals instead of global singleton.
src/components/Nav/NavPanels.tsx Adjusts JumpToState type import path.
src/components/Nav/index.astro Reads jump-to state from Astro.locals and passes it to NavPanels.
package-lock.json Lockfile metadata churn included alongside refactor changes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/layouts/BaseLayout.astro Outdated
Comment thread src/components/Nav/index.astro
@riya-saharan

Copy link
Copy Markdown
Author

@lirenjie95 @Nwakaego-Ego — this should be ready for another look now. Conflicts resolved, trailing newlines fixed, PR description updated with the isolation explanation, and I also addressed Copilot's two review comments. Full production build passes (6129 pages, 0 errors). Let me know if anything else is needed!

@riya-saharan

Copy link
Copy Markdown
Author

@lirenjie95 — this should be ready for another look now. Conflicts resolved, trailing newlines fixed, PR description updated with the isolation explanation, and I also addressed Copilot's two review comments. Full production build passes (6129 pages, 0 errors). Would appreciate a re-review when you get a chance!

@riya-saharan
riya-saharan requested review from lirenjie95 and a lite review from Copilot August 15, 2026 12:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@lirenjie95 lirenjie95 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the PR. A few notes:

  • #1391 / #1454 already fixed the same core issue by passing jumpToState as a prop, but that fix is incomplete: AboutLayout, the homepage pages, and education-resources still use the global setJumpToState, and the global variable is still exported from src/globals/state.ts.
  • Your Astro.locals approach is more thorough, but it conflicts with the prop-based pattern already merged in #1391.
  • Please revert the package-lock.json changes — they are unrelated noise.
  • The implementation is inconsistent: BaseLayout still takes a jumpToState prop while other layouts write directly to Astro.locals. Pick one pattern.
  • Setting Astro.locals.jumpToState = null in pages only to have the layout overwrite it is fragile.

Recommendation: close this and open a smaller PR that completes #1391's prop-based fix by converting the remaining setJumpToState calls and removing the global state. If the project prefers Astro.locals, this PR needs significant cleanup first.

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.

5 participants