From b4da5bffcfe272fb5593a8953c83fb7a70d862c5 Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Mon, 20 Apr 2026 19:43:09 -0400 Subject: [PATCH] feat(ext): split PopupMessage / ContentMessage unions + capability sets --- extension/src/shared/messages.ts | 94 +++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/extension/src/shared/messages.ts b/extension/src/shared/messages.ts index 72b4213..55a320a 100644 --- a/extension/src/shared/messages.ts +++ b/extension/src/shared/messages.ts @@ -1,68 +1,78 @@ -import type { Entry, Manifest, ManifestEntry, VaultConfig, SetupState } from './types'; +import type { + Item, ItemId, Manifest, ManifestEntry, VaultConfig, SetupState, + DeviceSettings, GeneratorRequest, +} from './types'; -// --- Request types (popup/content -> service worker) --- +// --- Messages a popup (or setup page) may send --- -export type Request = +export type PopupMessage = + | { type: 'is_unlocked' } | { type: 'unlock'; passphrase: string } | { type: 'lock' } - | { type: 'is_unlocked' } - | { type: 'list_entries'; group?: string } - | { type: 'get_entry'; id: string } - | { type: 'search_entries'; query: string } - | { type: 'add_entry'; entry: Entry } - | { type: 'update_entry'; id: string; entry: Entry } - | { type: 'delete_entry'; id: string } - | { type: 'get_totp'; id: string } - | { type: 'get_autofill_candidates'; url: string } - | { type: 'get_credentials'; id: string } + | { type: 'list_items'; group?: string } + | { type: 'get_item'; id: ItemId } + | { type: 'add_item'; item: Item } + | { type: 'update_item'; id: ItemId; item: Item } + | { type: 'delete_item'; id: ItemId } // soft-delete + | { type: 'get_totp'; id: ItemId } | { type: 'sync' } | { type: 'get_setup_state' } | { type: 'save_setup'; config: VaultConfig; imageBase64: string } - | { type: 'generate_password'; length: number } - | { type: 'fill_credentials'; username: string; password: string } - | { type: 'check_credential'; url: string; username: string; password: string } - | { type: 'blacklist_site'; hostname: string } + | { type: 'rate_passphrase'; passphrase: string } + | { type: 'generate_password'; request: GeneratorRequest } + | { type: 'fill_credentials'; id: ItemId; capturedTabId: number; capturedUrl: string } + | { type: 'ack_autofill_origin'; hostname: string } | { type: 'get_settings' } - | { type: 'update_settings'; settings: Partial } + | { type: 'update_settings'; settings: Partial } | { type: 'get_blacklist' } | { type: 'remove_blacklist'; hostname: string }; -// --- Response types (service worker -> popup/content) --- +// --- Messages a content script may send --- + +// Note deliberate absence of a `url` field — the SW derives origin from sender.tab.url. + +export type ContentMessage = + | { type: 'get_autofill_candidates' } + | { type: 'get_credentials'; id: ItemId } + | { type: 'check_credential'; username: string; password: string } + | { type: 'blacklist_site' }; + +// --- Union for chrome.runtime.sendMessage call sites --- + +export type Request = PopupMessage | ContentMessage; + +// --- Response --- export type Response = | { ok: true; data?: unknown } | { ok: false; error: string }; -export interface UnlockResponse extends Extract { - data: undefined; -} +// --- Typed response helpers --- export interface IsUnlockedResponse extends Extract { data: { unlocked: boolean }; } -export interface ListEntriesResponse extends Extract { - data: { entries: Array<[string, ManifestEntry]> }; +export interface ListItemsResponse extends Extract { + data: { items: Array<[ItemId, ManifestEntry]> }; } -export interface GetEntryResponse extends Extract { - data: { entry: Entry }; -} - -export interface SearchEntriesResponse extends Extract { - data: { entries: Array<[string, ManifestEntry]> }; +export interface GetItemResponse extends Extract { + data: { item: Item }; } export interface TotpResponse extends Extract { - data: { code: string; remaining_seconds: number }; + data: { code: string; expires_at: number }; } export interface AutofillCandidatesResponse extends Extract { - data: { candidates: Array<[string, ManifestEntry]> }; + data: { candidates: Array<[ItemId, ManifestEntry]> }; } export interface CredentialsResponse extends Extract { - data: { username: string; password: string }; + data: + | { requires_ack: true; hostname: string } + | { username: string; password: string }; } export interface SetupStateResponse extends Extract { @@ -72,3 +82,21 @@ export interface SetupStateResponse extends Extract { export interface GeneratePasswordResponse extends Extract { data: { password: string }; } + +export interface RatePassphraseResponse extends Extract { + data: { score: number; guesses_log10: number }; +} + +// --- Capability sets (consumed by the router) --- + +export const POPUP_ONLY_TYPES: ReadonlySet = new Set([ + 'is_unlocked', 'unlock', 'lock', 'list_items', 'get_item', 'add_item', + 'update_item', 'delete_item', 'get_totp', 'sync', 'get_setup_state', + 'save_setup', 'rate_passphrase', 'generate_password', 'fill_credentials', + 'ack_autofill_origin', 'get_settings', 'update_settings', 'get_blacklist', + 'remove_blacklist', +] as PopupMessage['type'][]); + +export const CONTENT_CALLABLE_TYPES: ReadonlySet = new Set([ + 'get_autofill_candidates', 'get_credentials', 'check_credential', 'blacklist_site', +] as ContentMessage['type'][]);