Fix Editor/Debugger script listing after page load

This commit is contained in:
Tommy D. Rossi
2025-12-29 16:46:34 +01:00
parent 5e0cd2dd98
commit 169ba48a77
7 changed files with 72 additions and 27 deletions
+9
View File
@@ -1,5 +1,14 @@
# Changelog
## 0.0.29
### Patch Changes
- **Fixed Editor/Debugger script listing after page load**: `listScripts()` and `list()` now work correctly even when called after page has loaded
- `enable()` now disables first then re-enables to force CDP to emit `scriptParsed` and `styleSheetAdded` events
- Added 100ms debounced wait for events to arrive before returning
- `listScripts()` and `list()` are now async and auto-call `enable()`
## 0.0.28
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "playwriter",
"description": "",
"version": "0.0.28",
"version": "0.0.29",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
+1 -1
View File
@@ -6,7 +6,7 @@ async function listScriptsAndSetBreakpoint() {
const dbg = createDebugger({ cdp })
await dbg.enable()
const scripts = dbg.listScripts({ search: 'app' })
const scripts = await dbg.listScripts({ search: 'app' })
console.log(scripts)
if (scripts.length > 0) {
+24 -5
View File
@@ -108,9 +108,28 @@ export class Debugger {
if (this.debuggerEnabled) {
return
}
await this.cdp.send('Debugger.disable')
await this.cdp.send('Runtime.disable')
this.scripts.clear()
const scriptsReady = new Promise<void>((resolve) => {
let timeout: ReturnType<typeof setTimeout>
const listener = () => {
clearTimeout(timeout)
timeout = setTimeout(() => {
this.cdp.off('Debugger.scriptParsed', listener)
resolve()
}, 100)
}
this.cdp.on('Debugger.scriptParsed', listener)
timeout = setTimeout(() => {
this.cdp.off('Debugger.scriptParsed', listener)
resolve()
}, 100)
})
await this.cdp.send('Debugger.enable')
await this.cdp.send('Runtime.enable')
await this.cdp.send('Runtime.runIfWaitingForDebugger')
await scriptsReady
this.debuggerEnabled = true
}
@@ -493,8 +512,7 @@ export class Debugger {
/**
* Lists available scripts where breakpoints can be set.
* Scripts are collected from Debugger.scriptParsed events after enable() is called.
* Reload the page after enabling to capture all scripts.
* Automatically enables the debugger if not already enabled.
*
* @param options - Options
* @param options.search - Optional string to filter scripts by URL (case-insensitive)
@@ -503,15 +521,16 @@ export class Debugger {
* @example
* ```ts
* // List all scripts
* const scripts = dbg.listScripts()
* const scripts = await dbg.listScripts()
* // [{ scriptId: '1', url: 'https://example.com/app.js' }, ...]
*
* // Search for specific files
* const handlers = dbg.listScripts({ search: 'handler' })
* const handlers = await dbg.listScripts({ search: 'handler' })
* // [{ scriptId: '5', url: 'https://example.com/handlers.js' }]
* ```
*/
listScripts({ search }: { search?: string } = {}): ScriptInfo[] {
async listScripts({ search }: { search?: string } = {}): Promise<ScriptInfo[]> {
await this.enable()
const scripts = Array.from(this.scripts.values())
const filtered = search
? scripts.filter((s) => s.url.toLowerCase().includes(search.toLowerCase()))
+1 -1
View File
@@ -107,7 +107,7 @@ async function readStylesheet() {
const editor = createEditor({ cdp })
await editor.enable()
const stylesheets = editor.list({ pattern: /\.css/ })
const stylesheets = await editor.list({ pattern: /\.css/ })
console.log('Stylesheets:', stylesheets)
if (stylesheets.length > 0) {
+32 -6
View File
@@ -86,9 +86,33 @@ export class Editor {
if (this.enabled) {
return
}
await this.cdp.send('Debugger.disable')
await this.cdp.send('CSS.disable')
this.scripts.clear()
this.stylesheets.clear()
this.sourceCache.clear()
const resourcesReady = new Promise<void>((resolve) => {
let timeout: ReturnType<typeof setTimeout>
const listener = () => {
clearTimeout(timeout)
timeout = setTimeout(() => {
this.cdp.off('Debugger.scriptParsed', listener)
this.cdp.off('CSS.styleSheetAdded', listener)
resolve()
}, 100)
}
this.cdp.on('Debugger.scriptParsed', listener)
this.cdp.on('CSS.styleSheetAdded', listener)
timeout = setTimeout(() => {
this.cdp.off('Debugger.scriptParsed', listener)
this.cdp.off('CSS.styleSheetAdded', listener)
resolve()
}, 100)
})
await this.cdp.send('Debugger.enable')
await this.cdp.send('DOM.enable')
await this.cdp.send('CSS.enable')
await resourcesReady
this.enabled = true
}
@@ -108,6 +132,7 @@ export class Editor {
/**
* Lists available script and stylesheet URLs. Use pattern to filter by regex.
* Automatically enables the editor if not already enabled.
*
* @param options - Options
* @param options.pattern - Optional regex to filter URLs
@@ -116,19 +141,20 @@ export class Editor {
* @example
* ```ts
* // List all scripts and stylesheets
* const urls = editor.list()
* const urls = await editor.list()
*
* // List only JS files
* const jsFiles = editor.list({ pattern: /\.js/ })
* const jsFiles = await editor.list({ pattern: /\.js/ })
*
* // List only CSS files
* const cssFiles = editor.list({ pattern: /\.css/ })
* const cssFiles = await editor.list({ pattern: /\.css/ })
*
* // Search for specific scripts
* const appScripts = editor.list({ pattern: /app/ })
* const appScripts = await editor.list({ pattern: /app/ })
* ```
*/
list({ pattern }: { pattern?: RegExp } = {}): string[] {
async list({ pattern }: { pattern?: RegExp } = {}): Promise<string[]> {
await this.enable()
const urls = [...Array.from(this.scripts.keys()), ...Array.from(this.stylesheets.keys())]
if (!pattern) {
@@ -319,7 +345,7 @@ export class Editor {
await this.enable()
const matches: SearchMatch[] = []
const urls = this.list({ pattern })
const urls = await this.list({ pattern })
for (const url of urls) {
let source: string
+4 -13
View File
@@ -2094,19 +2094,12 @@ describe('CDP Session Tests', () => {
const cdpSession = await getCDPSessionForPage({ page: cdpPage!, wsUrl })
const dbg = new Debugger({ cdp: cdpSession })
await dbg.enable()
for (let i = 0; i < 20; i++) {
if (dbg.listScripts().length > 0) break
await new Promise(r => setTimeout(r, 100))
}
const scripts = dbg.listScripts()
const scripts = await dbg.listScripts()
expect(scripts.length).toBeGreaterThan(0)
expect(scripts[0]).toHaveProperty('scriptId')
expect(scripts[0]).toHaveProperty('url')
const dataScripts = dbg.listScripts({ search: 'data:' })
const dataScripts = await dbg.listScripts({ search: 'data:' })
expect(dataScripts.length).toBeGreaterThan(0)
cdpSession.close()
@@ -2698,8 +2691,7 @@ describe('CDP Session Tests', () => {
`,
})
await new Promise(r => setTimeout(r, 100))
const scripts = editor.list()
const scripts = await editor.list()
expect(scripts.length).toBeGreaterThan(0)
const matches = await editor.grep({ regex: /greetUser/ })
@@ -2766,8 +2758,7 @@ describe('CDP Session Tests', () => {
`,
})
await new Promise(r => setTimeout(r, 100))
const stylesheets = editor.list({ pattern: /inline-css:/ })
const stylesheets = await editor.list({ pattern: /inline-css:/ })
expect(stylesheets.length).toBeGreaterThan(0)
const cssMatches = await editor.grep({ regex: /editor-test-element/, pattern: /inline-css:/ })