11import init , { analyze_page_with_options , analyze_page , prune_for_api } from "../pkg/sentience_core.js" ;
22
3- // background.js - Service Worker with WASM (CSP-Immune!)
4- // This runs in an isolated environment, completely immune to page CSP policies
3+ let wasmReady = ! 1 , wasmInitPromise = null ;
54
6-
7- console . log ( '[Sentience Background] Initializing...' ) ;
8-
9- // Global WASM initialization state
10- let wasmReady = false ;
11- let wasmInitPromise = null ;
12-
13- /**
14- * Initialize WASM module - called once on service worker startup
15- * Uses static imports (not dynamic import()) which is required for Service Workers
16- */
175async function initWASM ( ) {
18- if ( wasmReady ) return ;
19- if ( wasmInitPromise ) return wasmInitPromise ;
6+ if ( ! wasmReady ) return wasmInitPromise || ( wasmInitPromise = ( async ( ) => {
7+ try {
8+ globalThis . js_click_element = ( ) => { } , await init ( ) , wasmReady = ! 0 ;
9+ } catch ( error ) {
10+ throw error ;
11+ }
12+ } ) ( ) , wasmInitPromise ) ;
13+ }
2014
21- wasmInitPromise = ( async ( ) => {
15+ async function handleScreenshotCapture ( _tabId , options = { } ) {
2216 try {
23- console . log ( '[Sentience Background] Loading WASM module...' ) ;
24-
25- // Define the js_click_element function that WASM expects
26- // In Service Workers, use 'globalThis' instead of 'window'
27- // In background context, we can't actually click, so we log a warning
28- globalThis . js_click_element = ( ) => {
29- console . warn ( '[Sentience Background] js_click_element called in background (ignored)' ) ;
30- } ;
31-
32- // Initialize WASM - this calls the init() function from the static import
33- // The init() function handles fetching and instantiating the .wasm file
34- await init ( ) ;
35-
36- wasmReady = true ;
37- console . log ( '[Sentience Background] ✓ WASM ready!' ) ;
38- console . log (
39- '[Sentience Background] Available functions: analyze_page, analyze_page_with_options, prune_for_api'
40- ) ;
17+ const { format : format = "png" , quality : quality = 90 } = options ;
18+ return await chrome . tabs . captureVisibleTab ( null , {
19+ format : format ,
20+ quality : quality
21+ } ) ;
4122 } catch ( error ) {
42- console . error ( '[Sentience Background] WASM initialization failed:' , error ) ;
43- throw error ;
23+ throw new Error ( `Failed to capture screenshot: ${ error . message } ` ) ;
4424 }
45- } ) ( ) ;
46-
47- return wasmInitPromise ;
4825}
4926
50- // Initialize WASM on service worker startup
51- initWASM ( ) . catch ( ( err ) => {
52- console . error ( '[Sentience Background] Failed to initialize WASM:' , err ) ;
53- } ) ;
54-
55- /**
56- * Message handler for all extension communication
57- * Includes global error handling to prevent extension crashes
58- */
59- chrome . runtime . onMessage . addListener ( ( request , sender , sendResponse ) => {
60- // Global error handler to prevent extension crashes
61- try {
62- // Handle screenshot requests (existing functionality)
63- if ( request . action === 'captureScreenshot' ) {
64- handleScreenshotCapture ( sender . tab . id , request . options )
65- . then ( ( screenshot ) => {
66- sendResponse ( { success : true , screenshot } ) ;
67- } )
68- . catch ( ( error ) => {
69- console . error ( '[Sentience Background] Screenshot capture failed:' , error ) ;
70- sendResponse ( {
71- success : false ,
72- error : error . message || 'Screenshot capture failed' ,
73- } ) ;
74- } ) ;
75- return true ; // Async response
76- }
77-
78- // Handle WASM processing requests (NEW!)
79- if ( request . action === 'processSnapshot' ) {
80- handleSnapshotProcessing ( request . rawData , request . options )
81- . then ( ( result ) => {
82- sendResponse ( { success : true , result } ) ;
83- } )
84- . catch ( ( error ) => {
85- console . error ( '[Sentience Background] Snapshot processing failed:' , error ) ;
86- sendResponse ( {
87- success : false ,
88- error : error . message || 'Snapshot processing failed' ,
89- } ) ;
90- } ) ;
91- return true ; // Async response
92- }
93-
94- // Unknown action
95- console . warn ( '[Sentience Background] Unknown action:' , request . action ) ;
96- sendResponse ( { success : false , error : 'Unknown action' } ) ;
97- return false ;
98- } catch ( error ) {
99- // Catch any synchronous errors that might crash the extension
100- console . error ( '[Sentience Background] Fatal error in message handler:' , error ) ;
101- try {
102- sendResponse ( {
103- success : false ,
104- error : `Fatal error: ${ error . message || 'Unknown error' } ` ,
105- } ) ;
106- } catch ( e ) {
107- // If sendResponse already called, ignore
108- }
109- return false ;
110- }
111- } ) ;
112-
113- /**
114- * Handle screenshot capture (existing functionality)
115- */
116- async function handleScreenshotCapture ( _tabId , options = { } ) {
117- try {
118- const { format = 'png' , quality = 90 } = options ;
119-
120- const dataUrl = await chrome . tabs . captureVisibleTab ( null , {
121- format,
122- quality,
123- } ) ;
124-
125- console . log (
126- `[Sentience Background] Screenshot captured: ${ format } , size: ${ dataUrl . length } bytes`
127- ) ;
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- }
133- }
134-
135- /**
136- * Handle snapshot processing with WASM (NEW!)
137- * This is where the magic happens - completely CSP-immune!
138- * Includes safeguards to prevent crashes and hangs.
139- *
140- * @param {Array } rawData - Raw element data from injected_api.js
141- * @param {Object } options - Snapshot options (limit, filter, etc.)
142- * @returns {Promise<Object> } Processed snapshot result
143- */
14427async 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 (
156- `[Sentience Background] ⚠️ Large dataset: ${ rawData . length } elements. Limiting to ${ MAX_ELEMENTS } to prevent hangs.`
157- ) ;
158- rawData = rawData . slice ( 0 , MAX_ELEMENTS ) ;
159- }
160-
161- // Ensure WASM is initialized
162- await initWASM ( ) ;
163- if ( ! wasmReady ) {
164- throw new Error ( 'WASM module not initialized' ) ;
165- }
166-
167- console . log (
168- `[Sentience Background] Processing ${ rawData . length } elements with options:` ,
169- options
170- ) ;
171-
172- // Run WASM processing using the imported functions directly
173- // Wrap in try-catch with timeout protection
174- let analyzedElements ;
28+ const startTime = performance . now ( ) ;
17529 try {
176- // Use a timeout wrapper to prevent infinite hangs
177- const wasmPromise = new Promise ( ( resolve , reject ) => {
30+ if ( ! Array . isArray ( rawData ) ) throw new Error ( "rawData must be an array" ) ;
31+ if ( rawData . length > 1e4 && ( rawData = rawData . slice ( 0 , 1e4 ) ) , await initWASM ( ) ,
32+ ! wasmReady ) throw new Error ( "WASM module not initialized" ) ;
33+ let analyzedElements , prunedRawData ;
17834 try {
179- let result ;
180- if ( options . limit || options . filter ) {
181- result = analyze_page_with_options ( rawData , options ) ;
182- } else {
183- result = analyze_page ( rawData ) ;
184- }
185- resolve ( result ) ;
35+ const wasmPromise = new Promise ( ( resolve , reject ) => {
36+ try {
37+ let result ;
38+ result = options . limit || options . filter ? analyze_page_with_options ( rawData , options ) : analyze_page ( rawData ) ,
39+ resolve ( result ) ;
40+ } catch ( e ) {
41+ reject ( e ) ;
42+ }
43+ } ) ;
44+ analyzedElements = await Promise . race ( [ wasmPromise , new Promise ( ( _ , reject ) => setTimeout ( ( ) => reject ( new Error ( "WASM processing timeout (>18s)" ) ) , 18e3 ) ) ] ) ;
18645 } catch ( e ) {
187- reject ( e ) ;
46+ const errorMsg = e . message || "Unknown WASM error" ;
47+ throw new Error ( `WASM analyze_page failed: ${ errorMsg } ` ) ;
18848 }
189- } ) ;
190-
191- // Add timeout protection (18 seconds - less than content.js timeout)
192- analyzedElements = await Promise . race ( [
193- wasmPromise ,
194- new Promise ( ( _ , reject ) =>
195- setTimeout ( ( ) => reject ( new Error ( 'WASM processing timeout (>18s)' ) ) , 18000 )
196- ) ,
197- ] ) ;
198- } catch ( e ) {
199- const errorMsg = e . message || 'Unknown WASM error' ;
200- console . error ( `[Sentience Background] WASM analyze_page failed: ${ errorMsg } ` , e ) ;
201- throw new Error ( `WASM analyze_page failed: ${ errorMsg } ` ) ;
49+ try {
50+ prunedRawData = prune_for_api ( rawData ) ;
51+ } catch ( e ) {
52+ prunedRawData = rawData ;
53+ }
54+ performance . now ( ) ;
55+ return {
56+ elements : analyzedElements ,
57+ raw_elements : prunedRawData
58+ } ;
59+ } catch ( error ) {
60+ performance . now ( ) ;
61+ throw error ;
20262 }
63+ }
20364
204- // Prune elements for API (prevents 413 errors on large sites)
205- let prunedRawData ;
65+ initWASM ( ) . catch ( err => { } ) , chrome . runtime . onMessage . addListener ( ( request , sender , sendResponse ) => {
20666 try {
207- prunedRawData = prune_for_api ( rawData ) ;
208- } catch ( e ) {
209- console . warn ( '[Sentience Background] prune_for_api failed, using original data:' , e ) ;
210- prunedRawData = rawData ;
67+ return "captureScreenshot" === request . action ? ( handleScreenshotCapture ( sender . tab . id , request . options ) . then ( screenshot => {
68+ sendResponse ( {
69+ success : ! 0 ,
70+ screenshot : screenshot
71+ } ) ;
72+ } ) . catch ( error => {
73+ sendResponse ( {
74+ success : ! 1 ,
75+ error : error . message || "Screenshot capture failed"
76+ } ) ;
77+ } ) , ! 0 ) : "processSnapshot" === request . action ? ( handleSnapshotProcessing ( request . rawData , request . options ) . then ( result => {
78+ sendResponse ( {
79+ success : ! 0 ,
80+ result : result
81+ } ) ;
82+ } ) . catch ( error => {
83+ sendResponse ( {
84+ success : ! 1 ,
85+ error : error . message || "Snapshot processing failed"
86+ } ) ;
87+ } ) , ! 0 ) : ( sendResponse ( {
88+ success : ! 1 ,
89+ error : "Unknown action"
90+ } ) , ! 1 ) ;
91+ } catch ( error ) {
92+ try {
93+ sendResponse ( {
94+ success : ! 1 ,
95+ error : `Fatal error: ${ error . message || "Unknown error" } `
96+ } ) ;
97+ } catch ( e ) { }
98+ return ! 1 ;
21199 }
212-
213- const duration = performance . now ( ) - startTime ;
214- console . log (
215- `[Sentience Background] ✓ Processed: ${ analyzedElements . length } analyzed, ${ prunedRawData . length } pruned (${ duration . toFixed ( 1 ) } ms)`
216- ) ;
217-
218- return {
219- elements : analyzedElements ,
220- raw_elements : prunedRawData ,
221- } ;
222- } catch ( error ) {
223- const duration = performance . now ( ) - startTime ;
224- console . error ( `[Sentience Background] Processing error after ${ duration . toFixed ( 1 ) } ms:` , error ) ;
225- throw error ;
226- }
227- }
228-
229- console . log ( '[Sentience Background] Service worker ready' ) ;
230-
231- // Global error handlers to prevent extension crashes
232- self . addEventListener ( 'error' , ( event ) => {
233- console . error ( '[Sentience Background] Global error caught:' , event . error ) ;
234- event . preventDefault ( ) ; // Prevent extension crash
235- } ) ;
236-
237- self . addEventListener ( 'unhandledrejection' , ( event ) => {
238- console . error ( '[Sentience Background] Unhandled promise rejection:' , event . reason ) ;
239- event . preventDefault ( ) ; // Prevent extension crash
240- } ) ;
100+ } ) , self . addEventListener ( "error" , event => {
101+ event . preventDefault ( ) ;
102+ } ) , self . addEventListener ( "unhandledrejection" , event => {
103+ event . preventDefault ( ) ;
104+ } ) ;
0 commit comments