// Execute the shipped read-only observers on an in-memory discussion tree. // This fixture never starts a browser or touches the network/platform state. const vm = require('node:vm'); let input = ''; process.stdin.setEncoding('utf8'); process.stdin.on('data', chunk => input += chunk); process.stdin.on('end', () => { const { code, arg, tree } = JSON.parse(input); let lastMeasured = null; const location = { href: 'https://www.xiaohongshu.com/explore/' + 'a'.repeat(24) }; class El { constructor(value) { this.tagName = (value.tag || 'div').toUpperCase(); this.attrs = value.attrs || {}; this.id = this.attrs.id || ''; this.rect = value.rect; this.style = value.style || {}; this.scrollTop = value.scrollTop ?? 0; this.clientHeight = value.clientHeight ?? 35; this.scrollHeight = value.scrollHeight ?? this.clientHeight; this.text = value.text || ''; this.children = (value.children || []).map(child => new El(child)); for (const child of this.children) child.parentElement = this; } get href() { return this.attrs.href ? new URL(this.attrs.href, location.href).href : ''; } get innerText() { return [this.text, ...this.children.map(child => child.innerText)].filter(Boolean).join('\n'); } getAttribute(name) { return this.attrs[name] ?? null; } getBoundingClientRect() { lastMeasured=this;return this.rect || {left:0,right:300,top:10,bottom:45,width:300,height:35}; } matches(query) { return query.split(',').some(part => matches(this, part.trim())); } closest(query) { let node = this; while (node) { if (node.matches(query)) return node; node = node.parentElement; } return null; } contains(node) { return node === this || this.children.some(child => child.contains(node)); } querySelectorAll(query) { return flat(this).slice(1).filter(node => node.matches(query)); } querySelector(query) { return this.querySelectorAll(query)[0] || null; } } function flat(node) { return [node, ...node.children.flatMap(flat)]; } function atom(node, raw) { let query = raw; for (const [, key, op, value] of query.matchAll(/\[([\w-]+)(\*=|=)?["']?([^\]"']*)["']?\]/g)) { const actual = node.attrs[key]; if (actual === undefined || op === '=' && actual !== value || op === '*=' && !actual.includes(value)) return false; } query = query.replace(/\[[^\]]+\]/g, ''); const id = query.match(/#([\w-]+)/); if (id && node.id !== id[1]) return false; const classes = [...query.matchAll(/\.([\w-]+)/g)].map(match => match[1]); if (classes.some(value => !(node.attrs.class || '').split(/\s+/).includes(value))) return false; const tag = query.match(/^[A-Za-z]+/); return !tag || tag[0].toUpperCase() === node.tagName; } function matches(node, raw) { const parts = raw.trim().split(/\s+/); let current = node; if (!atom(current, parts.pop())) return false; while (parts.length) { const part = parts.pop(); current = current.parentElement; while (current && !atom(current, part)) current = current.parentElement; if (!current) return false; } return true; } const body = new El({tag:'body',children:[tree]}); const html = new El({tag:'html'}); html.children = [body]; body.parentElement = html; if(tree.document_scroll){Object.assign(html,tree.document_scroll);body.rect={left:0,right:400,top:0,bottom:0,width:400,height:0};} const document = { body, documentElement:html, scrollingElement:html, elementFromPoint(x,y) { const target=tree.hit_target_id?flat(html).find(node=>node.id===tree.hit_target_id):lastMeasured; if(!target)return body; const own=target.getBoundingClientRect(); let hit=x>=own.left&&x=own.top&&y=0&&x<400&&y>=0&&y<600; for(let parent=target.parentElement;parent;parent=parent.parentElement){ if(['BODY','HTML'].includes(parent.tagName))continue; const s=parent.style,r=parent.getBoundingClientRect(); if(/auto|scroll|hidden|clip/.test(s.overflowX||s.overflow||''))hit=hit&&x>=r.left&&x=r.top&&y node.matches(query)); }, querySelector(query) { return this.querySelectorAll(query)[0] || null; }, createTreeWalker(element) { const nodes = flat(element).filter(node => node.text).map(node => ({nodeValue:node.text,parentElement:node})); let index = 0; return {nextNode() { return nodes[index++] || null; }}; }, createRange() { let node; return {selectNodeContents(value) { node = value; },getClientRects() { return [node.parentElement.getBoundingClientRect()]; }}; } }; const context = {document,location,URL,Set,Array,Math,JSON,NodeFilter:{SHOW_TEXT:4},innerHeight:600,innerWidth:400, getComputedStyle(node) { return {display:'block',visibility:'visible',opacity:'1',...node.style}; }}; const fn = vm.runInNewContext('(' + code + ')',context,{timeout:1000}); process.stdout.write(JSON.stringify(fn(arg))); });