Skip to content

Commit 728a599

Browse files
author
Sentience Dev
committed
Merge pull request #119 from SentienceAPI/sync-extension-v2.1.0
Sync Extension: v2.1.0
2 parents 9600118 + 16fd9de commit 728a599

File tree

7 files changed

+1928
-1775
lines changed

7 files changed

+1928
-1775
lines changed

sentience/extension/background.js

Lines changed: 175 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
// Sentience Chrome Extension - Background Service Worker
2+
// Auto-generated from modular source
3+
import init, { analyze_page_with_options, analyze_page, prune_for_api } from '../pkg/sentience_core.js';
4+
15
// background.js - Service Worker with WASM (CSP-Immune!)
26
// This runs in an isolated environment, completely immune to page CSP policies
37

4-
// ✅ STATIC IMPORTS at top level - Required for Service Workers!
5-
// Dynamic import() is FORBIDDEN in ServiceWorkerGlobalScope
6-
import init, { analyze_page, analyze_page_with_options, prune_for_api } from './pkg/sentience_core.js';
78

89
console.log('[Sentience Background] Initializing...');
910

@@ -16,120 +17,121 @@ let wasmInitPromise = null;
1617
* Uses static imports (not dynamic import()) which is required for Service Workers
1718
*/
1819
async function initWASM() {
19-
if (wasmReady) return;
20-
if (wasmInitPromise) return wasmInitPromise;
20+
if (wasmReady) return;
21+
if (wasmInitPromise) return wasmInitPromise;
2122

22-
wasmInitPromise = (async () => {
23-
try {
24-
console.log('[Sentience Background] Loading WASM module...');
25-
26-
// Define the js_click_element function that WASM expects
27-
// In Service Workers, use 'globalThis' instead of 'window'
28-
// In background context, we can't actually click, so we log a warning
29-
globalThis.js_click_element = (_id) => {
30-
console.warn('[Sentience Background] js_click_element called in background (ignored)');
31-
};
32-
33-
// Initialize WASM - this calls the init() function from the static import
34-
// The init() function handles fetching and instantiating the .wasm file
35-
await init();
36-
37-
wasmReady = true;
38-
console.log('[Sentience Background] ✓ WASM ready!');
39-
console.log('[Sentience Background] Available functions: analyze_page, analyze_page_with_options, prune_for_api');
40-
} catch (error) {
41-
console.error('[Sentience Background] WASM initialization failed:', error);
42-
throw error;
43-
}
44-
})();
23+
wasmInitPromise = (async () => {
24+
try {
25+
console.log('[Sentience Background] Loading WASM module...');
26+
27+
// Define the js_click_element function that WASM expects
28+
// In Service Workers, use 'globalThis' instead of 'window'
29+
// In background context, we can't actually click, so we log a warning
30+
globalThis.js_click_element = () => {
31+
console.warn('[Sentience Background] js_click_element called in background (ignored)');
32+
};
33+
34+
// Initialize WASM - this calls the init() function from the static import
35+
// The init() function handles fetching and instantiating the .wasm file
36+
await init();
37+
38+
wasmReady = true;
39+
console.log('[Sentience Background] ✓ WASM ready!');
40+
console.log(
41+
'[Sentience Background] Available functions: analyze_page, analyze_page_with_options, prune_for_api'
42+
);
43+
} catch (error) {
44+
console.error('[Sentience Background] WASM initialization failed:', error);
45+
throw error;
46+
}
47+
})();
4548

46-
return wasmInitPromise;
49+
return wasmInitPromise;
4750
}
4851

4952
// Initialize WASM on service worker startup
50-
initWASM().catch(err => {
51-
console.error('[Sentience Background] Failed to initialize WASM:', err);
53+
initWASM().catch((err) => {
54+
console.error('[Sentience Background] Failed to initialize WASM:', err);
5255
});
5356

5457
/**
5558
* Message handler for all extension communication
5659
* Includes global error handling to prevent extension crashes
5760
*/
5861
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
59-
// Global error handler to prevent extension crashes
60-
try {
61-
// Handle screenshot requests (existing functionality)
62-
if (request.action === 'captureScreenshot') {
63-
handleScreenshotCapture(sender.tab.id, request.options)
64-
.then(screenshot => {
65-
sendResponse({ success: true, screenshot });
66-
})
67-
.catch(error => {
68-
console.error('[Sentience Background] Screenshot capture failed:', error);
69-
sendResponse({
70-
success: false,
71-
error: error.message || 'Screenshot capture failed'
72-
});
73-
});
74-
return true; // Async response
75-
}
62+
// Global error handler to prevent extension crashes
63+
try {
64+
// Handle screenshot requests (existing functionality)
65+
if (request.action === 'captureScreenshot') {
66+
handleScreenshotCapture(sender.tab.id, request.options)
67+
.then((screenshot) => {
68+
sendResponse({ success: true, screenshot });
69+
})
70+
.catch((error) => {
71+
console.error('[Sentience Background] Screenshot capture failed:', error);
72+
sendResponse({
73+
success: false,
74+
error: error.message || 'Screenshot capture failed',
75+
});
76+
});
77+
return true; // Async response
78+
}
7679

77-
// Handle WASM processing requests (NEW!)
78-
if (request.action === 'processSnapshot') {
79-
handleSnapshotProcessing(request.rawData, request.options)
80-
.then(result => {
81-
sendResponse({ success: true, result });
82-
})
83-
.catch(error => {
84-
console.error('[Sentience Background] Snapshot processing failed:', error);
85-
sendResponse({
86-
success: false,
87-
error: error.message || 'Snapshot processing failed'
88-
});
89-
});
90-
return true; // Async response
91-
}
80+
// Handle WASM processing requests (NEW!)
81+
if (request.action === 'processSnapshot') {
82+
handleSnapshotProcessing(request.rawData, request.options)
83+
.then((result) => {
84+
sendResponse({ success: true, result });
85+
})
86+
.catch((error) => {
87+
console.error('[Sentience Background] Snapshot processing failed:', error);
88+
sendResponse({
89+
success: false,
90+
error: error.message || 'Snapshot processing failed',
91+
});
92+
});
93+
return true; // Async response
94+
}
9295

93-
// Unknown action
94-
console.warn('[Sentience Background] Unknown action:', request.action);
95-
sendResponse({ success: false, error: 'Unknown action' });
96-
return false;
97-
} catch (error) {
98-
// Catch any synchronous errors that might crash the extension
99-
console.error('[Sentience Background] Fatal error in message handler:', error);
100-
try {
101-
sendResponse({
102-
success: false,
103-
error: `Fatal error: ${error.message || 'Unknown error'}`
104-
});
105-
} catch (e) {
106-
// If sendResponse already called, ignore
107-
}
108-
return false;
96+
// Unknown action
97+
console.warn('[Sentience Background] Unknown action:', request.action);
98+
sendResponse({ success: false, error: 'Unknown action' });
99+
return false;
100+
} catch (error) {
101+
// Catch any synchronous errors that might crash the extension
102+
console.error('[Sentience Background] Fatal error in message handler:', error);
103+
try {
104+
sendResponse({
105+
success: false,
106+
error: `Fatal error: ${error.message || 'Unknown error'}`,
107+
});
108+
} catch (e) {
109+
// If sendResponse already called, ignore
109110
}
111+
return false;
112+
}
110113
});
111114

112115
/**
113116
* Handle screenshot capture (existing functionality)
114117
*/
115118
async function handleScreenshotCapture(_tabId, options = {}) {
116-
try {
117-
const {
118-
format = 'png',
119-
quality = 90
120-
} = options;
121-
122-
const dataUrl = await chrome.tabs.captureVisibleTab(null, {
123-
format: format,
124-
quality: quality
125-
});
126-
127-
console.log(`[Sentience Background] Screenshot captured: ${format}, size: ${dataUrl.length} bytes`);
128-
return dataUrl;
129-
} catch (error) {
130-
console.error('[Sentience Background] Screenshot error:', error);
131-
throw new Error(`Failed to capture screenshot: ${error.message}`);
132-
}
119+
try {
120+
const { format = 'png', quality = 90 } = options;
121+
122+
const dataUrl = await chrome.tabs.captureVisibleTab(null, {
123+
format,
124+
quality,
125+
});
126+
127+
console.log(
128+
`[Sentience Background] Screenshot captured: ${format}, size: ${dataUrl.length} bytes`
129+
);
130+
return dataUrl;
131+
} catch (error) {
132+
console.error('[Sentience Background] Screenshot error:', error);
133+
throw new Error(`Failed to capture screenshot: ${error.message}`);
134+
}
133135
}
134136

135137
/**
@@ -142,92 +144,99 @@ async function handleScreenshotCapture(_tabId, options = {}) {
142144
* @returns {Promise<Object>} Processed snapshot result
143145
*/
144146
async function handleSnapshotProcessing(rawData, options = {}) {
145-
const MAX_ELEMENTS = 10000; // Safety limit to prevent hangs
146-
const startTime = performance.now();
147-
148-
try {
149-
// Safety check: limit element count to prevent hangs
150-
if (!Array.isArray(rawData)) {
151-
throw new Error('rawData must be an array');
152-
}
153-
154-
if (rawData.length > MAX_ELEMENTS) {
155-
console.warn(`[Sentience Background] ⚠️ Large dataset: ${rawData.length} elements. Limiting to ${MAX_ELEMENTS} to prevent hangs.`);
156-
rawData = rawData.slice(0, MAX_ELEMENTS);
157-
}
147+
const MAX_ELEMENTS = 10000; // Safety limit to prevent hangs
148+
const startTime = performance.now();
158149

159-
// Ensure WASM is initialized
160-
await initWASM();
161-
if (!wasmReady) {
162-
throw new Error('WASM module not initialized');
163-
}
150+
try {
151+
// Safety check: limit element count to prevent hangs
152+
if (!Array.isArray(rawData)) {
153+
throw new Error('rawData must be an array');
154+
}
164155

165-
console.log(`[Sentience Background] Processing ${rawData.length} elements with options:`, options);
156+
if (rawData.length > MAX_ELEMENTS) {
157+
console.warn(
158+
`[Sentience Background] ⚠️ Large dataset: ${rawData.length} elements. Limiting to ${MAX_ELEMENTS} to prevent hangs.`
159+
);
160+
rawData = rawData.slice(0, MAX_ELEMENTS);
161+
}
166162

167-
// Run WASM processing using the imported functions directly
168-
// Wrap in try-catch with timeout protection
169-
let analyzedElements;
170-
try {
171-
// Use a timeout wrapper to prevent infinite hangs
172-
const wasmPromise = new Promise((resolve, reject) => {
173-
try {
174-
let result;
175-
if (options.limit || options.filter) {
176-
result = analyze_page_with_options(rawData, options);
177-
} else {
178-
result = analyze_page(rawData);
179-
}
180-
resolve(result);
181-
} catch (e) {
182-
reject(e);
183-
}
184-
});
185-
186-
// Add timeout protection (18 seconds - less than content.js timeout)
187-
analyzedElements = await Promise.race([
188-
wasmPromise,
189-
new Promise((_, reject) =>
190-
setTimeout(() => reject(new Error('WASM processing timeout (>18s)')), 18000)
191-
)
192-
]);
193-
} catch (e) {
194-
const errorMsg = e.message || 'Unknown WASM error';
195-
console.error(`[Sentience Background] WASM analyze_page failed: ${errorMsg}`, e);
196-
throw new Error(`WASM analyze_page failed: ${errorMsg}`);
197-
}
163+
// Ensure WASM is initialized
164+
await initWASM();
165+
if (!wasmReady) {
166+
throw new Error('WASM module not initialized');
167+
}
198168

199-
// Prune elements for API (prevents 413 errors on large sites)
200-
let prunedRawData;
169+
console.log(
170+
`[Sentience Background] Processing ${rawData.length} elements with options:`,
171+
options
172+
);
173+
174+
// Run WASM processing using the imported functions directly
175+
// Wrap in try-catch with timeout protection
176+
let analyzedElements;
177+
try {
178+
// Use a timeout wrapper to prevent infinite hangs
179+
const wasmPromise = new Promise((resolve, reject) => {
201180
try {
202-
prunedRawData = prune_for_api(rawData);
181+
let result;
182+
if (options.limit || options.filter) {
183+
result = analyze_page_with_options(rawData, options);
184+
} else {
185+
result = analyze_page(rawData);
186+
}
187+
resolve(result);
203188
} catch (e) {
204-
console.warn('[Sentience Background] prune_for_api failed, using original data:', e);
205-
prunedRawData = rawData;
189+
reject(e);
206190
}
191+
});
192+
193+
// Add timeout protection (18 seconds - less than content.js timeout)
194+
analyzedElements = await Promise.race([
195+
wasmPromise,
196+
new Promise((_, reject) =>
197+
setTimeout(() => reject(new Error('WASM processing timeout (>18s)')), 18000)
198+
),
199+
]);
200+
} catch (e) {
201+
const errorMsg = e.message || 'Unknown WASM error';
202+
console.error(`[Sentience Background] WASM analyze_page failed: ${errorMsg}`, e);
203+
throw new Error(`WASM analyze_page failed: ${errorMsg}`);
204+
}
207205

208-
const duration = performance.now() - startTime;
209-
console.log(`[Sentience Background] ✓ Processed: ${analyzedElements.length} analyzed, ${prunedRawData.length} pruned (${duration.toFixed(1)}ms)`);
210-
211-
return {
212-
elements: analyzedElements,
213-
raw_elements: prunedRawData
214-
};
215-
} catch (error) {
216-
const duration = performance.now() - startTime;
217-
console.error(`[Sentience Background] Processing error after ${duration.toFixed(1)}ms:`, error);
218-
throw error;
206+
// Prune elements for API (prevents 413 errors on large sites)
207+
let prunedRawData;
208+
try {
209+
prunedRawData = prune_for_api(rawData);
210+
} catch (e) {
211+
console.warn('[Sentience Background] prune_for_api failed, using original data:', e);
212+
prunedRawData = rawData;
219213
}
214+
215+
const duration = performance.now() - startTime;
216+
console.log(
217+
`[Sentience Background] ✓ Processed: ${analyzedElements.length} analyzed, ${prunedRawData.length} pruned (${duration.toFixed(1)}ms)`
218+
);
219+
220+
return {
221+
elements: analyzedElements,
222+
raw_elements: prunedRawData,
223+
};
224+
} catch (error) {
225+
const duration = performance.now() - startTime;
226+
console.error(`[Sentience Background] Processing error after ${duration.toFixed(1)}ms:`, error);
227+
throw error;
228+
}
220229
}
221230

222231
console.log('[Sentience Background] Service worker ready');
223232

224233
// Global error handlers to prevent extension crashes
225234
self.addEventListener('error', (event) => {
226-
console.error('[Sentience Background] Global error caught:', event.error);
227-
event.preventDefault(); // Prevent extension crash
235+
console.error('[Sentience Background] Global error caught:', event.error);
236+
event.preventDefault(); // Prevent extension crash
228237
});
229238

230239
self.addEventListener('unhandledrejection', (event) => {
231-
console.error('[Sentience Background] Unhandled promise rejection:', event.reason);
232-
event.preventDefault(); // Prevent extension crash
240+
console.error('[Sentience Background] Unhandled promise rejection:', event.reason);
241+
event.preventDefault(); // Prevent extension crash
233242
});

0 commit comments

Comments
 (0)