- 添加项目配置文档 (AGENTS.md) - 创建 package.json,包含环信 SDK 及测试依赖 - 添加环信 polyfill 工具模块 - 添加双用户消息互发测试脚本 - 添加 polyfill 测试脚本 Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
138 lines
4.9 KiB
JavaScript
138 lines
4.9 KiB
JavaScript
/**
|
||
* 环信 SDK Node.js Polyfill 工具模块
|
||
*
|
||
* 提供 easemob-websdk 在 Node.js 下运行所需的浏览器 API 模拟。
|
||
*
|
||
* 用法:
|
||
* const { applyManualPolyfill, applyHappyDomPolyfill, printGlobalState } = require('./easemob-polyfill');
|
||
* applyManualPolyfill();
|
||
*/
|
||
|
||
// ──────────────────────────────────────────
|
||
// 手动 Polyfill:逐项注入缺失的浏览器 API
|
||
// ──────────────────────────────────────────
|
||
function applyManualPolyfill() {
|
||
// window → globalThis(关键!让 typeof window === 'object',SDK 据此判断浏览器环境)
|
||
if (typeof globalThis.window === 'undefined') {
|
||
globalThis.window = globalThis;
|
||
console.log(' + window = globalThis');
|
||
}
|
||
|
||
// self → globalThis
|
||
if (typeof globalThis.self === 'undefined') {
|
||
globalThis.self = globalThis;
|
||
console.log(' + self = globalThis');
|
||
}
|
||
|
||
// WebSocket → ws
|
||
if (typeof globalThis.WebSocket === 'undefined') {
|
||
try {
|
||
const wsModule = require('ws');
|
||
globalThis.WebSocket = wsModule.WebSocket || wsModule;
|
||
console.log(' + WebSocket = ws (Node.js)');
|
||
} catch {
|
||
console.warn(' ! ws 包未安装,跳过 WebSocket polyfill');
|
||
}
|
||
}
|
||
|
||
// localStorage → 内存 Map
|
||
if (typeof globalThis.localStorage === 'undefined') {
|
||
const store = new Map();
|
||
globalThis.localStorage = {
|
||
getItem: (key) => store.get(key) ?? null,
|
||
setItem: (key, value) => { store.set(key, String(value)); },
|
||
removeItem: (key) => { store.delete(key); },
|
||
clear: () => { store.clear(); },
|
||
get length() { return store.size; },
|
||
key: (_index) => null,
|
||
};
|
||
console.log(' + localStorage = in-memory Map');
|
||
}
|
||
|
||
// XMLHttpRequest 由调用方按需注入(xhr2 / xmlhttprequest),此处不再自动注入
|
||
|
||
// navigator 基本模拟
|
||
if (typeof globalThis.navigator === 'undefined') {
|
||
globalThis.navigator = {
|
||
userAgent: 'Node.js',
|
||
platform: process.platform,
|
||
appName: 'Node.js',
|
||
appVersion: process.version,
|
||
language: 'zh-CN',
|
||
};
|
||
console.log(' + navigator = basic mock');
|
||
}
|
||
}
|
||
|
||
// ──────────────────────────────────────────
|
||
// happy-dom Polyfill:注入完整浏览器环境
|
||
// ──────────────────────────────────────────
|
||
async function applyHappyDomPolyfill() {
|
||
let happyDOM;
|
||
try {
|
||
happyDOM = require('happy-dom');
|
||
} catch {
|
||
console.error(' ✗ happy-dom 未安装');
|
||
console.error(' 请运行: npm install happy-dom --save-dev');
|
||
process.exit(1);
|
||
}
|
||
|
||
// happy-dom v20+ uses Browser + BrowserContext API
|
||
if (happyDOM.Browser && happyDOM.BrowserContext) {
|
||
const browser = new happyDOM.Browser();
|
||
const page = browser.newPage('https://a1.easemob.com');
|
||
const window = page.mainFrame.window;
|
||
const document = window.document;
|
||
|
||
globalThis.window = window;
|
||
globalThis.document = document;
|
||
globalThis.self = window;
|
||
globalThis.navigator = window.navigator;
|
||
globalThis.localStorage = window.localStorage;
|
||
globalThis.XMLHttpRequest = window.XMLHttpRequest;
|
||
globalThis.WebSocket = window.WebSocket;
|
||
globalThis.fetch = window.fetch.bind(window);
|
||
globalThis.Headers = window.Headers;
|
||
globalThis.Request = window.Request;
|
||
globalThis.Response = window.Response;
|
||
|
||
console.log(' + happy-dom Browser + BrowserContext ✓');
|
||
console.log(` + page URL: ${window.location.href}`);
|
||
return;
|
||
}
|
||
|
||
// 旧版 GlobalRegistrator API (happy-dom < v15)
|
||
const Registrator = happyDOM.GlobalRegistrator;
|
||
if (Registrator && Registrator.register) {
|
||
await Registrator.register();
|
||
console.log(' + happy-dom GlobalRegistrator.register() ✓');
|
||
return;
|
||
}
|
||
|
||
console.error(' ✗ happy-dom 版本不兼容,无法注入全局 API');
|
||
console.error(' 当前版本:', require('happy-dom/package.json').version);
|
||
process.exit(1);
|
||
}
|
||
|
||
// ──────────────────────────────────────────
|
||
// 打印当前全局 API 状态
|
||
// ──────────────────────────────────────────
|
||
function printGlobalState() {
|
||
const apis = [
|
||
'self', 'WebSocket', 'localStorage', 'XMLHttpRequest',
|
||
'navigator', 'fetch', 'window', 'document',
|
||
];
|
||
console.log('\n 全局 API 状态:');
|
||
for (const api of apis) {
|
||
const exists = typeof globalThis[api] !== 'undefined';
|
||
const type = typeof globalThis[api];
|
||
console.log(` ${exists ? '✓' : '✗'} ${api}: ${exists ? type : 'undefined'}`);
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
applyManualPolyfill,
|
||
applyHappyDomPolyfill,
|
||
printGlobalState,
|
||
};
|