vault backup: 2026-11-09 09:18:58
This commit is contained in:
1 parent
856d0203da
commit
75072389db
42 files changed
+5994
-37
No files matched your search
@@ -0,0 +1,75 @@
|
||||
// Minimal Chrome DevTools Protocol client (Node 22 global WebSocket, no npm deps).
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
class CDP {
|
||||
constructor(wsUrl) {
|
||||
this.wsUrl = wsUrl;
|
||||
this.id = 0;
|
||||
this.pending = new Map();
|
||||
this.closed = false;
|
||||
}
|
||||
|
||||
connect(timeoutMs = 10000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const ws = new WebSocket(this.wsUrl);
|
||||
this.ws = ws;
|
||||
const timer = setTimeout(() => { try { ws.close(); } catch {} reject(new Error('connect timeout')); }, timeoutMs);
|
||||
ws.onopen = () => { clearTimeout(timer); resolve(this); };
|
||||
ws.onerror = () => { clearTimeout(timer); reject(new Error('ws error')); };
|
||||
ws.onclose = () => {
|
||||
this.closed = true;
|
||||
for (const [, p] of this.pending) p.rej(new Error('ws closed'));
|
||||
this.pending.clear();
|
||||
};
|
||||
ws.onmessage = (ev) => {
|
||||
let m;
|
||||
try { m = JSON.parse(ev.data); } catch { return; }
|
||||
if (m.id && this.pending.has(m.id)) {
|
||||
const p = this.pending.get(m.id);
|
||||
this.pending.delete(m.id);
|
||||
if (m.error) p.rej(new Error(m.error.message || 'cdp error'));
|
||||
else p.res(m.result);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
send(method, params = {}, timeoutMs = 15000) {
|
||||
if (this.closed) return Promise.reject(new Error('ws closed'));
|
||||
const id = ++this.id;
|
||||
return new Promise((res, rej) => {
|
||||
this.pending.set(id, { res, rej });
|
||||
this.ws.send(JSON.stringify({ id, method, params }));
|
||||
setTimeout(() => {
|
||||
if (this.pending.has(id)) { this.pending.delete(id); rej(new Error('cdp timeout: ' + method)); }
|
||||
}, timeoutMs);
|
||||
});
|
||||
}
|
||||
|
||||
async eval(expression, timeoutMs = 15000) {
|
||||
const r = await this.send('Runtime.evaluate', {
|
||||
expression,
|
||||
returnByValue: true,
|
||||
awaitPromise: true,
|
||||
userGesture: true
|
||||
}, timeoutMs);
|
||||
if (r.exceptionDetails) {
|
||||
throw new Error(r.exceptionDetails.exception?.description || r.exceptionDetails.text || 'eval failed');
|
||||
}
|
||||
return r.result ? r.result.value : undefined;
|
||||
}
|
||||
|
||||
close() { try { this.ws.close(); } catch {} this.closed = true; }
|
||||
}
|
||||
|
||||
async function listPages(port, urlMatch) {
|
||||
const res = await fetch(`http://127.0.0.1:${port}/json/list`, { signal: AbortSignal.timeout(5000) });
|
||||
const all = await res.json();
|
||||
return all.filter(t => t.type === 'page' && typeof t.url === 'string'
|
||||
&& t.url.includes(urlMatch) && !t.url.startsWith('devtools://'));
|
||||
}
|
||||
|
||||
const EXPR = fs.readFileSync(path.join(__dirname, 'probe-expression.js'), 'utf8');
|
||||
|
||||
module.exports = { CDP, listPages, EXPR };
|
||||
Reference in new issue
Block a user