/// Thin service-worker entry: loads WASM, constructs the router state, and /// forwards every message into router/index.route(). import type { Request, Response, SessionTimeoutConfig } from '../shared/messages'; import { CONTENT_CALLABLE_TYPES } from '../shared/messages'; import type { RouterState } from './router/index'; import { route } from './router/index'; import * as vault from './vault'; import { clearCurrent } from './session'; import * as sessionTimer from './session-timer'; // @ts-ignore TS2307 — resolved by webpack alias / copy import initDefault, { initSync } from '../../wasm/relicario_wasm.js'; // @ts-ignore TS2307 import * as wasmBindings from '../../wasm/relicario_wasm.js'; type WasmModule = typeof wasmBindings; let wasm: WasmModule | null = null; async function initWasm(): Promise { if (wasm) return wasm; // eslint-disable-next-line @typescript-eslint/no-explicit-any const SWGlobalScope = (globalThis as any).ServiceWorkerGlobalScope as (new () => ServiceWorker) | undefined; const isServiceWorker = typeof SWGlobalScope !== 'undefined' && self instanceof (SWGlobalScope as unknown as typeof EventTarget); if (isServiceWorker) { const wasmResponse = await fetch(chrome.runtime.getURL('relicario_wasm_bg.wasm')); const wasmBytes = await wasmResponse.arrayBuffer(); initSync({ module: new WebAssembly.Module(wasmBytes) }); } else { const wasmUrl = chrome.runtime.getURL('relicario_wasm_bg.wasm'); await initDefault(wasmUrl); } vault.setWasm(wasmBindings); wasm = wasmBindings; return wasm; } // Single router-state object shared by all messages for this SW instance. const state: RouterState = { manifest: null, gitHost: null, wasm: null, }; // --- Session timer wiring --- sessionTimer.onExpired(() => { // eslint-disable-next-line no-console console.log('[relicario sw] session expired — locking vault'); clearCurrent(); state.manifest = null; // Best-effort broadcast — receiver may not exist yet. chrome.runtime.sendMessage({ type: 'session_expired' }).catch(() => {}); }); // Restore saved session config from chrome.storage.local on SW startup. chrome.storage.local.get('session_timeout').then((r) => { if (r.session_timeout) { sessionTimer.setConfig(r.session_timeout as SessionTimeoutConfig); } }).catch(() => {}); chrome.commands.onCommand.addListener((command) => { if (command === 'open-vault') { chrome.tabs.create({ url: chrome.runtime.getURL('vault.html') }); } }); chrome.runtime.onMessage.addListener( (request: Request, sender: chrome.runtime.MessageSender, sendResponse: (r: Response) => void) => { (async () => { if (!CONTENT_CALLABLE_TYPES.has(request.type as never)) { sessionTimer.resetTimer(); } if (!state.wasm) { // eslint-disable-next-line no-console console.log('[relicario sw] initializing WASM on first message'); state.wasm = await initWasm(); } return route(request, state, sender); })() .then((r) => { if (!r.ok) { // eslint-disable-next-line no-console console.warn(`[relicario sw] ${request.type} -> error:`, r.error); } sendResponse(r); }) .catch((err: Error) => { // eslint-disable-next-line no-console console.error(`[relicario sw] ${request.type} threw:`, err); sendResponse({ ok: false, error: err.message }); }); return true; // async response }, );