// In-memory read-only DOM primitives for executing the actual observer strings. // No browser, network, module imports in the observer context or platform state. const vm = require('node:vm'); let input = ''; process.stdin.setEncoding('utf8'); process.stdin.on('data', d => input += d); process.stdin.on('end', () => { const { code, arg, scenario = 'note', observer = 'source' } = JSON.parse(input); class El { constructor(tag, attrs = {}, text = '', children = [], top = 10) { this.tagName = tag.toUpperCase(); this.attrs = attrs; this.id = attrs.id || ''; this.ownText = text; this.children = children; this.top = top; this.scrollTop = 0; this.scrollHeight = this.clientHeight = 600; for (const child of children) child.parentElement = this; } get href() { return this.attrs.href ? new URL(this.attrs.href, location.href).href : ''; } get innerText() { return [this.ownText, ...this.children.map(c => c.innerText)].filter(Boolean).join('\n'); } getBoundingClientRect() { return { left: 0, right: 300, top: this.top, bottom: this.top + 35, width: 300, height: 35 }; } getAttribute(k) { return this.attrs[k] ?? null; } matches(query) { return query.split(',').some(q => match(this, q.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(c => c.contains(node)); } querySelectorAll(query) { return flat(this).slice(1).filter(n => n.matches(query)); } querySelector(query) { return this.querySelectorAll(query)[0] || null; } } function flat(n) { return [n, ...n.children.flatMap(flat)]; } function atom(n, raw) { let q = raw; const attrs = [...q.matchAll(/\[([\w-]+)(\*=|=)?["']?([^\]"']*)["']?\]/g)]; for (const [, k, op, value] of attrs) { const actual = n.attrs[k]; if (actual === undefined || op === '=' && actual !== value || op === '*=' && !actual.includes(value)) return false; } q = q.replace(/\[[^\]]+\]/g, ''); const id = q.match(/#([\w-]+)/); if (id && n.id !== id[1]) return false; const classes = [...q.matchAll(/\.([\w-]+)/g)].map(x => x[1]); if (classes.some(c => !(n.attrs.class || '').split(/\s+/).includes(c))) return false; const tag = q.match(/^[A-Za-z]+/); return !tag || tag[0].toUpperCase() === n.tagName; } function match(n, raw) { const parts = raw.trim().split(/\s+/); let cur = n; if (!atom(cur, parts.pop())) return false; while (parts.length) { const part = parts.pop(); cur = cur.parentElement; while (cur && !atom(cur, part)) cur = cur.parentElement; if (!cur) return false; } return true; } const location = { href: 'https://www.xiaohongshu.com/explore/' + 'a'.repeat(24) }; const pa = 'https://www.xiaohongshu.com/user/profile/' + 'b'.repeat(24); const pb = 'https://www.xiaohongshu.com/user/profile/' + 'c'.repeat(24); const author = new El('a', {id:'author-avatar',href:pa,class:'avatar'}, '', [new El('img')]); const authorName = new El('a', {id:'author-name',href:pa,class:'name'}, '正文作者'); const header = new El('div', {class:'note-header'}, '', [author, authorName]); const desc = new El('div', {id:'detail-desc'}, '本人正在找装修公司'); const replyAuthor = new El('a', {id:'reply-author',href:pa,class:'name'}, '回复者'); const replyContent = new El('div', {id:'reply-content',class:'content'}, '回复本人需求'); const reply = new El('div', {id:'reply',class:'comment-item-sub'}, '', [new El('div',{class:'author-wrapper'},'', [replyAuthor]),replyContent]); const commentAuthor = new El('a', {id:'comment-author',href:pb,class:'name'}, '评论作者'); const commentContent = new El('div', {id:'comment-content',class:'content'}, '评论本人的需求'); const comment = new El('div', {id:'comment',class:'comment-item','data-comment-id':'actual-comment'}, '', [new El('div',{class:'author-wrapper'},'', [commentAuthor]),commentContent, reply]); const commentArea = new El('div', {id:'discussion',class:'comments-container'}, '', [comment]); const counter = new El('span',{id:'comment-entry',class:'chat-wrapper'},'101'); const root = new El('div',{id:'noteContainer'},'',[header,desc,commentArea,counter]); root.getBoundingClientRect = () => ({left:0,right:350,top:0,bottom:900,width:350,height:900}); if (scenario === 'ambiguous') { const other = new El('a',{id:'other-avatar',href:pb,class:'avatar'},'', [new El('img')]); other.parentElement=header;header.children.push(other); } if (scenario === 'below_viewport') for (const el of flat(commentArea)) el.top=800; const body = new El('body',{},'',[root]); const html = new El('html',{},'',[body]); html.scrollHeight=900; const document = { body, documentElement:html, scrollingElement:html, querySelectorAll(q) { return flat(html).filter(n => n.matches(q)); }, querySelector(q) { return this.querySelectorAll(q)[0] || null; }, createTreeWalker(el) { const nodes=flat(el).filter(n=>n.ownText).map(n=>({nodeValue:n.ownText,parentElement:n}));let i=0; return {nextNode(){return nodes[i++] || null;}}; }, createRange() { let node; return {selectNodeContents(n){node=n;},getClientRects(){return [node.parentElement.getBoundingClientRect()];}}; } }; const context={document,location,URL,Set,Array,Math,JSON,NodeFilter:{SHOW_TEXT:4},innerHeight:600,innerWidth:400, getComputedStyle(){return {display:'block',visibility:'visible',opacity:'1',overflowY:'visible'};}}; const fn=vm.runInNewContext('(' + code + ')',context,{timeout:1000}); const result=fn(arg); process.stdout.write(JSON.stringify(result)); });