#!/usr/bin/env node 'use strict'; const path = require('path'); const XBS_ISOLATED_VM_COMPATIBLE_NODE_VERSION = '26.3.1'; const { EXPECTED_XBS_APIS, createIsolatedVmRuntime, getBundledXbsIsolatedVmPath, isAbiMismatch, resolveXbsIsolatedVmPath, } = require('../assets/runtime-frameworks/isolated-vm-runtime'); function parseArgs(argv) { const args = { binary: '', json: false, markdown: false, strict: false, timeoutMs: 5000 }; for (let i = 2; i < argv.length; i++) { const a = argv[i]; if (a === '--binary' || a === '--xbs-isolated-vm' || a === '--path') args.binary = argv[++i] || ''; else if (a === '--timeout-ms') args.timeoutMs = Number(argv[++i] || 5000); else if (a === '--json') args.json = true; else if (a === '--markdown') args.markdown = true; else if (a === '--strict') args.strict = true; else if (a === '--help' || a === '-h') args.help = true; else throw new Error('未知参数:' + a); } if (!args.json && !args.markdown) args.markdown = true; return args; } function usage() { return [ '用法:', ' node scripts/check_xbs_isolated_vm.js --markdown', ' node scripts/check_xbs_isolated_vm.js --json', ' node scripts/check_xbs_isolated_vm.js --binary --strict --json', '', '说明:检测随包魔改 xbs isolated-vm 是否能在当前 Node / 平台 / ABI 下加载,并在 Context 内自检 window.xbs 的 17 个核心 API、xbs.dom.createDocument 和基础 DOM smoke test。默认遇到平台缺失或 ABI 不匹配时输出中文解释但不崩溃;真实 case 已选择 isolated-vm 时可加 --strict 作为阻断门禁。', ].join('\n'); } function baseResult(args) { const binaryPath = path.resolve(args.binary || resolveXbsIsolatedVmPath({})); return { tool: 'check_xbs_isolated_vm', nodeVersion: process.version, nodeAbi: process.versions && process.versions.modules ? process.versions.modules : '', platform: process.platform, arch: process.arch, platformKey: process.platform + '-' + process.arch, defaultBinary: getBundledXbsIsolatedVmPath(), binaryPath, expectedApis: EXPECTED_XBS_APIS.slice(), compatibleNodeVersion: XBS_ISOLATED_VM_COMPATIBLE_NODE_VERSION, requiresUserConfirmation: false, loaded: false, selfCheckPassed: false, abiMismatch: false, explainableFailure: false, status: 'not-run', error: '', xbs: null, dom: null, }; } async function runCheck(args) { const result = baseResult(args); let runtime = null; try { runtime = createIsolatedVmRuntime({ xbsIsolatedVmPath: args.binary || '', timeoutMs: args.timeoutMs, requireXbs: true }); result.loaded = true; result.binaryPath = runtime.binaryPath || result.binaryPath; const initialized = await runtime.initialize({}); result.xbs = initialized.xbs || runtime.getXbsApiSummary(); result.dom = result.xbs && result.xbs.dom ? result.xbs.dom : null; result.selfCheckPassed = !!(result.xbs && result.xbs.ok); result.status = result.selfCheckPassed ? 'ok' : 'xbs-api-missing'; if (!result.selfCheckPassed) result.error = '已加载二进制,但 Context 内 window.xbs 自检未通过。'; } catch (error) { result.error = String(error && error.message ? error.message : error || '未知错误'); result.abiMismatch = isAbiMismatch(error) || !!(error && error.abiMismatch); result.explainableFailure = result.abiMismatch || /未找到 isolated_vm\.node|未找到|no such file|cannot find/i.test(result.error); result.status = result.abiMismatch ? 'abi-mismatch' : (result.explainableFailure ? 'binary-missing' : 'load-failed'); result.requiresUserConfirmation = !!result.abiMismatch; if (result.abiMismatch) { result.recovery = { rule: '不得直接改用 npm 原版 isolated-vm,也不得桥接旧 addon.node;先询问用户是否通过 nvm 安装 / 切换兼容 Node。', commands: [ `nvm install ${XBS_ISOLATED_VM_COMPATIBLE_NODE_VERSION}`, `nvm use ${XBS_ISOLATED_VM_COMPATIBLE_NODE_VERSION}`, 'node -v', 'node -p "process.versions.modules"', 'node --no-node-snapshot scripts/check_xbs_isolated_vm.js --strict --json', ], }; } } finally { if (runtime && typeof runtime.dispose === 'function') await runtime.dispose().catch(() => {}); } result.passed = !!(result.loaded && result.selfCheckPassed); return result; } function renderMarkdown(result) { const lines = []; lines.push('# xbs isolated-vm 自检报告'); lines.push(''); lines.push('- 检查结果:' + (result.passed ? '通过' : '未通过')); lines.push('- 状态:' + result.status); lines.push('- 当前 Node:' + result.nodeVersion); lines.push('- 当前 ABI:' + result.nodeAbi); lines.push('- xbs isolated-vm 兼容 Node:v' + (result.compatibleNodeVersion || XBS_ISOLATED_VM_COMPATIBLE_NODE_VERSION)); lines.push('- 当前平台:' + result.platformKey); lines.push('- 二进制路径:' + result.binaryPath); lines.push('- 是否加载成功:' + (result.loaded ? '是' : '否')); lines.push('- Context 内 xbs API + DOM 自检:' + (result.selfCheckPassed ? '通过' : '未通过')); if (result.xbs) { lines.push('- window === globalThis:' + (result.xbs.windowIsGlobal ? '是' : '否')); lines.push('- window instanceof Window:' + (result.xbs.windowInstanceOfWindow ? '是' : '否')); lines.push('- 缺失核心 API:' + (result.xbs.missing && result.xbs.missing.length ? result.xbs.missing.join('、') : '无')); lines.push('- 额外 API:' + (result.xbs.extra && result.xbs.extra.length ? result.xbs.extra.join('、') : '无')); lines.push('- xbs.dom:' + (result.xbs.hasXbsDom ? '存在' : '不存在')); lines.push('- xbs.dom.createDocument:' + (result.xbs.hasCreateDocument ? '可用' : '不可用')); if (result.xbs.dom) { lines.push('- DOM smoke test:' + (result.xbs.dom.ok ? '通过' : '未通过')); lines.push('- document.constructor.name:' + (result.xbs.dom.documentCtor || '未知')); lines.push('- document 默认预挂载到 window:' + (result.xbs.dom.windowDocumentWasPreinstalled ? '是' : '否')); lines.push('- document.all 类型:' + (result.xbs.dom.allType || '未知')); lines.push('- document.all == null:' + (result.xbs.dom.allLooseNull ? '是' : '否')); lines.push('- document.all === undefined:' + (result.xbs.dom.allStrictUndefined ? '是' : '否')); lines.push('- Boolean(document.all):' + String(result.xbs.dom.allBoolean)); lines.push('- document.all.length 类型:' + (result.xbs.dom.allLengthType || '未知')); lines.push('- document.all.item 类型:' + (result.xbs.dom.allItemType || '未知')); lines.push('- document.all.namedItem 类型:' + (result.xbs.dom.allNamedItemType || '未知')); lines.push('- Object.prototype.toString.call(document.all):' + (result.xbs.dom.allObjectToString || '未知')); lines.push('- document.all.length 是自有属性:' + (result.xbs.dom.allOwnLength ? '是' : '否')); lines.push('- document.all.item 是自有属性:' + (result.xbs.dom.allOwnItem ? '是' : '否')); lines.push('- document.all.namedItem 是自有属性:' + (result.xbs.dom.allOwnNamedItem ? '是' : '否')); lines.push('- document.all 引用稳定:' + (result.xbs.dom.allSameReference ? '是' : '否')); lines.push('- omitApis 禁用 document.all:' + (result.xbs.dom.omitAllWorks ? '通过' : '未通过')); lines.push('- iframe contentDocument:' + (result.xbs.dom.iframeContentDocument ? '可用' : '未确认')); } } if (result.error) { lines.push(''); lines.push('## 说明'); lines.push(''); lines.push(result.error.replace(/\n/g, '\n\n')); } if (!result.passed && result.explainableFailure) { lines.push(''); lines.push('## 处理建议'); lines.push(''); if (result.abiMismatch) { lines.push('- ABI 不兼容时不要直接降级。先让用户确认是否通过 nvm 安装 / 切换 Node.js v' + (result.compatibleNodeVersion || XBS_ISOLATED_VM_COMPATIBLE_NODE_VERSION) + '。'); lines.push('- 用户拒绝切换 Node 时,再让用户提供当前平台 / 架构 / Node ABI 匹配的魔改 xbs isolated-vm 构建产物,或改选不使用框架 / Node.js 内置 vm / jsEnv。'); } else { lines.push('- 如果用户已选择 isolated-vm,请让用户提供与当前平台 / 架构 / Node ABI 匹配的魔改 xbs isolated-vm 构建产物,或按 Node 兼容恢复流程切换匹配版本。'); } lines.push('- 不要自动降级到 npm 原版 isolated-vm,也不要桥接旧 addon.node。'); } if (result.recovery && result.recovery.commands) { lines.push(''); lines.push('## ABI 恢复命令'); lines.push(''); lines.push(result.recovery.rule); lines.push(''); lines.push('```bash'); for (const cmd of result.recovery.commands) lines.push(cmd); lines.push('```'); } return lines.join('\n') + '\n'; } (async () => { const args = parseArgs(process.argv); if (args.help) { console.log(usage()); return; } const result = await runCheck(args); if (args.json) console.log(JSON.stringify(result, null, 2)); if (args.markdown) process.stdout.write(renderMarkdown(result)); if (!result.passed) process.exitCode = args.strict || !result.explainableFailure ? 1 : 0; })().catch(error => { console.error(error && error.message ? error.message : String(error)); process.exitCode = 2; });