refactor(ext/shared): typed StateHost + sweep as-any casts (Plan C Phase 1)
Replaces the previously any-typed StateHost contract with a typed interface. Adds double-registration guard and __resetHostForTests for vitest. sendMessage wrapper is currently a pass-through; Phase 4 will fill its body with the vault_locked intercept lifted from vault.ts. Widens PopupState/View on shared/popup-state.ts to cover vault-tab-only views (history, backup, import) and vault-tab-only fields (unlocked, drawerOpen, typePanelOpen) so VaultState satisfies StateHost.getState() without a cast. The popup surface ignores the new optional fields. Drops the `any` annotations on vault.ts's registerHost callbacks now that the typed StateHost contract infers them from PopupState. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,21 @@
|
||||
/// Service-locator for cross-bundle state access.
|
||||
///
|
||||
/// Both popup.ts and vault.ts register themselves as the "host".
|
||||
/// All popup components import from here instead of from popup.ts,
|
||||
/// so the same component code works in either bundle.
|
||||
// extension/src/shared/state.ts
|
||||
//
|
||||
// Single channel for popup and vault-tab UI to read/write app state and
|
||||
// dispatch messages to the service worker. Two registered hosts (popup,
|
||||
// vault tab) implement StateHost; each surface calls registerHost(this) at
|
||||
// boot.
|
||||
//
|
||||
// The vault_locked intercept (lines 47-74 in vault.ts pre-Phase-4) lifts
|
||||
// into sendMessage() here in Phase 4. Phase 1 lays the wrapper signature;
|
||||
// the body is a thin pass-through until Phase 4.
|
||||
|
||||
import type { Request, Response } from './messages';
|
||||
import type { PopupState, View } from './popup-state';
|
||||
|
||||
export interface StateHost {
|
||||
getState(): any;
|
||||
setState(partial: any): void;
|
||||
navigate(view: string, extras?: any): void;
|
||||
getState(): PopupState;
|
||||
setState(partial: Partial<PopupState>): void;
|
||||
navigate(view: View, extras?: Partial<PopupState>): void;
|
||||
sendMessage(request: Request): Promise<Response>;
|
||||
escapeHtml(s: string): string;
|
||||
popOutToTab(): void;
|
||||
@@ -19,24 +25,36 @@ export interface StateHost {
|
||||
|
||||
let host: StateHost | null = null;
|
||||
|
||||
export function registerHost(h: StateHost): void { host = h; }
|
||||
export function registerHost(h: StateHost): void {
|
||||
if (host) throw new Error('state host already registered');
|
||||
host = h;
|
||||
}
|
||||
|
||||
export function getState(): any {
|
||||
/** Test-only — vitest beforeEach() calls this to break inter-test leakage. */
|
||||
export function __resetHostForTests(): void {
|
||||
host = null;
|
||||
}
|
||||
|
||||
export function getState(): PopupState {
|
||||
if (!host) throw new Error('No state host registered');
|
||||
return host.getState();
|
||||
}
|
||||
|
||||
export function setState(partial: any): void {
|
||||
export function setState(partial: Partial<PopupState>): void {
|
||||
if (!host) throw new Error('No state host registered');
|
||||
host.setState(partial);
|
||||
}
|
||||
|
||||
export function navigate(view: string, extras?: any): void {
|
||||
export function navigate(view: View, extras?: Partial<PopupState>): void {
|
||||
if (!host) throw new Error('No state host registered');
|
||||
host.navigate(view, extras);
|
||||
}
|
||||
|
||||
export function sendMessage(request: Request): Promise<Response> {
|
||||
/**
|
||||
* Phase 4 will add a vault_locked intercept here. For now, this is a pure
|
||||
* pass-through so the signature is stable for Phase 4 to fill.
|
||||
*/
|
||||
export async function sendMessage(request: Request): Promise<Response> {
|
||||
if (!host) throw new Error('No state host registered');
|
||||
return host.sendMessage(request);
|
||||
}
|
||||
@@ -52,7 +70,7 @@ export function popOutToTab(): void {
|
||||
}
|
||||
|
||||
export function isInTab(): boolean {
|
||||
if (!host) return false;
|
||||
if (!host) throw new Error('No state host registered');
|
||||
return host.isInTab();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user