remove files
This commit is contained in:
@@ -1,242 +0,0 @@
|
|||||||
# Old vs New Implementation Comparison
|
|
||||||
|
|
||||||
## Old Implementation Issues
|
|
||||||
|
|
||||||
### Class-Based Complexity
|
|
||||||
|
|
||||||
**Old (`cdpRelay.ts`):**
|
|
||||||
```typescript
|
|
||||||
export class CDPRelayServer {
|
|
||||||
private _wsHost: string
|
|
||||||
private _cdpPath: string
|
|
||||||
private _extensionPath: string
|
|
||||||
private _wss: WebSocketServer
|
|
||||||
private _playwrightConnection: WebSocket | null = null
|
|
||||||
private _extensionConnection: ExtensionConnection | null = null
|
|
||||||
private _connectedTargets: Map<string, ConnectedTarget> = new Map()
|
|
||||||
private _extensionConnectionPromise!: ManualPromise<void>
|
|
||||||
|
|
||||||
constructor(server: http.Server) {
|
|
||||||
// Initialize all state
|
|
||||||
this._wss = new wsServer({ server })
|
|
||||||
this._wss.on('connection', (ws, request) => {
|
|
||||||
// Complex nested logic
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handlePlaywrightConnection(ws: WebSocket): void { }
|
|
||||||
private _handleExtensionMessage(message: ExtensionEventMessage) { }
|
|
||||||
private async _handlePlaywrightMessage(message: CDPCommand): Promise<void> { }
|
|
||||||
private _sendToPlaywright(message: CDPResponse | CDPEvent): void { }
|
|
||||||
// ... many more private methods
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExtensionConnection {
|
|
||||||
// Nested class with its own state
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Issues:**
|
|
||||||
- State scattered across private fields
|
|
||||||
- Methods mutate class state (`this._playwrightConnection = ws`)
|
|
||||||
- Nested classes make it hard to follow data flow
|
|
||||||
- Manual promise management (`ManualPromise`)
|
|
||||||
|
|
||||||
### New (Functional):**
|
|
||||||
```typescript
|
|
||||||
export async function startRelayServer({ port = 19988 }) {
|
|
||||||
const targetsRegistry = createTargetsRegistry()
|
|
||||||
let playwrightWs: WSContext | null = null
|
|
||||||
let extensionWs: WSContext | null = null
|
|
||||||
let extensionConnection: ExtensionConnection | null = null
|
|
||||||
|
|
||||||
// All state is local variables
|
|
||||||
// All logic is inline or in pure functions
|
|
||||||
|
|
||||||
return {
|
|
||||||
cdpEndpoint,
|
|
||||||
extensionEndpoint,
|
|
||||||
close() { /* cleanup */ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits:**
|
|
||||||
- All state in one place (function scope)
|
|
||||||
- Clear lifecycle (create → use → cleanup)
|
|
||||||
- No `this`, easier to reason about
|
|
||||||
- Returns cleanup function
|
|
||||||
|
|
||||||
## Message Handling
|
|
||||||
|
|
||||||
### Old:
|
|
||||||
```typescript
|
|
||||||
// Spread across multiple private methods
|
|
||||||
private async _handlePlaywrightMessage(message: CDPCommand): Promise<void> {
|
|
||||||
const forwardToExtension = async (...) => { }
|
|
||||||
const handleCDPCommand = async (...) => {
|
|
||||||
switch (method) {
|
|
||||||
case 'Browser.getVersion': { }
|
|
||||||
case 'Target.setAutoAttach': { }
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const result = await handleCDPCommand(...)
|
|
||||||
this._sendToPlaywright({ id, sessionId, result })
|
|
||||||
} catch (e) {
|
|
||||||
this._sendToPlaywright({ error })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### New:
|
|
||||||
```typescript
|
|
||||||
// CDP routing extracted to dedicated module
|
|
||||||
const router = createCdpRouter({ targetsRegistry, extensionConnection })
|
|
||||||
const result: any = await router.route({ method, params, sessionId })
|
|
||||||
sendToPlaywright({ id, sessionId, result })
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits:**
|
|
||||||
- Separation of concerns
|
|
||||||
- Router is testable in isolation
|
|
||||||
- Clear dependencies (passed as args)
|
|
||||||
|
|
||||||
## WebSocket Setup
|
|
||||||
|
|
||||||
### Old (Manual ws library):
|
|
||||||
```typescript
|
|
||||||
this._wss = new wsServer({ server })
|
|
||||||
this._wss.on('connection', (ws: WebSocket, request: http.IncomingMessage) => {
|
|
||||||
const url = new URL(`http://localhost${request.url}`)
|
|
||||||
if (url.pathname === this._cdpPath) {
|
|
||||||
this._handlePlaywrightConnection(ws)
|
|
||||||
} else if (url.pathname === this._extensionPath) {
|
|
||||||
this._extensionConnection = new ExtensionConnection(ws)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### New (Hono WebSocket):
|
|
||||||
```typescript
|
|
||||||
const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app })
|
|
||||||
|
|
||||||
app.get('/cdp', upgradeWebSocket(() => ({
|
|
||||||
onOpen(_event, ws) { },
|
|
||||||
onMessage(event, ws) { },
|
|
||||||
onClose() { },
|
|
||||||
onError(event) { }
|
|
||||||
})))
|
|
||||||
|
|
||||||
app.get('/extension', upgradeWebSocket(() => ({
|
|
||||||
onOpen(_event, ws) { },
|
|
||||||
onMessage(event, ws) { },
|
|
||||||
onClose() { },
|
|
||||||
onError(event) { }
|
|
||||||
})))
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits:**
|
|
||||||
- Declarative routing
|
|
||||||
- Built-in event handlers
|
|
||||||
- Framework handles upgrade protocol
|
|
||||||
- Cleaner separation between endpoints
|
|
||||||
|
|
||||||
## Targets Registry
|
|
||||||
|
|
||||||
### Old (Private Map):
|
|
||||||
```typescript
|
|
||||||
class CDPRelayServer {
|
|
||||||
private _connectedTargets: Map<string, ConnectedTarget> = new Map()
|
|
||||||
|
|
||||||
// Scattered operations
|
|
||||||
this._connectedTargets.set(targetParams.sessionId, { ... })
|
|
||||||
this._connectedTargets.delete(detachParams.sessionId)
|
|
||||||
for (const target of this._connectedTargets.values()) { }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### New (Functional Registry):
|
|
||||||
```typescript
|
|
||||||
function createTargetsRegistry() {
|
|
||||||
const targets = new Map<string, ConnectedTarget>()
|
|
||||||
return {
|
|
||||||
add({ sessionId, targetId, targetInfo }) { },
|
|
||||||
remove(sessionId) { },
|
|
||||||
get(sessionId) { },
|
|
||||||
findByTargetId(targetId) { },
|
|
||||||
getAll() { },
|
|
||||||
clear() { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetsRegistry = createTargetsRegistry()
|
|
||||||
targetsRegistry.add({ ... })
|
|
||||||
targetsRegistry.remove(sessionId)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits:**
|
|
||||||
- Encapsulated state
|
|
||||||
- Clear API
|
|
||||||
- Easy to test
|
|
||||||
- Can be reused
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
### Old:
|
|
||||||
```typescript
|
|
||||||
debugLogger(`New connection to ${url.pathname}`)
|
|
||||||
debugLogger('Rejecting second Playwright connection')
|
|
||||||
debugLogger('Playwright MCP connected')
|
|
||||||
debugLogger('Extension WebSocket closed:', reason, c === this._extensionConnection)
|
|
||||||
debugLogger(`← Playwright: ${message.method} (id=${message.id})`)
|
|
||||||
debugLogger('\x1b[36m← Playwright:\x1b[0m', `${message.method} (id=${message.id})`)
|
|
||||||
debugLogger('\x1b[33m← Extension:\x1b[0m', `Target.attachedToTarget ...`)
|
|
||||||
debugLogger('\x1b[32m→ Playwright:\x1b[0m', logMessage)
|
|
||||||
debugLogger('\x1b[31mError in the extension:\x1b[0m', e)
|
|
||||||
```
|
|
||||||
|
|
||||||
### New:
|
|
||||||
```typescript
|
|
||||||
console.log('Playwright connected')
|
|
||||||
console.log('Extension connected')
|
|
||||||
console.log('← Playwright:', `${message.method} (id=${message.id})`)
|
|
||||||
console.log('← Extension:', method)
|
|
||||||
console.error('Error handling CDP command:', e)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits:**
|
|
||||||
- Simple console.log
|
|
||||||
- Only important events
|
|
||||||
- No color codes
|
|
||||||
- No verbose state dumps
|
|
||||||
|
|
||||||
## Code Metrics
|
|
||||||
|
|
||||||
| Metric | Old | New |
|
|
||||||
|--------|-----|-----|
|
|
||||||
| Files | 4 | 2 (relay-server.ts + protocol.ts) |
|
|
||||||
| Main class LOC | ~417 | 321 (everything inlined) |
|
|
||||||
| Total LOC | ~600 | ~370 |
|
|
||||||
| Classes | 2 | 0 |
|
|
||||||
| Private methods | 10+ | 0 |
|
|
||||||
| Helper modules | 0 | 0 (all inlined) |
|
|
||||||
| Dependencies | playwright-core (ws) | hono, @hono/node-ws |
|
|
||||||
|
|
||||||
## Migration Path
|
|
||||||
|
|
||||||
The new implementation is a drop-in replacement:
|
|
||||||
|
|
||||||
**Old:**
|
|
||||||
```typescript
|
|
||||||
const httpServer = await startHttpServer({ port: 19988 })
|
|
||||||
const cdpRelayServer = new CDPRelayServer(httpServer)
|
|
||||||
cdpRelayServer.stop()
|
|
||||||
```
|
|
||||||
|
|
||||||
**New:**
|
|
||||||
```typescript
|
|
||||||
const server = await startRelayServer({ port: 19988 })
|
|
||||||
server.close()
|
|
||||||
```
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
# Migration Complete
|
|
||||||
|
|
||||||
The new CDP relay server implementation has been moved into `playwriter/src/extension/`.
|
|
||||||
|
|
||||||
## What Changed
|
|
||||||
|
|
||||||
### Files Removed
|
|
||||||
- ❌ `src/extension/cdpRelay.ts` (~417 lines) - Old class-based implementation
|
|
||||||
- ❌ `src/extension/types.ts` - Old type definitions
|
|
||||||
|
|
||||||
### Files Added/Updated
|
|
||||||
- ✅ `src/extension/relay-server.ts` (321 lines) - New inline implementation
|
|
||||||
- ✅ `src/extension/protocol.ts` (34 lines) - Message type definitions
|
|
||||||
- ✅ `src/extension/extensionContextFactory.ts` - Now just re-exports `startRelayServer`
|
|
||||||
|
|
||||||
### Updated Scripts
|
|
||||||
- ✅ `scripts/extension-server.ts` - Uses new simplified API
|
|
||||||
|
|
||||||
## New API
|
|
||||||
|
|
||||||
**Before:**
|
|
||||||
```typescript
|
|
||||||
const controller = new AbortController()
|
|
||||||
const { cdpRelayServer } = await startRelayServer(controller.signal)
|
|
||||||
// Complex class-based API
|
|
||||||
cdpRelayServer.stop()
|
|
||||||
```
|
|
||||||
|
|
||||||
**After:**
|
|
||||||
```typescript
|
|
||||||
const server = await startRelayServer({ port: 19988 })
|
|
||||||
// Simple object API
|
|
||||||
server.close()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Reduction
|
|
||||||
|
|
||||||
| Metric | Before | After |
|
|
||||||
|--------|--------|-------|
|
|
||||||
| Total LOC | ~600 | ~370 |
|
|
||||||
| Files | 4 | 3 |
|
|
||||||
| Classes | 2 | 0 |
|
|
||||||
| Helper functions | Multiple modules | All inlined |
|
|
||||||
|
|
||||||
## Dependencies Added
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hono": "^4.10.6",
|
|
||||||
"@hono/node-server": "^1.19.6",
|
|
||||||
"@hono/node-ws": "^1.2.0"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd playwriter
|
|
||||||
pnpm typecheck # ✅ Passes
|
|
||||||
pnpm build # ✅ Builds successfully
|
|
||||||
|
|
||||||
# Run the server
|
|
||||||
vite-node scripts/extension-server.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
Everything is now in **one file** (`relay-server.ts`):
|
|
||||||
- Target registry (simple Map)
|
|
||||||
- Extension request/response handling
|
|
||||||
- CDP command routing
|
|
||||||
- WebSocket endpoints
|
|
||||||
- Message forwarding
|
|
||||||
|
|
||||||
No abstractions, no helper modules, just straightforward code.
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
# Extension New - Rewritten CDP Relay Server
|
|
||||||
|
|
||||||
Clean, simple rewrite of the CDP relay server using Hono WebSocket.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The relay server acts as a bridge between:
|
|
||||||
- **Playwright** (connects via `/cdp`) - Controls the browser
|
|
||||||
- **Chrome Extension** (connects via `/extension`) - Provides chrome.debugger access
|
|
||||||
|
|
||||||
### Message Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
Playwright → /cdp → Router → /extension → Extension → chrome.debugger → Tab
|
|
||||||
Tab → chrome.debugger → Extension → /extension → /cdp → Playwright
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
**Everything inlined in one file** - No abstraction layers, just straightforward code.
|
|
||||||
|
|
||||||
### Files
|
|
||||||
|
|
||||||
**`relay-server.ts`** - Single file with everything:
|
|
||||||
- Two WebSocket endpoints (`/cdp` and `/extension`)
|
|
||||||
- Connection lifecycle management
|
|
||||||
- CDP command routing (Browser.*, Target.*, etc)
|
|
||||||
- Extension request/response handling
|
|
||||||
- Target registry (simple Map)
|
|
||||||
- All message forwarding logic
|
|
||||||
|
|
||||||
**`protocol.ts`** - Message type definitions
|
|
||||||
|
|
||||||
**`index.ts`** - Exports
|
|
||||||
**`example.ts`** - Usage example
|
|
||||||
|
|
||||||
## Key Improvements
|
|
||||||
|
|
||||||
**Simpler Code:**
|
|
||||||
- Everything in one place
|
|
||||||
- No helper functions or modules
|
|
||||||
- Clear linear flow
|
|
||||||
- All state as local variables
|
|
||||||
|
|
||||||
**Better Logging:**
|
|
||||||
- Only logs connection events, CDP messages, and errors
|
|
||||||
- No verbose debug noise
|
|
||||||
|
|
||||||
**Clean API:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const server = await startRelayServer({ port: 19988 })
|
|
||||||
|
|
||||||
// Returns:
|
|
||||||
// {
|
|
||||||
// cdpEndpoint: 'ws://localhost:19988/cdp',
|
|
||||||
// extensionEndpoint: 'ws://localhost:19988/extension',
|
|
||||||
// close: () => void
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Cleanup:
|
|
||||||
server.close()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { startRelayServer } from './relay-server.js'
|
|
||||||
|
|
||||||
const server = await startRelayServer({ port: 19988 })
|
|
||||||
|
|
||||||
console.log('Extension endpoint:', server.extensionEndpoint)
|
|
||||||
console.log('CDP endpoint:', server.cdpEndpoint)
|
|
||||||
|
|
||||||
// Later:
|
|
||||||
server.close()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running Example
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm install
|
|
||||||
pnpm build
|
|
||||||
node dist/example.js
|
|
||||||
```
|
|
||||||
Reference in New Issue
Block a user