[eric] diag: add targeted logs for each top crash-suspect (boot env snapshot for React-prod-mode + tree-shaking checks, wrapped lazy() loader with requested/loaded/failed logs for chunk-split race, [diag][ErrorBoundary] prefix on componentDidCatch so in-tree throws surface in stderr alongside window.onerror); retag v1.1.44

This commit is contained in:
Eric
2026-05-25 19:26:40 -07:00
parent 1ec3d473f3
commit 294d1a91ed
2 changed files with 50 additions and 9 deletions
+48 -8
View File
@@ -21,18 +21,58 @@ import AppShell from './components/Layout/AppShell';
import DashboardSelection from './pages/DashboardSelection/DashboardSelection';
import ErrorBoundary from './components/feedback/ErrorBoundary';
import { setPanelMode, disableOnboardingAfterCrash } from '@/shared/state/onboardingProgressSlice';
const Skills = lazy(() => import('./pages/Skills/Skills'));
const Tools = lazy(() => import('./pages/Tools/Tools'));
const Modes = lazy(() => import('./pages/Modes/Modes'));
const Views = lazy(() => import('./pages/Views/Views'));
const Customization = lazy(() => import('./pages/Customization/Customization'));
const Analytics = lazy(() => import('./pages/Analytics/Analytics'));
const OnboardingRoot = lazy(() =>
// Wrap every lazy() so each chunk load (request, success, failure) emits a diag line. Chunk-split race against React commit is one of the candidate causes of the packaged-only segfault, so this surfaces if a chunk failed to load in the moment leading up to a crash.
function diagLazy<T extends React.ComponentType<any>>(name: string, loader: () => Promise<{ default: T } | T>): React.LazyExoticComponent<T> {
return lazy(() => {
// eslint-disable-next-line no-console
console.log('[diag][lazy:requested]', name);
return Promise.resolve()
.then(loader)
.then((mod: any) => {
// eslint-disable-next-line no-console
console.log('[diag][lazy:loaded]', name);
return mod && 'default' in mod ? mod : { default: mod };
})
.catch((err) => {
// eslint-disable-next-line no-console
console.error('[diag][lazy:failed]', name, err && err.message, err && err.stack);
throw err;
});
});
}
const Skills = diagLazy('Skills', () => import('./pages/Skills/Skills'));
const Tools = diagLazy('Tools', () => import('./pages/Tools/Tools'));
const Modes = diagLazy('Modes', () => import('./pages/Modes/Modes'));
const Views = diagLazy('Views', () => import('./pages/Views/Views'));
const Customization = diagLazy('Customization', () => import('./pages/Customization/Customization'));
const Analytics = diagLazy('Analytics', () => import('./pages/Analytics/Analytics'));
const OnboardingRoot = diagLazy('OnboardingRoot', () =>
import('./components/Onboarding').then((m) => ({ default: m.OnboardingRoot })),
);
const SignInGate = lazy(() => import('./components/overlays/SignInGate'));
const SignInGate = diagLazy('SignInGate', () => import('./components/overlays/SignInGate'));
if (typeof window !== 'undefined') {
// Boot-time env snapshot targets candidates #2 (React prod mode) and #3 (tree-shaking removed side-effectful import): we log NODE_ENV, React version, presence of Emotion's cache stylesheet, and any pre-existing emotion/MUI globals so a post-crash trace shows whether the runtime env matched what the bundle expected.
try {
// eslint-disable-next-line no-console
console.log('[diag][env] NODE_ENV=', (typeof process !== 'undefined' && process.env && process.env.NODE_ENV) || 'undefined',
'React=', (React as any).version,
'origin=', window.location.origin,
'href=', window.location.href);
setTimeout(() => {
try {
const emotionStyles = document.querySelectorAll('style[data-emotion]');
const muiStyles = document.querySelectorAll('style[data-styled],style[data-mui]');
// eslint-disable-next-line no-console
console.log('[diag][env] emotion_styles=', emotionStyles.length, 'mui_styles=', muiStyles.length, 'all_styles=', document.styleSheets.length);
} catch (err) {
// eslint-disable-next-line no-console
console.error('[diag][env] style probe failed:', err && (err as Error).message);
}
}, 100);
} catch { /* never let the diag block crash */ }
// Diagnostic global error capture. The packaged bundle has no source maps, so without these handlers the only thing that reaches main-process stderr is "Uncaught TypeError: ... (bundle.js:2)" with zero stack context. Forward error.stack and Redux action.type when available so we can pinpoint the offender across the chat-spawn / workflow rendering paths even in minified prod.
window.addEventListener('error', (e) => {
try {
@@ -39,7 +39,8 @@ class ErrorBoundary extends React.Component<Props, State> {
} catch {}
try { this.props.onError?.(error, info); } catch {}
if (typeof console !== 'undefined' && console.error) {
console.error('[ErrorBoundary]', error, info);
// [diag] prefix so the packaged-build stderr monitor picks this up alongside other diag traces (the renderer-side crash we are hunting does not always reach window.onerror, so an in-React-tree throw needs its own visible breadcrumb).
console.error('[diag][ErrorBoundary]', this.props.scope || 'unknown', error && error.message, '\nstack:\n', error && error.stack, '\ncomponent_stack:\n', info && info.componentStack);
}
}