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' ;
1+ import init , { analyze_page_with_options , analyze_page , prune_for_api } from "./pkg/sentience_core.js" ;
42
5- // background.js - Service Worker with WASM (CSP-Immune!)
6- // This runs in an isolated environment, completely immune to page CSP policies
3+ let wasmReady = ! 1 , wasmInitPromise = null ;
74
8-
9- console . log ( '[Sentience Background] Initializing...' ) ;
10-
11- // Global WASM initialization state
12- let wasmReady = false ;
13- let wasmInitPromise = null ;
14-
15- /**
16- * Initialize WASM module - called once on service worker startup
17- * Uses static imports (not dynamic import()) which is required for Service Workers
18- */
195async function initWASM ( ) {
20- if ( wasmReady ) return ;
21- 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+ }
2214
23- wasmInitPromise = ( async ( ) => {
15+ async function handleScreenshotCapture ( _tabId , options = { } ) {
2416 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- ) ;
17+ const { format : format = "png" , quality : quality = 90 } = options ;
18+ return await chrome . tabs . captureVisibleTab ( null , {
19+ format : format ,
20+ quality : quality
21+ } ) ;
4322 } catch ( error ) {
44- console . error ( '[Sentience Background] WASM initialization failed:' , error ) ;
45- throw error ;
23+ throw new Error ( `Failed to capture screenshot: ${ error . message } ` ) ;
4624 }
47- } ) ( ) ;
48-
49- return wasmInitPromise ;
5025}
5126
52- // Initialize WASM on service worker startup
53- initWASM ( ) . catch ( ( err ) => {
54- console . error ( '[Sentience Background] Failed to initialize WASM:' , err ) ;
55- } ) ;
56-
57- /**
58- * Message handler for all extension communication
59- * Includes global error handling to prevent extension crashes
60- */
61- chrome . runtime . onMessage . addListener ( ( request , sender , sendResponse ) => {
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- }
79-
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- }
95-
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
110- }
111- return false ;
112- }
113- } ) ;
114-
115- /**
116- * Handle screenshot capture (existing functionality)
117- */
118- async function handleScreenshotCapture ( _tabId , options = { } ) {
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- }
135- }
136-
137- /**
138- * Handle snapshot processing with WASM (NEW!)
139- * This is where the magic happens - completely CSP-immune!
140- * Includes safeguards to prevent crashes and hangs.
141- *
142- * @param {Array } rawData - Raw element data from injected_api.js
143- * @param {Object } options - Snapshot options (limit, filter, etc.)
144- * @returns {Promise<Object> } Processed snapshot result
145- */
14627async function handleSnapshotProcessing ( rawData , options = { } ) {
147- const MAX_ELEMENTS = 10000 ; // Safety limit to prevent hangs
148- const startTime = performance . now ( ) ;
149-
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- }
155-
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- }
162-
163- // Ensure WASM is initialized
164- await initWASM ( ) ;
165- if ( ! wasmReady ) {
166- throw new Error ( 'WASM module not initialized' ) ;
167- }
168-
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 ;
28+ const startTime = performance . now ( ) ;
17729 try {
178- // Use a timeout wrapper to prevent infinite hangs
179- 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 ;
18034 try {
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 ) ;
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 ) ) ] ) ;
18845 } catch ( e ) {
189- reject ( e ) ;
46+ const errorMsg = e . message || "Unknown WASM error" ;
47+ throw new Error ( `WASM analyze_page failed: ${ errorMsg } ` ) ;
19048 }
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 } ` ) ;
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 ;
20462 }
63+ }
20564
206- // Prune elements for API (prevents 413 errors on large sites)
207- let prunedRawData ;
65+ initWASM ( ) . catch ( err => { } ) , chrome . runtime . onMessage . addListener ( ( request , sender , sendResponse ) => {
20866 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 ;
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 ;
21399 }
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- }
229- }
230-
231- console . log ( '[Sentience Background] Service worker ready' ) ;
232-
233- // Global error handlers to prevent extension crashes
234- self . addEventListener ( 'error' , ( event ) => {
235- console . error ( '[Sentience Background] Global error caught:' , event . error ) ;
236- event . preventDefault ( ) ; // Prevent extension crash
237- } ) ;
238-
239- self . addEventListener ( 'unhandledrejection' , ( event ) => {
240- console . error ( '[Sentience Background] Unhandled promise rejection:' , event . reason ) ;
241- event . preventDefault ( ) ; // Prevent extension crash
242- } ) ;
100+ } ) , self . addEventListener ( "error" , event => {
101+ event . preventDefault ( ) ;
102+ } ) , self . addEventListener ( "unhandledrejection" , event => {
103+ event . preventDefault ( ) ;
104+ } ) ;
0 commit comments