// 浏览器端 Node.js 内置模块 polyfill // 用于加载 webpack/ncc 打包的官方音源插件 // 提供 http/https (基于 fetch)、stream、util、events、crypto、url、zlib、path、fs、buffer 等 function createEventEmitter(): any { const listeners: Record = {}; const self: any = { on(event: string, fn: Function) { (listeners[event] = listeners[event] || []).push(fn); return self; }, once(event: string, fn: Function) { const wrapper = (...args: any[]) => { self.off(event, wrapper); fn(...args); }; return self.on(event, wrapper); }, off(event: string, fn: Function) { if (listeners[event]) { listeners[event] = listeners[event].filter((f: Function) => f !== fn); } return self; }, emit(event: string, ...args: any[]) { const fns = listeners[event] || []; for (const fn of fns) { try { fn.apply(null, args); } catch (_) {} } return true; }, addListener: function() { return self.on.apply(self, arguments as any); }, removeListener: function() { return self.off.apply(self, arguments as any); }, removeAllListeners(event?: string) { if (event) delete listeners[event]; else { for (const k in listeners) delete listeners[k]; } return self; }, listeners(event: string) { return listeners[event] || []; }, }; return self; } function createStreamBase(): any { function Stream() {} Stream.prototype.pipe = function(dest: any) { return dest; }; Stream.prototype.on = function(_e: string, _fn: Function) { return this; }; Stream.prototype.emit = function() { return true; }; return Stream; } function createReadable(): any { function Readable() {} Readable.prototype.pipe = function(dest: any) { return dest; }; Readable.prototype.on = function() { return this; }; Readable.prototype.once = function() { return this; }; Readable.prototype.emit = function() { return true; }; Readable.prototype.read = function() { return null; }; Readable.prototype.pause = function() { return this; }; Readable.prototype.resume = function() { return this; }; Readable.prototype.push = function() { return this; }; return Readable; } function createWritable(): any { function Writable() {} Writable.prototype.write = function() { return true; }; Writable.prototype.end = function() { return this; }; Writable.prototype.on = function() { return this; }; Writable.prototype.once = function() { return this; }; Writable.prototype.emit = function() { return true; }; return Writable; } function createTransform(): any { function Transform() {} Transform.prototype.pipe = function(dest: any) { return dest; }; Transform.prototype.write = function() { return true; }; Transform.prototype.end = function() { return this; }; Transform.prototype.on = function() { return this; }; Transform.prototype.once = function() { return this; }; Transform.prototype.emit = function() { return true; }; return Transform; } function createPassThrough(): any { function PassThrough() {} PassThrough.prototype.pipe = function(dest: any) { return dest; }; PassThrough.prototype.write = function() { return true; }; PassThrough.prototype.end = function() { return this; }; PassThrough.prototype.on = function() { return this; }; PassThrough.prototype.once = function() { return this; }; PassThrough.prototype.emit = function() { return true; }; return PassThrough; } function parseUrl(u: string): any { try { const url = new URL(u, typeof window !== 'undefined' ? window.location.href : 'http://localhost/'); return { protocol: url.protocol, hostname: url.hostname, host: url.host, port: url.port || (url.protocol === 'https:' ? '443' : '80'), pathname: url.pathname, path: url.pathname + url.search, search: url.search, query: Object.fromEntries(url.searchParams.entries()), hash: url.hash, href: url.href, auth: url.username ? url.username + (url.password ? ':' + url.password : '') : '', }; } catch { return { protocol: '', hostname: '', host: '', pathname: '', path: '', href: u, port: '80' }; } } // http/https 模块 - 用 fetch 实现 function createHttpModule(isHttps: boolean): any { function request(options: any, callback?: (res: any) => void): any { let url: string; let opts: any = typeof options === 'string' ? {} : { ...options }; if (typeof options === 'string') { url = options; } else if (options && typeof options === 'object') { const protocol = opts.protocol || (isHttps ? 'https:' : 'http:'); const host = opts.hostname || opts.host || 'localhost'; const port = opts.port ? ':' + opts.port : ''; const path = opts.path || opts.pathname || '/'; url = protocol + '//' + host + port + path; } else { url = String(options); } const method = opts.method || 'GET'; const headers = opts.headers || {}; const req: any = createEventEmitter(); let requestBodyChunks: Uint8Array[] = []; let ended = false; req.write = function(chunk: any) { if (chunk) requestBodyChunks.push(typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk); return true; }; req.end = function(chunk: any) { if (chunk) req.write(chunk); if (ended) return; ended = true; let body: any = undefined; if (method !== 'GET' && method !== 'HEAD' && requestBodyChunks.length > 0) { if (requestBodyChunks.length === 1) body = requestBodyChunks[0]; else { const total = requestBodyChunks.reduce((a, b) => a + b.length, 0); const merged = new Uint8Array(total); let offset = 0; for (const c of requestBodyChunks) { merged.set(c, offset); offset += c.length; } body = merged; } } fetch(url, { method, headers, body, signal: opts.signal, mode: 'cors', credentials: 'omit', } as any).then((resp: any) => { const res: any = createEventEmitter(); res.statusCode = resp.status; res.statusMessage = resp.statusText; res.headers = {}; try { resp.headers.forEach((v: string, k: string) => { res.headers[k.toLowerCase()] = v; }); } catch {} res.setEncoding = function() { return res; }; res.pipe = function(dest: any) { resp.arrayBuffer().then((buf: ArrayBuffer) => { if (dest && typeof dest.write === 'function') dest.write(new Uint8Array(buf)); if (dest && typeof dest.end === 'function') dest.end(); res.emit('end'); }).catch((err: Error) => res.emit('error', err)); return dest; }; res.text = () => resp.text(); res.json = () => resp.json(); res.body = resp.body; if (callback) callback(res); req.emit('response', res); // 如果没有 callback,等待 body 然后发射 data/end 事件 if (!callback) { resp.arrayBuffer().then((buf: ArrayBuffer) => { res.emit('data', new Uint8Array(buf)); res.emit('end'); }).catch((err: Error) => res.emit('error', err)); } }).catch((err: Error) => { req.emit('error', err); }); return req; }; req.abort = function() { req.emit('abort'); }; req.setTimeout = function() { return req; }; req.setHeader = function(name: string, value: string) { (headers as any)[name.toLowerCase()] = value; return req; }; req.getHeader = function(name: string) { return (headers as any)[name.toLowerCase()]; }; req.removeHeader = function(name: string) { delete (headers as any)[name.toLowerCase()]; }; // 如果用户立即调用 req.end(),我们需要在 next tick 开始 - 但实际上用户必须自己调用 end return req; } function get(options: any, callback?: (res: any) => void): any { const req = request(options, callback); req.end(); return req; } return { request, get, createServer: () => ({ listen: () => {}, close: () => {} }), Agent: function() { this.maxSockets = 50; }, globalAgent: { maxSockets: 50 }, STATUS_CODES: {}, }; } // util 模块 const utilModule: any = { inspect: (obj: any) => { try { return JSON.stringify(obj); } catch { return String(obj); } }, format: function() { return Array.prototype.slice.call(arguments).join(' '); }, inherits: function(ctor: any, superCtor: any) { if (!superCtor || !superCtor.prototype) return; ctor.super_ = superCtor; const origProto = ctor.prototype; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true }, }); // 保留原型上原有的属性 if (origProto) { for (const k in origProto) { if (Object.prototype.hasOwnProperty.call(origProto, k)) { ctor.prototype[k] = origProto[k]; } } } }, promisify: function(fn: any) { return function() { const args = Array.prototype.slice.call(arguments); return new Promise((resolve, reject) => { args.push((err: any, ...rest: any[]) => { if (err) reject(err); else resolve(rest.length === 1 ? rest[0] : rest); }); try { fn.apply(null, args); } catch (e) { reject(e); } }); }; }, callbackify: function(fn: any) { return function() { const args = Array.prototype.slice.call(arguments); const cb = args.pop(); fn.apply(null, args).then((r: any) => cb(null, r)).catch((e: Error) => cb(e)); }; }, TextDecoder: typeof (globalThis as any).TextDecoder !== 'undefined' ? (globalThis as any).TextDecoder : function(this: any) { this.decode = function() { return ''; }; }, TextEncoder: typeof (globalThis as any).TextEncoder !== 'undefined' ? (globalThis as any).TextEncoder : function(this: any) { this.encode = function(s: string) { return s; }; }, types: { isDate: (x: any) => x instanceof Date, isRegExp: (x: any) => x instanceof RegExp, isError: (x: any) => x instanceof Error, isArray: Array.isArray, isFunction: (x: any) => typeof x === 'function' }, }; // crypto 模块 const cryptoModule: any = { createHash: function(algo: string) { const chunks: string[] = []; return { update: function(chunk: any) { chunks.push(String(chunk)); return this; }, digest: function(encoding?: string) { // 简化:不做真正的哈希,返回一个稳定的伪随机值 const s = algo + ':' + chunks.join(''); let h = 0; for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0; const hex = Math.abs(h).toString(16).padStart(16, '0') + Math.abs(h * 31).toString(16).padStart(16, '0'); if (encoding === 'hex') return hex; if (encoding === 'base64') return btoa(hex); return hex; }, }; }, createHmac: function(algo: string, key: any) { const chunks: string[] = []; return { update: function(chunk: any) { chunks.push(String(chunk)); return this; }, digest: function(encoding?: string) { const s = 'hmac:' + algo + ':' + key + ':' + chunks.join(''); let h = 0; for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0; const hex = Math.abs(h).toString(16).padStart(16, '0') + Math.abs(h * 17).toString(16).padStart(16, '0'); if (encoding === 'hex') return hex; if (encoding === 'base64') return btoa(hex); return hex; }, }; }, randomBytes: function(n: number, cb?: (err: Error | null, buf: any) => void) { const arr = new Uint8Array(n); if (typeof (globalThis as any).crypto !== 'undefined' && (globalThis as any).crypto.getRandomValues) { (globalThis as any).crypto.getRandomValues(arr); } else { for (let i = 0; i < n; i++) arr[i] = Math.floor(Math.random() * 256); } const buf: any = { buffer: arr, length: n, toString: function(enc?: string) { if (enc === 'hex') { let s = ''; for (let i = 0; i < n; i++) s += arr[i].toString(16).padStart(2, '0'); return s; } if (enc === 'base64') { let s = ''; for (let i = 0; i < n; i++) s += String.fromCharCode(arr[i]); return btoa(s); } return String.fromCharCode.apply(null, arr as any); }, }; if (cb) { setTimeout(() => cb(null, buf), 0); return undefined; } return buf; }, randomUUID: function() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }, }; // 完整的 polyfill 注册表 export function createNodePolyfills(): Record { const streamMod: any = { Stream: createStreamBase(), Readable: createReadable(), Writable: createWritable(), Duplex: createTransform(), Transform: createTransform(), PassThrough: createPassThrough(), pipeline: (_a: any, b: any, cb?: () => void) => { if (cb) cb(); return b; }, finished: (_s: any, cb: () => void) => { cb(); }, }; streamMod.Stream.prototype = Object.create(createEventEmitter()); streamMod.Readable.prototype = Object.create(streamMod.Stream.prototype); streamMod.Writable.prototype = Object.create(streamMod.Stream.prototype); streamMod.Duplex.prototype = Object.create(streamMod.Stream.prototype); streamMod.Transform.prototype = Object.create(streamMod.Duplex.prototype); streamMod.PassThrough.prototype = Object.create(streamMod.Transform.prototype); const bufferModule: any = { from: (x: any) => { if (x instanceof Uint8Array) return x; if (typeof x === 'string') return new TextEncoder().encode(x); if (Array.isArray(x)) return new Uint8Array(x); return new Uint8Array(x || 0); }, alloc: (n: number) => new Uint8Array(n), allocUnsafe: (n: number) => new Uint8Array(n), isBuffer: () => false, concat: (arrs: any[]) => { const total = arrs.reduce((a, b) => a + (b?.length || 0), 0); const merged = new Uint8Array(total); let offset = 0; for (const c of arrs) { if (c) { merged.set(c, offset); offset += c.length; } } return merged; }, byteLength: (x: any) => typeof x === 'string' ? new TextEncoder().encode(x).length : (x?.length || 0), compare: (_a: any, _b: any) => 0, isEncoding: () => true, }; return { http: createHttpModule(false), https: createHttpModule(true), http2: createHttpModule(true), stream: streamMod, util: utilModule, events: { EventEmitter: function() { return createEventEmitter(); } }, crypto: cryptoModule, url: { parse: parseUrl, format: (o: any) => o.href || `${o.protocol}//${o.host}${o.path || o.pathname || ''}`, resolve: (_from: string, to: string) => { try { return new URL(to, _from).href; } catch { return to; } }, URL: typeof (globalThis as any).URL !== 'undefined' ? (globalThis as any).URL : function() {}, URLSearchParams: typeof (globalThis as any).URLSearchParams !== 'undefined' ? (globalThis as any).URLSearchParams : function() {}, }, zlib: { gunzipSync: (d: any) => d, inflateSync: (d: any) => d, deflateSync: (d: any) => d, gzipSync: (d: any) => d, gunzip: (d: any, cb: (err: Error | null, out: any) => void) => { setTimeout(() => cb(null, d), 0); }, inflate: (d: any, cb: (err: Error | null, out: any) => void) => { setTimeout(() => cb(null, d), 0); }, createGunzip: () => { const p = Object.create(streamMod.Transform.prototype); p.write = () => true; p.end = function() { this.emit && this.emit('end'); return this; }; return p; }, createInflate: () => { const p = Object.create(streamMod.Transform.prototype); p.write = () => true; p.end = function() { this.emit && this.emit('end'); return this; }; return p; }, createGzip: () => { const p = Object.create(streamMod.Transform.prototype); p.write = () => true; p.end = function() { this.emit && this.emit('end'); return this; }; return p; }, }, path: { join: function() { return Array.prototype.slice.call(arguments).filter(Boolean).join('/').replace(/\/+/g, '/'); }, resolve: function() { return Array.prototype.slice.call(arguments).filter(Boolean).join('/'); }, dirname: (p: string) => String(p).split('/').slice(0, -1).join('/') || '/', basename: (p: string, ext?: string) => { const base = String(p).split('/').pop() || ''; return ext && base.endsWith(ext) ? base.slice(0, -ext.length) : base; }, extname: (p: string) => { const m = String(p).match(/\.[^.]*$/); return m ? m[0] : ''; }, sep: '/', delimiter: ':', parse: (p: string) => { const parts = String(p).split('/'); return { root: '/', dir: parts.slice(0, -1).join('/'), base: parts[parts.length - 1], ext: '', name: parts[parts.length - 1].replace(/\.[^.]*$/, '') }; }, normalize: (p: string) => p, isAbsolute: (p: string) => p && p[0] === '/', relative: (_from: string, to: string) => to, }, fs: { readFileSync: () => '', writeFileSync: () => {}, existsSync: () => false, createWriteStream: () => { const s = Object.create(streamMod.Writable.prototype); s.write = () => true; s.end = function() { this.emit && this.emit('finish'); return this; }; return s; }, createReadStream: () => { const s = Object.create(streamMod.Readable.prototype); s.pipe = (d: any) => { s.emit && s.emit('end'); return d; }; return s; }, readFile: (_path: any, enc: any, cb: (err: Error | null, data: any) => void) => { if (typeof enc === 'function') cb = enc; setTimeout(() => cb(null, ''), 0); }, writeFile: (_p: any, _d: any, cb?: (err: Error | null) => void) => { if (typeof cb === 'function') setTimeout(() => cb(null), 0); }, mkdirSync: () => {}, statSync: () => ({ isDirectory: () => false, isFile: () => true, size: 0, mtime: new Date() }), unlinkSync: () => {}, promises: { readFile: async () => '', writeFile: async () => {}, mkdir: async () => {}, }, }, os: { platform: () => 'browser', type: () => 'Browser', release: () => '1.0', tmpdir: () => '/tmp', homedir: () => '/', EOL: '\n', arch: () => 'x64', cpus: () => [], totalmem: () => 0, freemem: () => 0, }, assert: { ok: function() {}, equal: function() {}, deepEqual: function() {}, strictEqual: function() {}, throws: function() {}, notEqual: function() {}, ifError: function() {}, }, dns: { lookup: (_host: string, cb: (err: Error | null, addr: string, family: number) => void) => { setTimeout(() => cb(null, '127.0.0.1', 4), 0); }, resolve: (_host2: string, cb: (err: Error | null, addrs: string[]) => void) => { setTimeout(() => cb(null, ['127.0.0.1']), 0); }, }, buffer: bufferModule, Buffer: bufferModule, string_decoder: { StringDecoder: function() { this.write = function(x: any) { return typeof x === 'string' ? x : new TextDecoder().decode(x); }; this.end = function(x: any) { return x ? this.write(x) : ''; }; }, }, querystring: { parse: (s: string) => { const out: Record = {}; const q = String(s || '').replace(/^\?/, ''); if (!q) return out; for (const pair of q.split('&')) { const [k, v] = pair.split('='); if (k) out[decodeURIComponent(k)] = v !== undefined ? decodeURIComponent(v) : ''; } return out; }, stringify: (obj: any) => Object.entries(obj || {}).map(([k, v]) => encodeURIComponent(String(k)) + '=' + encodeURIComponent(String(v))).join('&'), }, tty: { isatty: () => false, ReadStream: function() {}, WriteStream: function() {} }, net: { createServer: () => ({ listen: () => {}, close: () => {}, on: function() { return this; } }), createConnection: () => createEventEmitter(), Socket: function() { return createEventEmitter(); }, }, tls: { connect: () => createEventEmitter(), TLSSocket: function() { return createEventEmitter(); }, createSecureContext: () => ({}), }, child_process: { spawn: () => createEventEmitter(), exec: (_cmd: string, cb: (err: Error | null, stdout: string, stderr: string) => void) => setTimeout(() => cb(null, '', ''), 0), execSync: () => '', }, cluster: { isWorker: false, isMaster: true, fork: function() {}, on: function() { return this; } }, module: { Module: function() {} }, vm: { runInNewContext: (code: string, sandbox: any) => { const fn = new Function(...Object.keys(sandbox || {}), code); return fn(...Object.values(sandbox || {})); } }, perf_hooks: { performance: typeof (globalThis as any).performance !== 'undefined' ? (globalThis as any).performance : { now: () => Date.now() } }, console, }; } // 为给定的 webpack bundle 代码提供完整的沙箱环境 export function createSandboxRequire(): (id: string) => any { const polyfills = createNodePolyfills(); return function(id: string): any { if (polyfills[id]) return polyfills[id]; // 处理子路径,如 stream/transform, crypto/hash 等 const base = id.split('/')[0]; if (polyfills[base]) return polyfills[base]; return {}; }; }