diff --git a/package-lock.json b/package-lock.json index 9c91664..26a975e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "proofsnap-extension", - "version": "1.1.1", + "version": "1.1.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "proofsnap-extension", - "version": "1.1.1", + "version": "1.1.4", "dependencies": { "react": "^19.0.0", "react-dom": "^19.0.0" @@ -49,7 +49,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -1156,7 +1155,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "dev": true, - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -1267,7 +1265,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -1709,7 +1706,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "peer": true, "engines": { "node": ">=12" }, @@ -1769,7 +1765,6 @@ "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2000,7 +1995,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", "dev": true, - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/src/background/service-worker.ts b/src/background/service-worker.ts index 479f27b..f7047ca 100644 --- a/src/background/service-worker.ts +++ b/src/background/service-worker.ts @@ -14,8 +14,8 @@ console.log('ProofSnap background service worker loaded'); const assetStorage = indexedDBService; const metadataStorage = storageService; -// Initialize storage services -Promise.all([ +// Readiness gate: all message handlers must await this before accessing storage +const initPromise = Promise.all([ assetStorage.init(), metadataStorage.init() ]).then(async () => { @@ -96,7 +96,10 @@ chrome.runtime.onMessage.addListener((message: ExtensionMessage, _sender, sendRe switch (message.type) { case 'CAPTURE_SCREENSHOT': - handleScreenshotCaptureMessage(message as CaptureScreenshotMessage) + (async () => { + await initPromise; + return handleScreenshotCaptureMessage(message as CaptureScreenshotMessage); + })() .then((result) => { if (result && result.cancelled) { sendResponse({ success: false, cancelled: true }); @@ -108,7 +111,10 @@ chrome.runtime.onMessage.addListener((message: ExtensionMessage, _sender, sendRe return true; // Keep channel open for async response case 'UPLOAD_ASSET': - handleAssetUpload(message.payload) + (async () => { + await initPromise; + return handleAssetUpload(message.payload); + })() .then(() => sendResponse({ success: true })) .catch((error) => sendResponse({ success: false, error: error.message })); return true; @@ -117,6 +123,7 @@ chrome.runtime.onMessage.addListener((message: ExtensionMessage, _sender, sendRe console.log('Starting Google Auth in background...'); (async () => { try { + await initPromise; const numbersApi = await getNumbersApi(); // 1. Get ID Token via Chrome Identity (interactive flow) @@ -140,7 +147,10 @@ chrome.runtime.onMessage.addListener((message: ExtensionMessage, _sender, sendRe case 'SELECTION_COMPLETE': // Handle selection complete from content script - handleSelectionComplete(message.payload); + (async () => { + await initPromise; + await handleSelectionComplete(message.payload); + })().catch((error) => console.error('Error handling SELECTION_COMPLETE:', error)); sendResponse({ success: true }); return false; @@ -185,6 +195,13 @@ async function handleSelectionCapture(tab: chrome.tabs.Tab): Promise { // Wait for selection to complete via message return new Promise((resolve, reject) => { + // If there is already a pending selection, cancel it before starting a new one + if (pendingSelectionReject) { + pendingSelectionReject(new Error('Selection cancelled: a new selection was started')); + pendingSelectionResolve = null; + pendingSelectionReject = null; + } + pendingSelectionResolve = resolve; pendingSelectionReject = reject; diff --git a/src/services/NumbersApiManager.ts b/src/services/NumbersApiManager.ts index d95aa29..bdbc05a 100644 --- a/src/services/NumbersApiManager.ts +++ b/src/services/NumbersApiManager.ts @@ -87,6 +87,7 @@ export class NumbersApiManager { async clearAuth(): Promise { await this.auth.clearAuth(); await storageService.clearAuth(); + resetInstance(); } /** @@ -150,10 +151,22 @@ export class NumbersApiManager { // Lazy singleton pattern let instance: NumbersApiManager | null = null; +export function resetInstance(): void { + instance = null; +} + export async function getNumbersApi(): Promise { if (!instance) { - instance = new NumbersApiManager(); - await instance.initialize(); + const manager = new NumbersApiManager(); + instance = manager; + await manager.initialize(); + // If clearAuth() was called during initialize (stale/invalid token), + // instance was reset; provide a fresh unauthenticated manager. + if (!instance) { + const freshManager = new NumbersApiManager(); + instance = freshManager; + await freshManager.initialize(); + } } return instance; }