import fs from 'node:fs'; import { JSDOM, ResourceLoader } from 'jsdom'; import { patchEnvironment } from './scripts/jsdom-patch-env.mjs'; const ROOT = '/Users/yml/codes/douyu_login_py'; const taskId = process.argv[2] || '142265ef3d874ce6'; const out = `${ROOT}/data/yyb-worker-dev/${taskId}/jsdom-order`; const goodsUrl = JSON.parse(fs.readFileSync(`${out}/device-fp.json`, 'utf8')).goods_url; const pageInfoPath = process.argv[3] || `${out}/web-page-info-response.json`; const pageInfo = fs.existsSync(pageInfoPath) ? fs.readFileSync(pageInfoPath, 'utf8') : '{"ret":0,"msg":"","info":{}}'; const captured = []; const requests = []; const pageErrors = []; const dom = new JSDOM(fs.readFileSync(`${out}/goods.html`, 'utf8'), { url: goodsUrl, referrer: 'https://z.iwan.yyb.qq.com/', pretendToBeVisual: true, runScripts: 'dangerously', resources: new ResourceLoader(), beforeParse(window) { patchEnvironment(window); const NativeXHR = window.XMLHttpRequest; window.XMLHttpRequest = class extends NativeXHR { open(method, url, async = true) { this.__m = method; this.__u = String(url); return super.open(method, url, async); } send(body) { const name = this.__u.includes('web_save') ? 'web_save' : this.__u.includes('web_page_info') ? 'web_page_info' : this.__u.includes('fp-behv') ? 'fp_behv' : 'other'; requests.push({ name, url: this.__u.slice(0, 120), bodyLen: String(body || '').length }); if (name === 'web_save') captured.push(String(body || '')); this.readyState = 4; this.status = 200; this.responseText = name === 'web_page_info' ? pageInfo : '{"ret":0,"msg":"","info":{}}'; this.response = this.responseText; if (typeof this.onreadystatechange === 'function') this.onreadystatechange(); if (typeof this.onload === 'function') this.onload(); this.dispatchEvent(new window.Event('readystatechange')); this.dispatchEvent(new window.Event('load')); } }; window.addEventListener('error', event => pageErrors.push(String(event.error || event.message || 'unknown error'))); }, }); await new Promise(r => setTimeout(r, 15000)); console.log('=== 页面发出的 XHR ==='); for (const r of requests) console.log(' ', r.name, '|', r.url, '| bodyLen:', r.bodyLen); console.log('=== web_save body 捕获:', captured.length ? 'YES len=' + captured[0].length : 'NO'); console.log('=== 页面可见控件 ==='); const btns = dom.window.document.querySelectorAll('[class*="btn"],[class*="confirm"],[class*="submit"],button'); for (const b of btns) { const t = (b.textContent || '').trim().replace(/\s+/g, ' '); if (t && t.length < 40) console.log(' control:', b.tagName, b.className, '|', t.slice(0, 30)); } console.log('=== Avalon 控制器 ==='); const models = dom.window.avalon?.vmodels || {}; for (const [id, model] of Object.entries(models)) { const keys = Object.keys(model).filter(key => !key.startsWith('$')).slice(0, 30); console.log(' model:', id, '|', keys.join(',')); } console.log('=== 订单状态 ==='); console.log(JSON.stringify({ channelListLength: String(models.order?.channelList || '').length, channelData: models.order?.channelData, goodsName: models.order?.goodsName, amount: models.order?.amount, pageErrors: pageErrors.slice(0, 10), }, null, 2)); dom.window.close();