document debugger
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"repository": "https://github.com/remorses/playwriter",
|
"repository": "https://github.com/remorses/playwriter",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rm -rf dist *.tsbuildinfo && mkdir dist && cp src/resource.md src/prompt.md dist/ && vite-node scripts/download-selector-generator.ts && tsc",
|
"build": "rm -rf dist *.tsbuildinfo && mkdir dist && cp src/resource.md src/prompt.md src/debugger-examples.ts dist/ && vite-node scripts/download-selector-generator.ts && tsc",
|
||||||
"prepublishOnly": "pnpm build",
|
"prepublishOnly": "pnpm build",
|
||||||
"watch": "tsc -w",
|
"watch": "tsc -w",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import type { Page } from 'playwright-core'
|
||||||
|
import type { CDPSession } from './cdp-session.js'
|
||||||
|
import type { Debugger } from './debugger.js'
|
||||||
|
|
||||||
|
export declare const page: Page
|
||||||
|
export declare const getCDPSession: (options: { page: Page }) => Promise<CDPSession>
|
||||||
|
export declare const createDebugger: (options: { cdp: CDPSession }) => Debugger
|
||||||
|
export declare const console: { log: (...args: unknown[]) => void }
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { page, getCDPSession, createDebugger, console } from './debugger-examples-types.js'
|
||||||
|
|
||||||
|
// Example: List available scripts and set a breakpoint
|
||||||
|
async function listScriptsAndSetBreakpoint() {
|
||||||
|
const cdp = await getCDPSession({ page })
|
||||||
|
const dbg = createDebugger({ cdp })
|
||||||
|
await dbg.enable()
|
||||||
|
|
||||||
|
const scripts = dbg.listScripts({ search: 'app' })
|
||||||
|
console.log(scripts)
|
||||||
|
|
||||||
|
if (scripts.length > 0) {
|
||||||
|
const bpId = await dbg.setBreakpoint({ file: scripts[0].url, line: 100 })
|
||||||
|
console.log('Breakpoint set:', bpId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Inspect state when paused at a breakpoint
|
||||||
|
async function inspectWhenPaused() {
|
||||||
|
const cdp = await getCDPSession({ page })
|
||||||
|
const dbg = createDebugger({ cdp })
|
||||||
|
await dbg.enable()
|
||||||
|
|
||||||
|
if (dbg.isPaused()) {
|
||||||
|
const loc = await dbg.getLocation()
|
||||||
|
console.log('Paused at:', loc.url, 'line', loc.lineNumber)
|
||||||
|
console.log('Source:', loc.sourceContext)
|
||||||
|
|
||||||
|
const vars = await dbg.inspectVariables({ scope: 'local' })
|
||||||
|
console.log('Variables:', vars)
|
||||||
|
|
||||||
|
const result = await dbg.evaluate({ expression: 'myVar.length' })
|
||||||
|
console.log('myVar.length =', result.value)
|
||||||
|
|
||||||
|
await dbg.stepOver()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Step through code
|
||||||
|
async function stepThroughCode() {
|
||||||
|
const cdp = await getCDPSession({ page })
|
||||||
|
const dbg = createDebugger({ cdp })
|
||||||
|
await dbg.enable()
|
||||||
|
|
||||||
|
await dbg.setBreakpoint({ file: 'https://example.com/app.js', line: 42 })
|
||||||
|
|
||||||
|
if (dbg.isPaused()) {
|
||||||
|
await dbg.stepOver()
|
||||||
|
await dbg.stepInto()
|
||||||
|
await dbg.stepOut()
|
||||||
|
await dbg.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Cleanup all breakpoints
|
||||||
|
async function cleanupBreakpoints() {
|
||||||
|
const cdp = await getCDPSession({ page })
|
||||||
|
const dbg = createDebugger({ cdp })
|
||||||
|
|
||||||
|
const breakpoints = dbg.listBreakpoints()
|
||||||
|
for (const bp of breakpoints) {
|
||||||
|
await dbg.deleteBreakpoint({ breakpointId: bp.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { listScriptsAndSetBreakpoint, inspectWhenPaused, stepThroughCode, cleanupBreakpoints }
|
||||||
@@ -15,6 +15,7 @@ import { getCdpUrl, LOG_FILE_PATH, VERSION, sleep } from './utils.js'
|
|||||||
import { killPortProcess } from 'kill-port-process'
|
import { killPortProcess } from 'kill-port-process'
|
||||||
import { waitForPageLoad, WaitForPageLoadOptions, WaitForPageLoadResult } from './wait-for-page-load.js'
|
import { waitForPageLoad, WaitForPageLoadOptions, WaitForPageLoadResult } from './wait-for-page-load.js'
|
||||||
import { getCDPSessionForPage, CDPSession } from './cdp-session.js'
|
import { getCDPSessionForPage, CDPSession } from './cdp-session.js'
|
||||||
|
import { Debugger } from './debugger.js'
|
||||||
|
|
||||||
class CodeExecutionTimeoutError extends Error {
|
class CodeExecutionTimeoutError extends Error {
|
||||||
constructor(timeout: number) {
|
constructor(timeout: number) {
|
||||||
@@ -72,6 +73,7 @@ interface VMContext {
|
|||||||
clearAllLogs: () => void
|
clearAllLogs: () => void
|
||||||
waitForPageLoad: (options: WaitForPageLoadOptions) => Promise<WaitForPageLoadResult>
|
waitForPageLoad: (options: WaitForPageLoadOptions) => Promise<WaitForPageLoadResult>
|
||||||
getCDPSession: (options: { page: Page }) => Promise<CDPSession>
|
getCDPSession: (options: { page: Page }) => Promise<CDPSession>
|
||||||
|
createDebugger: (options: { cdp: CDPSession }) => Debugger
|
||||||
require: NodeRequire
|
require: NodeRequire
|
||||||
import: (specifier: string) => Promise<any>
|
import: (specifier: string) => Promise<any>
|
||||||
}
|
}
|
||||||
@@ -389,6 +391,38 @@ const promptContent =
|
|||||||
fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'prompt.md'), 'utf-8') +
|
fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'prompt.md'), 'utf-8') +
|
||||||
`\n\nfor debugging internal playwriter errors, check playwriter relay server logs at: ${LOG_FILE_PATH}`
|
`\n\nfor debugging internal playwriter errors, check playwriter relay server logs at: ${LOG_FILE_PATH}`
|
||||||
|
|
||||||
|
server.resource('debugger-api', 'playwriter://debugger-api', { mimeType: 'text/plain' }, async () => {
|
||||||
|
const packageJsonPath = require.resolve('playwriter/package.json')
|
||||||
|
const distDir = path.join(path.dirname(packageJsonPath), 'dist')
|
||||||
|
|
||||||
|
const debuggerTypes = fs.readFileSync(path.join(distDir, 'debugger.d.ts'), 'utf-8')
|
||||||
|
const debuggerExamples = fs.readFileSync(path.join(distDir, 'debugger-examples.ts'), 'utf-8')
|
||||||
|
|
||||||
|
return {
|
||||||
|
contents: [
|
||||||
|
{
|
||||||
|
uri: 'playwriter://debugger-api',
|
||||||
|
text: dedent`
|
||||||
|
# Debugger API Reference
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
\`\`\`ts
|
||||||
|
${debuggerTypes}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
\`\`\`ts
|
||||||
|
${debuggerExamples}
|
||||||
|
\`\`\`
|
||||||
|
`,
|
||||||
|
mimeType: 'text/plain',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
server.tool(
|
server.tool(
|
||||||
'execute',
|
'execute',
|
||||||
promptContent,
|
promptContent,
|
||||||
@@ -584,6 +618,10 @@ server.tool(
|
|||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createDebugger = (options: { cdp: CDPSession }) => {
|
||||||
|
return new Debugger(options)
|
||||||
|
}
|
||||||
|
|
||||||
let vmContextObj: VMContextWithGlobals = {
|
let vmContextObj: VMContextWithGlobals = {
|
||||||
page,
|
page,
|
||||||
context,
|
context,
|
||||||
@@ -595,6 +633,7 @@ server.tool(
|
|||||||
clearAllLogs,
|
clearAllLogs,
|
||||||
waitForPageLoad,
|
waitForPageLoad,
|
||||||
getCDPSession,
|
getCDPSession,
|
||||||
|
createDebugger,
|
||||||
resetPlaywright: async () => {
|
resetPlaywright: async () => {
|
||||||
const { page: newPage, context: newContext } = await resetConnection()
|
const { page: newPage, context: newContext } = await resetConnection()
|
||||||
|
|
||||||
@@ -609,6 +648,7 @@ server.tool(
|
|||||||
clearAllLogs,
|
clearAllLogs,
|
||||||
waitForPageLoad,
|
waitForPageLoad,
|
||||||
getCDPSession,
|
getCDPSession,
|
||||||
|
createDebugger,
|
||||||
resetPlaywright: vmContextObj.resetPlaywright,
|
resetPlaywright: vmContextObj.resetPlaywright,
|
||||||
require,
|
require,
|
||||||
// TODO --experimental-vm-modules is needed to make import work in vm
|
// TODO --experimental-vm-modules is needed to make import work in vm
|
||||||
|
|||||||
@@ -95,13 +95,16 @@ you have access to some functions in addition to playwright methods:
|
|||||||
- `page`: the page object to create the session for
|
- `page`: the page object to create the session for
|
||||||
- Returns: `{ send(method, params?), on(event, callback), off(event, callback) }`
|
- Returns: `{ send(method, params?), on(event, callback), off(event, callback) }`
|
||||||
- Example: `const cdp = await getCDPSession({ page }); const metrics = await cdp.send('Page.getLayoutMetrics');`
|
- Example: `const cdp = await getCDPSession({ page }); const metrics = await cdp.send('Page.getLayoutMetrics');`
|
||||||
- Example listening for events:
|
- `createDebugger({ cdp })`: creates a Debugger instance for setting breakpoints, stepping, and inspecting variables. Works with browser JS or Node.js (--inspect).
|
||||||
|
- `cdp`: a CDPSession from `getCDPSession`
|
||||||
|
- Methods: `enable()`, `setBreakpoint({ file, line })`, `deleteBreakpoint({ breakpointId })`, `listBreakpoints()`, `listScripts({ search? })`, `evaluate({ expression })`, `executeCode({ code })`, `inspectVariables({ scope? })`, `getLocation()`, `stepOver()`, `stepInto()`, `stepOut()`, `resume()`, `getConsoleOutput({ limit? })`, `isPaused()`
|
||||||
|
- Example:
|
||||||
```js
|
```js
|
||||||
const cdp = await getCDPSession({ page });
|
const cdp = await getCDPSession({ page }); const dbg = createDebugger({ cdp }); await dbg.enable();
|
||||||
await cdp.send('Debugger.enable');
|
console.log(dbg.listScripts({ search: 'app' }));
|
||||||
const pausedEvent = await new Promise((resolve) => { cdp.on('Debugger.paused', resolve); });
|
await dbg.setBreakpoint({ file: 'https://example.com/app.js', line: 42 });
|
||||||
console.log('Paused at:', pausedEvent.callFrames[0].location);
|
// user triggers the code, then:
|
||||||
await cdp.send('Debugger.resume');
|
if (dbg.isPaused()) { console.log(await dbg.getLocation()); console.log(await dbg.inspectVariables()); await dbg.resume(); }
|
||||||
```
|
```
|
||||||
|
|
||||||
example:
|
example:
|
||||||
|
|||||||
Reference in New Issue
Block a user