'use strict'; const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const vm = require('node:vm'); const test = require('node:test'); const root = path.resolve(__dirname, '..', '..'); class MiniElement { constructor(tagName = 'div') { this.tagName = String(tagName).toUpperCase(); this.parentNode = null; this.children = []; this.attributes = {}; this.dataset = {}; this.listeners = {}; this.offsetLeft = 0; this.offsetWidth = 0; this.scrollWidth = 0; this.clientWidth = 0; this.scrollLeft = 0; this.rectLeft = 0; this.rectWidth = 0; this.scrollCalls = []; this._text = ''; this._classes = new Set(); this.classList = { add: (...tokens) => tokens.forEach((token) => this._classes.add(token)), remove: (...tokens) => tokens.forEach((token) => this._classes.delete(token)), contains: (token) => this._classes.has(token), toggle: (token, force) => { const shouldAdd = force === undefined ? !this._classes.has(token) : Boolean(force); if (shouldAdd) this._classes.add(token); else this._classes.delete(token); return shouldAdd; }, }; } get className() { return Array.from(this._classes).join(' '); } set className(value) { this._classes = new Set(String(value || '').split(/\s+/).filter(Boolean)); } get id() { return this.getAttribute('id') || ''; } set id(value) { this.setAttribute('id', value); } get max() { return this.getAttribute('max') || ''; } set max(value) { this.setAttribute('max', value); } get value() { return this._value || ''; } set value(value) { this._value = String(value); } get parentElement() { return this.parentNode && this.parentNode.tagName !== '#DOCUMENT' ? this.parentNode : null; } get firstChild() { return this.children[0] || null; } get textContent() { return this._text + this.children.map((child) => child.textContent).join(''); } set textContent(value) { this._text = String(value); this.replaceChildren(); } get innerHTML() { return this.children.map((child) => child.outerHTML || child.textContent).join(''); } set innerHTML(markup) { this.replaceChildren(); parseMarkup(String(markup || ''), this); } setAttribute(name, value) { const text = String(value); this.attributes[name] = text; if (name === 'class') this.className = text; if (name.startsWith('data-')) this.dataset[toDatasetKey(name.slice(5))] = text; } getAttribute(name) { return Object.hasOwn(this.attributes, name) ? this.attributes[name] : null; } hasAttribute(name) { return Object.hasOwn(this.attributes, name); } appendChild(child) { if (child.parentNode) child.remove(); child.parentNode = this; this.children.push(child); return child; } insertBefore(child, anchor) { if (!anchor || anchor.parentNode !== this) return this.appendChild(child); if (child.parentNode) child.remove(); child.parentNode = this; this.children.splice(this.children.indexOf(anchor), 0, child); return child; } replaceChildren(...nodes) { this.children.forEach((child) => { child.parentNode = null; }); this.children = []; nodes.forEach((node) => this.appendChild(node)); } remove() { if (!this.parentNode) return; const siblings = this.parentNode.children; const index = siblings.indexOf(this); if (index !== -1) siblings.splice(index, 1); this.parentNode = null; } addEventListener(type, callback) { (this.listeners[type] ||= []).push(callback); } dispatch(type, properties = {}) { const event = { type, target: this, defaultPrevented: false, preventDefault() { this.defaultPrevented = true; }, stopPropagation() { this.propagationStopped = true; }, ...properties, }; let node = this; while (node) { for (const listener of node.listeners[type] || []) listener(event); if (event.propagationStopped) break; node = node.parentNode; } return event; } click() { return this.dispatch('click'); } focus() { this.ownerDocument.activeElement = this; } closest(selector) { for (let node = this; node; node = node.parentNode) { if (matchesCompound(node, selector)) return node; } return null; } matches(selector) { return matchesCompound(this, selector); } querySelectorAll(selector) { const directOnly = selector.startsWith(':scope > '); const query = directOnly ? selector.slice(9) : selector; const nodes = directOnly ? this.children.slice() : descendants(this); return nodes.filter((node) => matchesSelector(node, query)); } querySelector(selector) { return this.querySelectorAll(selector)[0] || null; } insertAdjacentHTML(position, markup) { const fragment = this.ownerDocument.createElement('div'); fragment.innerHTML = markup; const nodes = fragment.children.slice(); if (position === 'afterbegin') nodes.reverse().forEach((node) => this.insertBefore(node, this.firstChild)); else if (position === 'beforebegin' && this.parentNode) nodes.forEach((node) => this.parentNode.insertBefore(node, this)); else nodes.forEach((node) => this.appendChild(node)); } scrollTo(options) { this.scrollCalls.push(options); } getBoundingClientRect() { return { left: this.rectLeft, width: this.rectWidth || this.offsetWidth }; } } class MiniDocument extends MiniElement { constructor() { super('#document'); this.ownerDocument = this; this.readyState = 'complete'; this.body = this.createElement('body'); this.appendChild(this.body); this.activeElement = null; } createElement(tagName) { const element = new MiniElement(tagName); element.ownerDocument = this; return element; } } function toDatasetKey(value) { return value.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase()); } function descendants(node) { return node.children.flatMap((child) => [child, ...descendants(child)]); } function matchesCompound(node, selector) { if (!selector || selector === '*') return true; const tag = selector.match(/^[a-z][a-z0-9-]*/i); if (tag && node.tagName.toLowerCase() !== tag[0].toLowerCase()) return false; const id = selector.match(/#([a-zA-Z_][\w-]*)/); if (id && node.getAttribute('id') !== id[1]) return false; for (const [, className] of selector.matchAll(/\.([a-zA-Z_][\w-]*)/g)) { if (!node.classList.contains(className)) return false; } for (const [, name, expected] of selector.matchAll(/\[([\w-]+)(?:=["']?([^\]"']+)["']?)?\]/g)) { if (!node.hasAttribute(name)) return false; if (expected !== undefined && node.getAttribute(name) !== expected) return false; } return true; } function matchesSelector(node, selector) { const tokens = selector.replace(/>/g, ' > ').trim().split(/\s+/); let cursor = node; if (!matchesCompound(cursor, tokens.pop())) return false; while (tokens.length) { const combinator = tokens.pop(); const previous = combinator === '>' ? tokens.pop() : combinator; if (combinator === '>') { cursor = cursor.parentNode; if (!cursor || !matchesCompound(cursor, previous)) return false; } else { cursor = cursor.parentNode; while (cursor && !matchesCompound(cursor, previous)) cursor = cursor.parentNode; if (!cursor) return false; } } return true; } function parseMarkup(markup, parent) { const tokens = markup.match(/|<[^>]+>|[^<]+/g) || []; const stack = [parent]; const voidTags = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']); for (const token of tokens) { if (token.startsWith('