Repository navigation
fix(dev): Share one @sentry/junior copy between the app and dashboard - #2363
Merged
Merged
Conversation
Co-Authored-By: Thomas Jiang <20501235+thomasjiangcy@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
thomasjiangcy
approved these changes
Oct 29, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Under
pnpm dev, a dashboard message can stay stuck. The defaultJUNIOR_STATE_ADAPTER=memorymakes this happen. The web input saves the pending message in one memory store. The queue consumer reads a different memory store, finds no work, and returnsno_work. Because of that,conversation.work.lease.acquiredis not logged.Root cause
The app and the dashboard load two copies of the
@sentry/juniormodule in the same process. Each copy has its ownstateAdaptersingleton (packages/junior/src/chat/state/adapter.ts,let stateAdapter).dev-server.mjslinks the injected dashboarddisttopackages/junior-dashboard/dist. Node resolves symlinks to their real path. So the dashboard'simport "@sentry/junior/api"(createJuniorApi) resolves frompackages/junior-dashboard/node_modules/@sentry/junior. That is the injected copy@sentry+junior@file+packages+junior_@types+node@25.9.1_react-dom@19.2.6_react@19.2.6__react@19.2.6_vitest@4.1.12. It is a real copy, not a link.syncInjectedPackageDistlinked only the@sentry/juniorcopies that resolve from the workspace root andapps/example. Those copies runpackages/junior/dist.This change also links the
@sentry/juniorcopy that the dashboard resolves. Then both sides resolve topackages/junior/distand share one module instance.Evidence
I used temporary logs that print an id for each memory state instance,
process.pid,threadId, andimport.meta.url. I also used a temporary in-process queue, because the Vercel queue send needs credentials. The diff does not include either one. I sent the message withPOST /api/conversations.Before (
mainat995d44e):Both instances have the same pid and thread. This rules out a worker thread or a separate process. It also rules out a Nitro restart, because both instances were alive at the same time.
After (this branch, on
9818055): before the run, I replaced the injecteddistlink with a real copy.pnpm devmade the link again (.../_vitest@4.1.12/node_modules/@sentry/junior/dist -> packages/junior/dist).Checks
pnpm devrepro, before and after, as shown aboveprettier --check scripts/dev-server.mjs: passnode --check scripts/dev-server.mjs: passNot verified
watchDistForNitroRestartrestarts Nitro after each dist rebuild, and that still erases memory state. That is a separate cause, and this PR does not change it.via @thomasjiangcy.
--
View Junior Session [Sentry]