Files

187 lines
9.5 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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 <isolated_vm.node> --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;
});