fix: route CDP clients through live extension bindings

When an extension reconnects, clients can be rebound to a successor connection without reopening the Playwright socket. Command routing was still resolving via the extension id captured at client socket open, so rebound clients could hit stale routes and return Extension not connected.

Resolve the bound extension id from relay state on every incoming Playwright message, and prefer the newest same-stableKey successor when rebinding on extension close. This keeps reconnect handoff consistent between state transitions and runtime routing.
This commit is contained in:
Tommy D. Rossi
2026-02-27 09:01:53 +01:00
parent 44212ceda8
commit 6807209e85
+18 -7
View File
@@ -932,6 +932,11 @@ export async function startPlayWriterCDPRelayServer({
: getExtensionConnection(null, { allowFallback: true })
const clientExtensionId = resolvedExtension?.id || null
const getBoundExtensionIdForClient = (): string | null => {
const client = store.getState().playwrightClients.get(clientId)
return client?.extensionId || null
}
return {
async onOpen(_event, ws) {
if (store.getState().playwrightClients.has(clientId)) {
@@ -990,7 +995,8 @@ export async function startPlayWriterCDPRelayServer({
emitter.emit('cdp:command', { clientId, command: message })
const extensionConn = getExtensionConnection(clientExtensionId)
const boundExtensionId = getBoundExtensionIdForClient()
const extensionConn = getExtensionConnection(boundExtensionId)
if (!extensionConn) {
sendToPlaywright({
message: {
@@ -1091,9 +1097,9 @@ export async function startPlayWriterCDPRelayServer({
if (attachResponse?.sessionId) {
const freshExt3 = store.getState().extensions.get(extensionConn.id)
const freshTargets3 = freshExt3?.connectedTargets || new Map()
const target = Array.from(freshTargets3.values()).find(
(t) => t.targetId === attachRequestParams?.targetId,
)
const target = Array.from(freshTargets3.values()).find((t) => {
return t.targetId === attachRequestParams?.targetId
})
if (target) {
const attachedPayload = {
method: 'Target.attachedToTarget',
@@ -1572,10 +1578,15 @@ export async function startPlayWriterCDPRelayServer({
const currentRelayState = store.getState()
const closingExtension = currentRelayState.extensions.get(connectionId)
const successorExtension = closingExtension
? Array.from(currentRelayState.extensions.values()).find((ext) => {
return ext.id !== connectionId && ext.stableKey === closingExtension.stableKey
const successorCandidates = closingExtension
? Array.from(currentRelayState.extensions.values())
.reverse()
.filter((ext) => {
return ext.id !== connectionId && ext.stableKey === closingExtension.stableKey && Boolean(ext.ws)
})
: []
const successorExtension = closingExtension
? successorCandidates[0]
: undefined
if (successorExtension) {