Skip to content

Commit 98ae575

Browse files
committed
refactor(vite): simplify client module initialization and payload structure\n\n- Replaced the extensive inline variable declarations with a structured payload object for better readability and maintainability.\n- Updated the client module code to initialize the browser echo with a single call, improving clarity and reducing complexity.
1 parent 3fde549 commit 98ae575

File tree

1 file changed

+16
-133
lines changed

1 file changed

+16
-133
lines changed

packages/vite/src/index.ts

Lines changed: 16 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -292,137 +292,20 @@ function colorize(level: BrowserLogLevel, message: string): string {
292292
type ClientPayload = { sessionId?: string; entries: Array<{ level: BrowserLogLevel | string; text: string; time?: number; stack?: string; source?: string; tag?: string; }>; };
293293

294294
function makeClientModule(options: Required<BrowserLogsToTerminalOptions>) {
295-
const include = JSON.stringify(options.include);
296-
const preserve = JSON.stringify(options.preserveConsole);
297-
const route = JSON.stringify(options.route);
298-
const tag = JSON.stringify(options.tag);
299-
const batchSize = String(options.batch?.size ?? 20);
300-
const batchInterval = String(options.batch?.interval ?? 300);
301-
const netEnabled = !!options.networkLogs?.enabled;
302-
const netFull = !!options.networkLogs?.captureFull;
303-
const bodies = options.networkLogs?.bodies || {};
304-
const bodyReq = !!bodies.request;
305-
const bodyRes = !!bodies.response;
306-
const bodyMax = Number(bodies.maxBytes ?? 2048) | 0;
307-
const bodyPretty = bodies.prettyJson !== false;
308-
const bodyAllow = Array.isArray(bodies.allowContentTypes) && bodies.allowContentTypes.length ? bodies.allowContentTypes : ['application/json','text/','application/x-www-form-urlencoded'];
309-
return `
310-
const __INSTALLED_KEY = '__vite_browser_echo_installed__';
311-
if (!window[__INSTALLED_KEY]) {
312-
window[__INSTALLED_KEY] = true;
313-
const INCLUDE = ${include};
314-
const PRESERVE = ${preserve};
315-
const ROUTE = ${route};
316-
const TAG = ${tag};
317-
const BATCH_SIZE = ${batchSize} | 0;
318-
const BATCH_INTERVAL = ${batchInterval} | 0;
319-
const NET_ENABLED = ${JSON.stringify(netEnabled)};
320-
const NET_FULL = ${JSON.stringify(netFull)};
321-
const NET_BODY_REQ = ${JSON.stringify(bodyReq)};
322-
const NET_BODY_RES = ${JSON.stringify(bodyRes)};
323-
const NET_BODY_MAX = ${JSON.stringify(bodyMax)} | 0;
324-
const NET_BODY_PRETTY = ${JSON.stringify(bodyPretty)};
325-
const NET_BODY_ALLOW = ${JSON.stringify(bodyAllow)};
326-
const SESSION = (function(){try{const a=new Uint8Array(8);crypto.getRandomValues(a);return Array.from(a).map(b=>b.toString(16).padStart(2,'0')).join('')}catch{return String(Math.random()).slice(2,10)}})();
327-
const queue = []; let timer = null;
328-
function enqueue(entry){ queue.push(entry); if (queue.length >= BATCH_SIZE) flush(); else if (!timer) timer = setTimeout(flush, BATCH_INTERVAL); }
329-
function flush(){ if (timer) { clearTimeout(timer); timer = null; } if (!queue.length) return;
330-
const payload = JSON.stringify({ sessionId: SESSION, entries: queue.splice(0, queue.length) });
331-
try { if (navigator.sendBeacon) { navigator.sendBeacon(ROUTE, new Blob([payload], {type:'application/json'})); } else { fetch(ROUTE, { method: 'POST', headers:{'content-type':'application/json'}, body: payload, keepalive: true, cache: 'no-store' }).catch(()=>{}); } } catch {}
332-
}
333-
document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') flush(); });
334-
addEventListener('pagehide', flush); addEventListener('beforeunload', flush);
335-
const ORIGINAL = {};
336-
for (const level of INCLUDE) {
337-
const orig = console[level] ? console[level].bind(console) : console.log.bind(console);
338-
ORIGINAL[level] = orig;
339-
console[level] = (...args) => {
340-
const text = args.map((v)=>{try{if(typeof v==='string') return v; if(v instanceof Error) return (v.name||'Error')+': '+(v.message||''); const seen=new WeakSet(); return JSON.stringify(v,(k,val)=>{ if(typeof val==='bigint') return String(val)+'n'; if(typeof val==='function') return '[Function '+(val.name||'anonymous')+']'; if(val instanceof Error) return {name:val.name,message:val.message,stack:val.stack}; if(typeof val==='symbol') return val.toString(); if(val && typeof val==='object'){ if(seen.has(val)) return '[Circular]'; seen.add(val); } return val; }); } catch { try { return String(v) } catch { return '[Unserializable]' } }}).join(' ');
341-
const stack = (new Error()).stack?.split('\\n').slice(1).filter(l=>!/virtual:browser-echo|enqueue|flush/.test(l)).join('\\n') || '';
342-
const srcMatch = stack.match(/\\(?((?:file:\\/\\/|https?:\\/\\/|\\/)[^) \\n]+):(\\d+):(\\d+)\\)?/);
343-
const source = srcMatch ? (srcMatch[1]+':'+srcMatch[2]+':'+srcMatch[3]) : '';
344-
enqueue({ level, text, time: Date.now(), stack, source });
345-
if (PRESERVE) { try { orig(...args) } catch {} }
346-
};
347-
}
348-
try { ORIGINAL['info']?.(TAG + ' forwarding console logs to ' + ROUTE + ' (session ' + SESSION + ')'); } catch {}
349-
function normUrlStr(input){ try { if(typeof input==='string') return input; if (input && typeof input.url==='string') return input.url; if (input && input.href) return String(input.href||''); return '' } catch { return '' } }
350-
if (NET_ENABLED) {
351-
try {
352-
const __origFetch = window.fetch && window.fetch.bind(window);
353-
if (__origFetch) {
354-
window.fetch = function(input, init){
355-
const start = performance.now();
356-
const method = (init && init.method ? String(init.method) : (input && input.method ? String(input.method) : 'GET')).toUpperCase();
357-
const u = normUrlStr(input);
358-
function baseLine(status, dur){ const st = isFinite(status) ? String(status) : 'ERR'; return '[NETWORK] ['+method+'] ['+(u||'(request)')+'] ['+st+'] ['+dur+'ms]'; }
359-
function getHeader(headers, name){ try{ if(!headers) return ''; var key=String(name).toLowerCase(); if (headers.get) { var v=headers.get(name)||headers.get(key)||''; return String(v||'').toLowerCase(); } if (Array.isArray(headers)) { for (var i=0;i<headers.length;i++){ var kv=headers[i]; if (String(kv[0]).toLowerCase()===key) return String(kv[1]||'').toLowerCase(); } } if (typeof headers==='object'){ for (var k in headers){ if (k.toLowerCase()===key) return String(headers[k]||'').toLowerCase(); } } } catch{} return '' }
360-
function isAllowed(ct){ try{ var c=String(ct||'').toLowerCase(); if(!c) return false; for (var i=0;i<NET_BODY_ALLOW.length;i++){ var al=String(NET_BODY_ALLOW[i]); if (c.startsWith(al)) return true; } } catch{} return false }
361-
function isLikelyText(s){ var t=String(s||'').trim(); if(!t) return true; if(t[0]==='{'||t[0]==='[') return true; return /^[\x09\x0A\x0D\x20-\x7E\u00A0-\uFFFF]*$/.test(t) }
362-
function formatSnippet(raw, ct){ try{ var text=String(raw||''); var lct=String(ct||'').toLowerCase(); if (NET_BODY_PRETTY && (lct.startsWith('application/json') || text.trim().startsWith('{') || text.trim().startsWith('['))) { try { text = JSON.stringify(JSON.parse(text), null, 2) } catch {} } var enc=new TextEncoder(); var bytes=enc.encode(text); if (bytes.length <= NET_BODY_MAX) return text; var sliced=bytes.slice(0, Math.max(0, NET_BODY_MAX)); var dec=new TextDecoder(); var shown=dec.decode(sliced); var extra=bytes.length - sliced.length; return shown+'... (+'+extra+' bytes)'; } catch { return '' } }
363-
function reqSnippet(){ if(!NET_BODY_REQ) return Promise.resolve(''); try{ if (input && typeof input==='object' && input.clone) { var req=input; var headers=req.headers && req.headers.get ? req.headers : null; var ct=getHeader(headers,'content-type') || (init && init.headers ? getHeader(init.headers,'content-type') : ''); if (!isAllowed(ct)) return Promise.resolve(''); return req.clone().text().then(function(txt){ return formatSnippet(txt, ct) }); } var ct2 = init && init.headers ? getHeader(init.headers,'content-type') : ''; var body = init && init.body; if (typeof body==='string') { if (!ct2 || isAllowed(ct2) || isLikelyText(body)) return Promise.resolve(formatSnippet(body, ct2)); } else if (body && body.toString && (body instanceof URLSearchParams)) { var s = body.toString(); var reqCt = ct2 || 'application/x-www-form-urlencoded'; if (isAllowed(reqCt)) return Promise.resolve(formatSnippet(s, reqCt)); } else if (body && typeof body.size==='number') { var size = Number(body.size)|0; return Promise.resolve('[binary: '+size+' bytes]'); } } catch {} return Promise.resolve('') }
364-
function resSnippet(res){ if(!NET_BODY_RES) return Promise.resolve(''); try{ var ct=getHeader(res && res.headers, 'content-type'); if (!isAllowed(ct)) return Promise.resolve(''); if (res && res.clone) { try { var clone=res.clone(); return clone.text().then(function(txt){ return formatSnippet(txt, ct) }); } catch {} } } catch {} return Promise.resolve('') }
365-
try {
366-
const p = __origFetch(input, init);
367-
return Promise.resolve(p).then(function(res){ var dur=Math.max(0, Math.round(performance.now()-start)); var st=Number(res && res.status || 0)|0; var ok=!!(res && res.ok); var extra = NET_FULL ? (' [size:' + (Number(res && res.headers && res.headers.get && res.headers.get('content-length') || 0) | 0) + ']') : ''; Promise.all([reqSnippet(), resSnippet(res)]).then(function(arr){ var reqS=arr[0], resS=arr[1]; var line = baseLine(st, dur) + extra; if (reqS) line += '\n req: ' + reqS; if (resS) line += '\n res: ' + resS; enqueue({ level: ok ? 'info' : 'warn', text: line, time: Date.now(), tag: '[network]' }); }).catch(function(){ var line=baseLine(st, dur) + extra; enqueue({ level: ok ? 'info' : 'warn', text: line, time: Date.now(), tag: '[network]' }); }); return res; }).catch(function(err){ var dur=Math.max(0, Math.round(performance.now()-start)); reqSnippet().then(function(reqS){ var line = baseLine(0, dur) + ' fetch failed'; if (reqS) line += '\n req: ' + reqS; enqueue({ level: 'warn', text: line, time: Date.now(), tag: '[network]' }); }).catch(function(){ var line=baseLine(0, dur) + ' fetch failed'; enqueue({ level: 'warn', text: line, time: Date.now(), tag: '[network]' }); }); throw err; });
368-
} catch (err) {
369-
var dur2 = Math.max(0, Math.round(performance.now()-start));
370-
var line2 = baseLine(0, dur2) + ' fetch failed';
371-
enqueue({ level: 'warn', text: line2, time: Date.now(), tag: '[network]' });
372-
throw err;
373-
}
374-
}
375-
}
376-
} catch {}
377-
try {
378-
const XHR = window.XMLHttpRequest;
379-
if (XHR && XHR.prototype) {
380-
const _open = XHR.prototype.open, _send = XHR.prototype.send;
381-
XHR.prototype.open = function(method, url){ try{ this.__be_method__ = String(method||'GET').toUpperCase() }catch{} try{ this.__be_url__ = String(url||'') }catch{} return _open.apply(this, arguments); };
382-
XHR.prototype.send = function(){ const start = performance.now(); const onEnd = ()=>{ try{ const dur = Math.max(0, Math.round(performance.now()-start)); const method = this.__be_method__ || 'GET'; const u = this.__be_url__ || ''; const status = Number(this.status||0)|0; const ok = status >= 200 && status < 400; const extra = NET_FULL ? ('ready:'+this.readyState) : ''; const line = '[NETWORK] ['+method+'] ['+u+'] ['+(status||'ERR')+'] ['+dur+'ms]'+(extra?(' '+extra):''); enqueue({ level: ok ? 'info' : 'warn', text: line, time: Date.now(), tag: '[network]' }); } catch {} try { this.removeEventListener('loadend', onEnd); this.removeEventListener('error', onEnd); this.removeEventListener('abort', onEnd); } catch {} };
383-
try { this.addEventListener('loadend', onEnd); } catch {}
384-
try { this.addEventListener('error', onEnd); } catch {}
385-
try { this.addEventListener('abort', onEnd); } catch {}
386-
return _send.apply(this, arguments);
387-
}
388-
}
389-
} catch {}
390-
try {
391-
const WS = window.WebSocket;
392-
if (WS) {
393-
// @ts-ignore
394-
window.WebSocket = new Proxy(WS, {
395-
construct(Target, args) {
396-
const url = normUrlStr(args?.[0]);
397-
const start = performance.now();
398-
// @ts-ignore
399-
const socket = new Target(...args);
400-
try {
401-
socket.addEventListener('open', () => {
402-
const dur = Math.max(0, Math.round(performance.now() - start));
403-
const line = '[NETWORK] [WS OPEN] ['+(url||'(ws)')+'] ['+dur+'ms]';
404-
enqueue({ level: 'info', text: line, time: Date.now(), tag: '[network]' });
405-
});
406-
socket.addEventListener('close', (ev) => {
407-
const dur = Math.max(0, Math.round(performance.now() - start));
408-
const code = Number(ev && ev.code || 0) | 0;
409-
const reason = ev && ev.reason ? String(ev.reason) : '';
410-
const extra = reason ? ('code:'+code+' reason:'+reason) : ('code:'+code);
411-
const line = '[NETWORK] [WS CLOSE] ['+(url||'(ws)')+'] ['+dur+'ms] '+extra;
412-
enqueue({ level: code === 1000 ? 'info' : 'warn', text: line, time: Date.now(), tag: '[network]' });
413-
});
414-
socket.addEventListener('error', () => {
415-
const dur = Math.max(0, Math.round(performance.now() - start));
416-
const line = '[NETWORK] [WS ERROR] ['+(url||'(ws)')+'] ['+dur+'ms]';
417-
enqueue({ level: 'warn', text: line, time: Date.now(), tag: '[network]' });
418-
});
419-
} catch {}
420-
return socket;
421-
}
422-
});
423-
}
424-
} catch {}
425-
}
426-
}
427-
`;
295+
const payload = {
296+
route: options.route,
297+
include: options.include,
298+
preserveConsole: options.preserveConsole,
299+
tag: options.tag,
300+
batch: options.batch,
301+
stackMode: options.stackMode,
302+
networkLogs: options.networkLogs,
303+
};
304+
const code = [
305+
`import { initBrowserEcho } from '@browser-echo/core';`,
306+
`if (typeof window !== 'undefined') {`,
307+
` initBrowserEcho(${JSON.stringify(payload)});`,
308+
`}`
309+
].join('\n');
310+
return code;
428311
}

0 commit comments

Comments
 (0)