Skip to content

Commit 2dab2f1

Browse files
committed
Fix stale pathname after await and eager spread identity check
- __root.tsx: Reintroduce pathnameRef to read the latest pathname after the async bootstrapFromSnapshotRef.current() call. The closure-captured pathname becomes stale across the await boundary, causing unwanted redirects if the user navigates during bootstrap. - terminalStateStore.ts: In clearTerminalState, defer the spread of terminalEventEntriesByKey until after confirming entries actually need removal. The eager spread created a new reference unconditionally, making the === identity check always false and the no-op early return unreachable.
1 parent 6749595 commit 2dab2f1

File tree

2 files changed

+18
-5
lines changed

2 files changed

+18
-5
lines changed

apps/web/src/routes/__root.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ function EventRouter() {
213213
const seenServerConfigUpdateIdRef = useRef(getServerConfigUpdatedNotification()?.id ?? 0);
214214
const disposedRef = useRef(false);
215215
const bootstrapFromSnapshotRef = useRef<() => Promise<void>>(async () => undefined);
216+
const pathnameRef = useRef(pathname);
217+
pathnameRef.current = pathname;
216218
const serverConfig = useServerConfig();
217219

218220
const handleWelcome = useEffectEvent((payload: ServerLifecycleWelcomePayload | null) => {
@@ -230,7 +232,7 @@ function EventRouter() {
230232
}
231233
setProjectExpanded(payload.bootstrapProjectId, true);
232234

233-
if (pathname !== "/") {
235+
if (pathnameRef.current !== "/") {
234236
return;
235237
}
236238
if (handledBootstrapThreadIdRef.current === payload.bootstrapThreadId) {

apps/web/src/terminalStateStore.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -587,18 +587,29 @@ export const useTerminalStateStore = create<TerminalStateStoreState>()(
587587
threadId,
588588
() => createDefaultThreadTerminalState(),
589589
);
590-
const nextTerminalEventEntriesByKey = { ...state.terminalEventEntriesByKey };
591-
for (const key of Object.keys(nextTerminalEventEntriesByKey)) {
590+
let removedEventEntries = false;
591+
for (const key of Object.keys(state.terminalEventEntriesByKey)) {
592592
if (key.startsWith(`${threadId}\u0000`)) {
593-
delete nextTerminalEventEntriesByKey[key];
593+
removedEventEntries = true;
594+
break;
594595
}
595596
}
596597
if (
597598
nextTerminalStateByThreadId === state.terminalStateByThreadId &&
598-
nextTerminalEventEntriesByKey === state.terminalEventEntriesByKey
599+
!removedEventEntries
599600
) {
600601
return state;
601602
}
603+
const nextTerminalEventEntriesByKey = removedEventEntries
604+
? { ...state.terminalEventEntriesByKey }
605+
: state.terminalEventEntriesByKey;
606+
if (removedEventEntries) {
607+
for (const key of Object.keys(nextTerminalEventEntriesByKey)) {
608+
if (key.startsWith(`${threadId}\u0000`)) {
609+
delete nextTerminalEventEntriesByKey[key];
610+
}
611+
}
612+
}
602613
return {
603614
terminalStateByThreadId: nextTerminalStateByThreadId,
604615
terminalEventEntriesByKey: nextTerminalEventEntriesByKey,

0 commit comments

Comments
 (0)