diff options
Diffstat (limited to 'src/trace.js')
| -rw-r--r-- | src/trace.js | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/src/trace.js b/src/trace.js new file mode 100644 index 0000000..8717ff1 --- /dev/null +++ b/src/trace.js @@ -0,0 +1,79 @@ +// Daemon diagnostic traces (WA_CLI_TRACE=1 or "trace": true in config.json). +// Logs: CDP/evaluate calls still pending after >15 s, exceptions inside functions exposed +// to the page (puppeteer swallows them), navigations and client events. +// Useful to see where whatsapp-web.js hangs when it never reaches `ready`. + +import { createRequire } from 'node:module'; + +const require = createRequire(import.meta.url); +const QUIET = new Set(['loading_screen', 'message', 'message_create', 'message_ack', 'unread_count', 'media_uploaded']); + +export function enableTrace(log) { + const { CdpPage } = require('puppeteer-core/lib/cjs/puppeteer/cdp/Page.js'); + const { CdpCDPSession } = require('puppeteer-core/lib/cjs/puppeteer/cdp/CdpSession.js'); + const wweb = require('whatsapp-web.js'); + const t = (...a) => log('trace:', ...a); + + const pending = new Map(); + let seq = 0; + const track = (desc, p) => { + const id = ++seq; + pending.set(id, { desc, start: Date.now() }); + const done = () => pending.delete(id); + p.then(done, (e) => { + done(); + t('error', desc, String(e?.message ?? e).slice(0, 200)); + }); + return p; + }; + setInterval(() => { + for (const p of pending.values()) { + const s = Math.round((Date.now() - p.start) / 1000); + if (s >= 15 && s % 15 === 0) t(`pendiente ${s}s:`, p.desc); + } + }, 1000).unref(); + + const send = CdpCDPSession.prototype.send; + CdpCDPSession.prototype.send = function (method, ...args) { + return track(`cdp ${method}`, send.call(this, method, ...args)); + }; + + const expose = CdpPage.prototype.exposeFunction; + CdpPage.prototype.exposeFunction = function (name, fn) { + const wrapped = async (...args) => { + try { + return await fn(...args); + } catch (e) { + t(`exception in ${name}:`, String(e?.stack ?? e).split('\n').slice(0, 4).join(' | ')); + throw e; + } + }; + return track(`exposeFunction(${name})`, expose.call(this, name, wrapped)); + }; + + const emit = wweb.Client.prototype.emit; + wweb.Client.prototype.emit = function (event, ...args) { + if (!QUIET.has(event)) t('evento', event, event === 'disconnected' || event === 'change_state' ? args[0] : ''); + return emit.call(this, event, ...args); + }; + + const init = wweb.Client.prototype.initialize; + wweb.Client.prototype.initialize = function (...args) { + const iv = setInterval(() => { + if (!this.pupPage) return; + clearInterval(iv); + const page = this.pupPage; + page.on('framenavigated', (f) => t('framenavigated', f === page.mainFrame() ? 'main' : 'iframe', f.url().slice(0, 60))); + page.on('load', () => t('load')); + page.on('pageerror', (e) => t('pageerror', String(e?.message ?? e).slice(0, 150))); + }, 20); + return init.apply(this, args); + }; + + const inject = wweb.Client.prototype.inject; + wweb.Client.prototype.inject = function (...args) { + t('inject()'); + return track('inject()', inject.apply(this, args)); + }; + if (inject.waCompat) wweb.Client.prototype.inject.waCompat = true; +} |