One parentless commit with only the files that the open seed tasks touch and the modules they import. The full squashed import (SPEC 8.6) is a later step: this tree is not the cutoff tree. T2T-StandIn-Source-Commit: 43f95b7333efe73aeaebf806023f17bffea27aba
64 lines
2.5 KiB
JavaScript
64 lines
2.5 KiB
JavaScript
// Buzón opt-in de errores/feedback. Apagado por defecto — la promesa de
|
|
// «nada sale de tu máquina» sigue siendo cierta salvo que el usuario decida
|
|
// lo contrario en Ajustes. Cuando está activo:
|
|
// - errores no capturados (window.onerror / unhandledrejection) y los
|
|
// puntos concretos del código que llaman a reportError() se mandan a
|
|
// /proxy/report (mismo origen, vía el proxy compartido de Elffuss)
|
|
// - sendFeedback() manda texto libre que el usuario escriba a propósito
|
|
// Nunca se manda código ni contenido del proyecto — solo el mensaje técnico,
|
|
// la pila, la URL y el user-agent. Sin cola ni reintentos: si el envío
|
|
// falla, se descarta sin más (evita que un fallo de red se convierta en
|
|
// almacenamiento sin límite).
|
|
let appName = 'elffuss';
|
|
let enabled = false;
|
|
let hooked = false;
|
|
|
|
const storageKey = () => 'elffuss.telemetry.' + appName;
|
|
|
|
export function isEnabled() { return enabled; }
|
|
|
|
export function setEnabled(v) {
|
|
enabled = !!v;
|
|
try { localStorage.setItem(storageKey(), enabled ? '1' : '0'); } catch { /* localStorage lleno/bloqueado */ }
|
|
if (enabled) hookGlobalErrors();
|
|
}
|
|
|
|
async function post(kind, message, opts = {}) {
|
|
if (!enabled) return;
|
|
try {
|
|
await fetch('/proxy/report', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
app: appName, kind, message: String(message ?? '').slice(0, 2000),
|
|
stack: String(opts.stack || '').slice(0, 4000),
|
|
url: location.href, userAgent: navigator.userAgent,
|
|
extra: String(opts.extra || '').slice(0, 2000),
|
|
}),
|
|
});
|
|
} catch { /* sin conexión o proxy caído: se descarta, no hay reintento */ }
|
|
}
|
|
|
|
export function reportError(message, opts = {}) { return post('error', message, opts); }
|
|
export function sendFeedback(text) { return post('feedback', text); }
|
|
|
|
function hookGlobalErrors() {
|
|
if (hooked) return;
|
|
hooked = true;
|
|
window.addEventListener('error', e => {
|
|
reportError(e.message || String(e.error) || 'error', { stack: e.error?.stack || '' });
|
|
});
|
|
window.addEventListener('unhandledrejection', e => {
|
|
const r = e.reason;
|
|
reportError('unhandledrejection: ' + (r?.message || r), { stack: r?.stack || '' });
|
|
});
|
|
}
|
|
|
|
// Llamar UNA vez al arrancar, con el nombre de la app (para distinguir
|
|
// elffuss-code de elffuss-claw en los informes) — lee la preferencia guardada.
|
|
export function init(name) {
|
|
appName = name || appName;
|
|
try { enabled = localStorage.getItem(storageKey()) === '1'; } catch { enabled = false; }
|
|
if (enabled) hookGlobalErrors();
|
|
}
|