import { renderDevPanel, renderLoggedIn, renderLoggedOut, setProtectedApiResult } from "./view"; import { readAuthConfig, type AuthConfig } from "../shared/auth-config"; import { isAuthResponseMessage, type AuthResponseMessage } from "../shared/auth-messages"; import { createProtectedApiClient } from "../shared/protected-api-client"; interface BootPopupOptions { config?: Partial; document?: Document; fetchProtectedApi?: () => Promise; sendMessage?: (message: unknown) => Promise; } export async function bootPopup(options: BootPopupOptions = {}): Promise { const currentDocument = options.document ?? document; const popupConfig = readAuthConfig(options.config); const root = currentDocument.querySelector("#app"); const HTMLElementCtor = currentDocument.defaultView?.HTMLElement; if (!root || (HTMLElementCtor && !(root instanceof HTMLElementCtor))) { throw new Error("popup root #app is required"); } const sendMessage = options.sendMessage ?? ((message: unknown) => Promise.resolve( ( globalThis as typeof globalThis & { chrome?: { runtime?: { sendMessage?: (payload: unknown) => Promise; }; }; } ).chrome?.runtime?.sendMessage?.(message) )); const fetchProtectedApi = options.fetchProtectedApi ?? createProtectedApiClient({ baseUrl: "http://127.0.0.1:4319", sendMessage }).loadProtectedMockData; await renderCurrentAuthState(root, popupConfig, sendMessage, fetchProtectedApi); } async function renderCurrentAuthState( root: HTMLElement, popupConfig: AuthConfig, sendMessage: (message: unknown) => Promise, fetchProtectedApi: () => Promise ): Promise { const response = await sendMessage({ type: "auth:get-state" }); if (!isAuthResponseMessage(response) || !response.ok || response.type !== "auth:state") { renderLoggedOut(root, "认证状态读取失败"); return; } if (!response.value.isAuthenticated) { renderLoggedOut(root, response.value.lastError); root .querySelector('[data-popup-sign-in="button"]') ?.addEventListener("click", () => { void runAuthAction(root, popupConfig, sendMessage, { actionMessage: { type: "auth:sign-in" }, fetchProtectedApi }); }); return; } renderLoggedIn(root, response.value); root .querySelector('[data-popup-sign-out="button"]') ?.addEventListener("click", () => { void runAuthAction(root, popupConfig, sendMessage, { actionMessage: { type: "auth:sign-out" }, fetchProtectedApi }); }); if (popupConfig.enableDevAuthPanel) { renderDevPanel(root, response.value); root .querySelector('[data-popup-test-protected-api="button"]') ?.addEventListener("click", () => { void runProtectedApiProbe(root, fetchProtectedApi); }); } } async function runAuthAction( root: HTMLElement, popupConfig: AuthConfig, sendMessage: (message: unknown) => Promise, options: { actionMessage: { type: "auth:sign-in" } | { type: "auth:sign-out" }; fetchProtectedApi: () => Promise; } ): Promise { const response = await sendMessage(options.actionMessage); if (isActionError(response)) { renderLoggedOut(root, response.error); root .querySelector('[data-popup-sign-in="button"]') ?.addEventListener("click", () => { void runAuthAction(root, popupConfig, sendMessage, options); }); return; } await renderCurrentAuthState( root, popupConfig, sendMessage, options.fetchProtectedApi ); } function isActionError(response: unknown): response is Extract { return ( isAuthResponseMessage(response) && !response.ok && response.type === "auth:error" ); } async function runProtectedApiProbe( root: HTMLElement, fetchProtectedApi: () => Promise ): Promise { setProtectedApiResult(root, "请求中..."); try { const result = await fetchProtectedApi(); setProtectedApiResult(root, JSON.stringify(result, null, 2)); } catch (error) { setProtectedApiResult( root, error instanceof Error ? error.message : String(error) ); } } if (typeof document !== "undefined") { void bootPopup(); }