chore(ui): surface caught boot errors on the diagnostic panel
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 1m5s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m57s

The es2019 + conditional core-js fixes let the module mount on the Chromium 66 in-app
WebView, but the lobby then shows a white screen and a generic "something went wrong"
toast — a caught error that never reaches window.onerror, so the diagnostic ERRORS
panel stayed empty and could not name the cause.

Mirror console.error / console.warn into the panel, and add a temporary, mock-gated
console.error of the raw error (with stack) in handleError, so the on-device panel
shows exactly what threw and whether it is one error or several. Expected: a
ReferenceError from the 64-bit FlatBuffers timestamp decode (BigInt, absent on Chrome
66 and not polyfillable by core-js) — to be confirmed on-device before deciding the
support floor.

Temporary — both hunks revert together with the index.html BOOT-DIAG block.
This commit is contained in:
Ilia Denisov
2026-07-04 17:40:31 +02:00
parent 4a0c296bbb
commit de2f1432be
2 changed files with 34 additions and 0 deletions
+28
View File
@@ -143,6 +143,34 @@
var r = e && e.reason;
logErr('unhandledrejection', r && (r.stack || r.message) ? r.stack || r.message : String(r));
});
// Mirror console.error / console.warn into the panel. The app catches its boot failures and
// surfaces them only as a toast (they never reach window.onerror); a temporary console.error
// at the app's handleError routes the real cause here, so the panel shows what actually threw.
function fmt(a) {
if (a && (a.stack || a.message)) return a.stack || a.message;
if (typeof a === 'object') {
try {
return JSON.stringify(a);
} catch (e) {
return String(a);
}
}
return String(a);
}
function wrapConsole(orig, label) {
return function () {
try {
var parts = [];
for (var i = 0; i < arguments.length; i++) parts.push(fmt(arguments[i]));
logErr('console.' + label, parts.join(' '));
} catch (e) {}
if (orig) return orig.apply(console, arguments);
};
}
if (window.console) {
console.error = wrapConsole(console.error, 'error');
console.warn = wrapConsole(console.warn, 'warn');
}
// ---- environment -----------------------------------------------------------------------
var ua = nav.userAgent || '(no userAgent)';
+6
View File
@@ -283,6 +283,12 @@ export function markChatRead(gameId: string): void {
* failure — or anything raised while the app is mid-reconnect — is shown by the "Connecting…"
* header indicator (and auto-retried), never a red toast. */
export function handleError(err: unknown): void {
// TEMP (boot diagnostic): the toast is generic, so route the real caught error to the on-device
// BOOT-DIAG panel via console (it mirrors console.error). Mode-gated to keep it out of the mock
// e2e. Revert together with the index.html BOOT-DIAG block.
if (import.meta.env.MODE !== 'mock') {
console.error('[diag] handleError:', err instanceof Error ? err.stack || err.message : err);
}
const code = err instanceof GatewayError ? err.code : '';
if (code === 'session_invalid' || code === 'unauthenticated') {
void logout();