From a7670547fd9146ed9fc90520eeadf048957de287 Mon Sep 17 00:00:00 2001 From: Jordan Wages Date: Wed, 25 Jun 2025 01:51:31 -0500 Subject: [PATCH] Fix initialization and remove Services dependency --- background.js | 32 ++++++++++++++++++-------------- modules/AiClassifier.js | 31 ++++++++++++++++++++++++++++--- options/options.js | 2 +- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/background.js b/background.js index a51028a..bd6f28d 100644 --- a/background.js +++ b/background.js @@ -18,30 +18,32 @@ async function sha256Hex(str) { const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(str)); return Array.from(new Uint8Array(buf), b => b.toString(16).padStart(2, '0')).join(''); } -// Startup + (async () => { logger = await import(browser.runtime.getURL("logger.js")); - logger.aiLog("background.js loaded – ready to classify", {debug: true}); try { - AiClassifier = await import(browser.runtime.getURL('modules/AiClassifier.js')); + AiClassifier = await import(browser.runtime.getURL("modules/AiClassifier.js")); logger.aiLog("AiClassifier imported", {debug: true}); } catch (e) { - logger.aiLog("failed to import AiClassifier", {level: 'error'}, e); + console.error("failed to import AiClassifier", e); + return; } + try { const store = await browser.storage.local.get(["endpoint", "templateName", "customTemplate", "customSystemPrompt", "aiParams", "debugLogging", "aiRules"]); logger.setDebug(store.debugLogging); - AiClassifier.setConfig(store); + await AiClassifier.setConfig(store); aiRules = Array.isArray(store.aiRules) ? store.aiRules : []; logger.aiLog("configuration loaded", {debug: true}, store); } catch (err) { logger.aiLog("failed to load config", {level: 'error'}, err); } -})(); -// Listen for messages from UI/devtools -browser.runtime.onMessage.addListener(async (msg) => { - logger.aiLog("onMessage received", {debug: true}, msg); + logger.aiLog("background.js loaded – ready to classify", {debug: true}); + + // Listen for messages from UI/devtools + browser.runtime.onMessage.addListener(async (msg) => { + logger.aiLog("onMessage received", {debug: true}, msg); if (msg?.type === "aiFilter:test") { const { text = "", criterion = "" } = msg; @@ -104,8 +106,10 @@ window.addEventListener("unhandledrejection", ev => { logger.aiLog("Unhandled promise rejection", {level: 'error'}, ev.reason); }); -browser.runtime.onInstalled.addListener(async ({ reason }) => { - if (reason === "install") { - await browser.runtime.openOptionsPage(); - } -}); + browser.runtime.onInstalled.addListener(async ({ reason }) => { + if (reason === "install") { + await browser.runtime.openOptionsPage(); + } + }); + +})(); diff --git a/modules/AiClassifier.js b/modules/AiClassifier.js index 039d244..fbb0a36 100644 --- a/modules/AiClassifier.js +++ b/modules/AiClassifier.js @@ -1,6 +1,16 @@ "use strict"; import { aiLog, setDebug } from "../logger.js"; -const { Services } = globalThis || ChromeUtils.importESModule("resource://gre/modules/Services.sys.mjs"); + +let Services; +try { + if (typeof globalThis !== "undefined" && globalThis.Services) { + Services = globalThis.Services; + } else if (typeof ChromeUtils !== "undefined" && ChromeUtils.importESModule) { + ({ Services } = ChromeUtils.importESModule("resource://gre/modules/Services.sys.mjs")); + } +} catch (e) { + Services = undefined; +} const SYSTEM_PREFIX = `You are an email-classification assistant. Read the email below and the classification criterion provided by the user. @@ -62,6 +72,9 @@ async function loadCache() { function loadCacheSync() { if (!gCacheLoaded) { + if (!Services?.tm?.spinEventLoopUntil) { + throw new Error("loadCacheSync requires Services"); + } let done = false; loadCache().finally(() => { done = true; }); Services.tm.spinEventLoopUntil(() => done); @@ -95,6 +108,9 @@ async function loadTemplate(name) { } function loadTemplateSync(name) { + if (!Services?.tm?.spinEventLoopUntil) { + throw new Error("loadTemplateSync requires Services"); + } let text = ""; let done = false; loadTemplate(name).then(t => { text = t; }).catch(() => {}).finally(() => { done = true; }); @@ -102,7 +118,7 @@ function loadTemplateSync(name) { return text; } -function setConfig(config = {}) { +async function setConfig(config = {}) { if (config.endpoint) { gEndpoint = config.endpoint; } @@ -125,7 +141,13 @@ function setConfig(config = {}) { if (typeof config.debugLogging === "boolean") { setDebug(config.debugLogging); } - gTemplateText = gTemplateName === "custom" ? gCustomTemplate : loadTemplateSync(gTemplateName); + if (gTemplateName === "custom") { + gTemplateText = gCustomTemplate; + } else if (Services?.tm?.spinEventLoopUntil) { + gTemplateText = loadTemplateSync(gTemplateName); + } else { + gTemplateText = await loadTemplate(gTemplateName); + } aiLog(`[AiClassifier] Endpoint set to ${gEndpoint}`, {debug: true}); aiLog(`[AiClassifier] Template set to ${gTemplateName}`, {debug: true}); } @@ -180,6 +202,9 @@ function cacheResult(cacheKey, matched) { } function classifyTextSync(text, criterion, cacheKey = null) { + if (!Services?.tm?.spinEventLoopUntil) { + throw new Error("classifyTextSync requires Services"); + } const cached = getCachedResult(cacheKey); if (cached !== null) { return cached; diff --git a/options/options.js b/options/options.js index aaa86a8..ecca4a5 100644 --- a/options/options.js +++ b/options/options.js @@ -147,7 +147,7 @@ document.addEventListener('DOMContentLoaded', async () => { })).filter(r => r.criterion); await browser.storage.local.set({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave, debugLogging, aiRules: rules }); try { - AiClassifier.setConfig({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave, debugLogging }); + await AiClassifier.setConfig({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave, debugLogging }); logger.setDebug(debugLogging); } catch (e) { logger.aiLog('[options] failed to apply config', {level: 'error'}, e);