feat(robustness+ui): отказоустойчивость main, тесты, a11y-полировка, лицензия
Надёжность main-процесса: - глобальные uncaughtException/unhandledRejection (лог + flushNow) - safeHandle/safeOn вокруг всех IPC-хендлеров (не падаем молча, generic-ошибка наружу) - таймаут 4s на tasklist, Atomics.wait вместо busy-spin на exit-записи - единый log.error для фоновых сбоев вместо console.error/тишины Тесты (178 -> 203): meeting-detect, scheduler-gating, store (миграции/карантин/cap). UI/UX: - prefers-reduced-motion через MotionConfig + CSS media-блок - Spinner/Skeleton примитивы, loading-состояния вместо пустых заглушек - aria-live анонсы достижений и выполнения (useAnnounce) - оформленные пустые состояния, клавиатура в меню ExerciseCard Лицензия: проприетарный LICENSE + правка README/CLAUDE.md, счётчик тестов. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,9 +13,26 @@ import { broadcastState } from './state-actions'
|
||||
import { startGamesRegistry, stopGamesRegistry } from './games/registry'
|
||||
import { initUpdater, stopUpdater } from './updater'
|
||||
import { IPC } from '@shared/ipc'
|
||||
import { log } from './logger'
|
||||
|
||||
const APP_ID = 'com.anril.exercise-reminder'
|
||||
|
||||
// Глобальная сеть безопасности: без этих обработчиков необработанное
|
||||
// исключение/rejection в main-процессе валит приложение молча — пользователь
|
||||
// видит, что окно просто исчезло, а в логах пусто. Логируем всё в latest.log.
|
||||
// uncaughtException дополнительно флашит state, чтобы не потерять данные.
|
||||
process.on('uncaughtException', (err) => {
|
||||
log.error('[fatal] uncaughtException', err)
|
||||
try {
|
||||
flushNow()
|
||||
} catch {
|
||||
// flush сам может бросить (диск/AV) — мы уже в аварийном пути, глушим.
|
||||
}
|
||||
})
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
log.error('[fatal] unhandledRejection', reason)
|
||||
})
|
||||
|
||||
// Must be set BEFORE app.whenReady() for Windows toasts to show
|
||||
// the correct app name / icon in Action Center.
|
||||
app.setAppUserModelId(APP_ID)
|
||||
@@ -38,7 +55,7 @@ if (!gotLock) {
|
||||
|
||||
startScheduler()
|
||||
startGamesRegistry().catch((err) =>
|
||||
console.error('games registry failed:', err)
|
||||
log.error('[index] games registry failed', err)
|
||||
)
|
||||
initUpdater()
|
||||
|
||||
@@ -88,7 +105,7 @@ if (!gotLock) {
|
||||
try {
|
||||
await stopGamesRegistry()
|
||||
} catch (err) {
|
||||
console.error('[index] stopGamesRegistry threw:', err)
|
||||
log.error('[index] stopGamesRegistry threw', err)
|
||||
}
|
||||
flushNow()
|
||||
app.exit(0)
|
||||
|
||||
Reference in New Issue
Block a user