/
mentoster
/
vk-stilus
Обзор
Документация
Войти
/
mentoster
/
vk-stilus
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
3
CI/CD
Аналитика
Безопасность
master
src/background/reset-controller.ts
120 строк
5 KB
mentoster
refactor(background): type privileged lifecycle owners
20 июл 2026, 22:02
20 июл 2026, 22:02
7c5dffb
Код
Авторство
О чём код?
import { verifyInvariant } from '../core/invariant.js'; import { err, ok } from '../core/result.js'; import type { BackgroundBrowserApi, BackgroundResult, BackgroundSchedule, TabActionOwner, } from './background-types.js'; import { PAGE_ACTION_IDS } from './page-actions.js'; const RESET_CONTEXT = Object.freeze({ owner: 'ResetController' }); const RESET_DELAY_MS = 100; /** @assertion-exception Pure reset error factory owns no lifecycle state. */ function resetError(code: string, message: string) { return Object.freeze({ code, message }); } /** @assertion-exception Browser timer adapter owns no reusable state. */ const defaultSchedule: BackgroundSchedule = (callback, delay) => setTimeout(callback, delay); /** * @classdesc Owns page reset and full application reset sequencing. * @responsibility Invoke fixed page cleanup, clear only extension local/sync stores, and reload the extension. * @nonresponsibility Clear unrelated browsing/site data, choose menu labels, or accept arbitrary scripts. * @trustBoundary Validates checked page-action/storage/runtime Results and isolates delayed reset failures. * @lifecycle Construct once; at most one application reset timer may be scheduled. * @invariant Reset delay remains 100 ms and only local/sync extension stores are cleared. * @resourceBudget One scheduled reset callback and at most three browser operations per application reset. * @sideEffects Clears documented page-local data, extension storage, and reloads the extension runtime. */ export class ResetController { browserApi: BackgroundBrowserApi; tabController: TabActionOwner; schedule: BackgroundSchedule; scheduledReset: unknown; lastError: unknown; constructor( options: Readonly<{ browserApi: BackgroundBrowserApi; tabController: TabActionOwner; schedule?: BackgroundSchedule; }>, ) { this.browserApi = options.browserApi; this.tabController = options.tabController; this.schedule = options.schedule ?? defaultSchedule; this.scheduledReset = null; this.lastError = null; const storageApi = verifyInvariant( typeof this.browserApi.clearStorage === 'function' && typeof this.browserApi.reload === 'function', 'reset.construct.storage-api', RESET_CONTEXT, ); const actionApi = verifyInvariant( typeof this.tabController.executePageAction === 'function', 'reset.construct.action-api', RESET_CONTEXT, ); if (!storageApi.ok || !actionApi.ok) throw new TypeError('ResetController unavailable'); } resetPage(): Promise<BackgroundResult<unknown>> { return this.tabController.executePageAction(PAGE_ACTION_IDS.RESET, null, { scope: 'all' }); } async resetApplication(): Promise<BackgroundResult<Readonly<{ scheduled: true }>>> { if (this.scheduledReset !== null) { return err(resetError('RESET_ALREADY_SCHEDULED', 'application reset is already scheduled')); } const pageReset = await this.tabController.executePageAction( PAGE_ACTION_IDS.APP_RESET, null, { scope: 'first' }, ); if (!pageReset.ok) return pageReset; const callback = this.handleScheduledResetListener.bind(this); this.scheduledReset = this.schedule(callback, RESET_DELAY_MS); const delayStable = verifyInvariant( RESET_DELAY_MS === 100, 'reset.schedule.delay', RESET_CONTEXT, ); const scheduled = verifyInvariant( this.scheduledReset !== null, 'reset.schedule.registered', RESET_CONTEXT, ); if (!delayStable.ok) return err(delayStable.error); return scheduled.ok ? ok(Object.freeze({ scheduled: true as const })) : err(scheduled.error); } /** @assertion-exception Scheduled callback delegates to one checked async reset handler. */ handleScheduledResetListener(): void { void this.handleScheduledReset(); } /** @assertion-exception Scheduled reset wrapper stores one checked diagnostic. */ async handleScheduledReset(): Promise<BackgroundResult<Readonly<{ cleared: true }>>> { const result = await this.clearAndReload(); this.lastError = result.ok ? null : result.error; this.scheduledReset = null; return result; } async clearAndReload(): Promise<BackgroundResult<Readonly<{ cleared: true }>>> { const local = await this.browserApi.clearStorage('local'); if (!local.ok) return local; const sync = await this.browserApi.clearStorage('sync'); if (!sync.ok) return sync; const reloaded = await this.browserApi.reload(); const localCleared = verifyInvariant(local.ok, 'reset.clear.local', RESET_CONTEXT); const syncCleared = verifyInvariant(sync.ok, 'reset.clear.sync', RESET_CONTEXT); if (!localCleared.ok) return err(localCleared.error); if (!syncCleared.ok) return err(syncCleared.error); return reloaded.ok ? ok(Object.freeze({ cleared: true as const })) : reloaded; } }