fix: clear removed web session tab tracking (#4099)
This commit is contained in:
parent
30b93c6bf7
commit
4615f2e169
|
|
@ -6,6 +6,7 @@ import { makePaneKey } from '../../../shared/stable-pane-id'
|
|||
import type { BrowserPage, BrowserWorkspace, Tab, TerminalTab } from '../../../shared/types'
|
||||
import type { OpenFile } from '../store/slices/editor'
|
||||
import {
|
||||
_getWebSessionTabsTrackingCountsForTest,
|
||||
applyFreshWebSessionTabsSnapshot,
|
||||
applyWebSessionTabsSnapshot,
|
||||
applyWebSessionTabsSnapshots,
|
||||
|
|
@ -92,6 +93,121 @@ describe('applyWebSessionTabsSnapshot', () => {
|
|||
expect(applyFreshWebSessionTabsSnapshot(afterNewer, newer, ENV, NOW)).toBe(afterNewer)
|
||||
})
|
||||
|
||||
it('clears web session tracking maps when the host removes a worktree snapshot', () => {
|
||||
const workspace: BrowserWorkspace = {
|
||||
id: 'local-browser-workspace',
|
||||
worktreeId: WT,
|
||||
activePageId: 'local-browser-page',
|
||||
pageIds: ['local-browser-page'],
|
||||
url: 'https://example.com/',
|
||||
title: 'Example Domain',
|
||||
loading: false,
|
||||
faviconUrl: null,
|
||||
canGoBack: false,
|
||||
canGoForward: false,
|
||||
loadError: null,
|
||||
createdAt: NOW - 10
|
||||
}
|
||||
const page: BrowserPage = {
|
||||
id: 'local-browser-page',
|
||||
workspaceId: workspace.id,
|
||||
worktreeId: WT,
|
||||
url: 'https://example.com/',
|
||||
title: 'Example Domain',
|
||||
loading: false,
|
||||
faviconUrl: null,
|
||||
canGoBack: false,
|
||||
canGoForward: false,
|
||||
loadError: null,
|
||||
createdAt: NOW - 10
|
||||
}
|
||||
const unifiedTab: Tab = {
|
||||
id: 'local-browser-unified',
|
||||
entityId: workspace.id,
|
||||
groupId: 'host-group-1',
|
||||
worktreeId: WT,
|
||||
contentType: 'browser',
|
||||
label: 'New Tab',
|
||||
customLabel: null,
|
||||
color: null,
|
||||
sortOrder: 0,
|
||||
createdAt: NOW - 10,
|
||||
isPreview: false,
|
||||
isPinned: false
|
||||
}
|
||||
|
||||
const patch = applyFreshWebSessionTabsSnapshot(
|
||||
makeState({
|
||||
browserTabsByWorktree: { [WT]: [workspace] },
|
||||
browserPagesByWorkspace: { [workspace.id]: [page] },
|
||||
remoteBrowserPageHandlesByPageId: {
|
||||
[page.id]: { environmentId: ENV, remotePageId: 'host-browser-page' }
|
||||
},
|
||||
unifiedTabsByWorktree: { [WT]: [unifiedTab] },
|
||||
groupsByWorktree: {
|
||||
[WT]: [
|
||||
{
|
||||
id: 'host-group-1',
|
||||
worktreeId: WT,
|
||||
activeTabId: unifiedTab.id,
|
||||
tabOrder: [unifiedTab.id],
|
||||
recentTabIds: [unifiedTab.id]
|
||||
}
|
||||
]
|
||||
}
|
||||
}),
|
||||
makeSnapshot(
|
||||
[
|
||||
{
|
||||
type: 'browser',
|
||||
id: 'host-browser-unified',
|
||||
title: 'Example Domain',
|
||||
browserWorkspaceId: 'host-browser-workspace',
|
||||
browserPageId: 'host-browser-page',
|
||||
url: 'https://example.com/',
|
||||
loading: false,
|
||||
canGoBack: false,
|
||||
canGoForward: false,
|
||||
isActive: true
|
||||
}
|
||||
],
|
||||
{ activeTabId: 'host-browser-unified', activeTabType: 'browser' }
|
||||
),
|
||||
ENV,
|
||||
NOW
|
||||
) as Partial<WebSessionTabsSyncState>
|
||||
const afterHostSnapshot = {
|
||||
...makeState(),
|
||||
...patch
|
||||
} as WebSessionTabsSyncState
|
||||
|
||||
expect(_getWebSessionTabsTrackingCountsForTest()).toEqual({
|
||||
freshness: 1,
|
||||
hostMappings: 1
|
||||
})
|
||||
|
||||
applyFreshWebSessionTabsSnapshot(
|
||||
afterHostSnapshot,
|
||||
{
|
||||
...makeSnapshot([], {
|
||||
publicationEpoch: 'removed-epoch',
|
||||
snapshotVersion: 0,
|
||||
activeGroupId: null,
|
||||
activeTabId: null,
|
||||
activeTabType: null
|
||||
}),
|
||||
removed: true
|
||||
} as RuntimeMobileSessionTabsResult,
|
||||
ENV,
|
||||
NOW + 1
|
||||
)
|
||||
|
||||
expect(_getWebSessionTabsTrackingCountsForTest()).toEqual({
|
||||
freshness: 0,
|
||||
hostMappings: 0
|
||||
})
|
||||
})
|
||||
|
||||
it('hydrates ready host terminal surfaces as remote runtime terminal tabs', () => {
|
||||
const patch = applyWebSessionTabsSnapshot(
|
||||
makeState(),
|
||||
|
|
|
|||
|
|
@ -134,6 +134,13 @@ export function shouldApplyWebSessionTabsSnapshot(
|
|||
environmentId: string
|
||||
): boolean {
|
||||
const key = sessionTabsFreshnessKey(environmentId, snapshot.worktree)
|
||||
if ((snapshot as { removed?: unknown }).removed === true) {
|
||||
// Why: removed worktrees can stop publishing snapshots, so their
|
||||
// freshness/mapping entries need explicit cleanup instead of waiting for
|
||||
// a later replacement snapshot that may never arrive.
|
||||
clearWebSessionTabsTrackingForWorktree(environmentId, snapshot.worktree)
|
||||
return true
|
||||
}
|
||||
const current = latestSessionTabsSnapshotByWorktree.get(key)
|
||||
if (
|
||||
current &&
|
||||
|
|
@ -154,6 +161,26 @@ export function resetWebSessionTabsSnapshotFreshnessForTests(): void {
|
|||
hostSessionTabIdByLocalKey.clear()
|
||||
}
|
||||
|
||||
export function _getWebSessionTabsTrackingCountsForTest(): {
|
||||
freshness: number
|
||||
hostMappings: number
|
||||
} {
|
||||
return {
|
||||
freshness: latestSessionTabsSnapshotByWorktree.size,
|
||||
hostMappings: hostSessionTabIdByLocalKey.size
|
||||
}
|
||||
}
|
||||
|
||||
function clearWebSessionTabsTrackingForWorktree(environmentId: string, worktreeId: string): void {
|
||||
latestSessionTabsSnapshotByWorktree.delete(sessionTabsFreshnessKey(environmentId, worktreeId))
|
||||
const keyPrefix = `${environmentId}:${worktreeId}:`
|
||||
for (const key of hostSessionTabIdByLocalKey.keys()) {
|
||||
if (key.startsWith(keyPrefix)) {
|
||||
hostSessionTabIdByLocalKey.delete(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hostSessionTabMappingKey(args: {
|
||||
environmentId: string
|
||||
worktreeId: string
|
||||
|
|
|
|||
Loading…
Reference in New Issue