I'm learning how to build chrome extensions with manifest v3, what I'm trying to do is the following
In my extension background.js (service worker) I want to do this:
- connect to WebSocket to get data updates
- reconnect to the Websocket when service-worker wake up
- those are tasks to get data updates from a WebSocket and update the badge text and send notifications.
I need to do these tasks while not relying on having a port open with the popup or a content script.
I'm using Chrome Alarms to wake up the service worker
it may sound weird that I have to reconnect every time the service worker wakes up considering chrome is shutting the service worker down like every 15s or less once I close the extensions dev tools (which makes me cry blood) but it is better than sending XHR periodically using Chrome alarms, which would result in a lot more requests being sent to an API, so reconnecting to the Websocket seems less problematic.
I'm having a super hard time debugging my service worker (background script) in my chrome extension. The problem is when I have dev tools open the service worker will NEVER go inactive, and what I'm trying to do is identify when the SW wakes up to perform tasks, super-duper weird because I need dev tools open to debugging...
how do you debug an extension SW without devtools open?
do you/anyone reading this have any recommendations/thoughts on what I want to do with this extension and the pain process for debugging the SW?
here is the code I have for the background.js
const extension = {
count: 0,
disconnected: false,
port: {} as any,
ws: null,
};
const fetchData = () => {
return fetch(
'https://api.coingecko.com/api/v3/coins/ethereum?localization=incididuntelit&tickers=false&market_data=true&community_data=true&developer_data=true&sparkline=true'
).then((res) => res.json());
};
// Chrome Alarms
const setupAlarms = () => {
console.log('###ALARMS-SETUP');
chrome.alarms.get('data-fetch', (alarm) => {
if (!alarm) {
chrome.alarms.create('data-fetch', { periodInMinutes: 0.1 });
}
});
chrome.alarms.get('client-pinging-server', (alarm) => {
if (!alarm) {
chrome.alarms.create('client-pinging-server', { periodInMinutes: 0.1 });
}
});
chrome.alarms.onAlarm.addListener((e) => {
if (e.name === 'data-fetch') {
fetchData()
.then((res) => {
// console.log('###ETHEREUM:', res.market_data.current_price.cad);
chrome.action.setBadgeText({ text: `${Math.round(Math.random() * 100)}` });
})
.catch((error) => console.error('###ERROR', error));
}
if (e.name === 'client-pinging-server') {
if (!extension.ws || !extension.ws.getInstance()) {
console.log('\n');
console.log('###reconnecting...', extension.ws);
console.log('\n');
extension.ws = WebSocketClient();
extension.ws.connect();
}
if (extension.ws.getInstance()) {
console.log('###sending-client-ping');
extension.ws.getInstance().send(JSON.stringify({ message: 'client ping - keep alive' }));
}
}
});
};
// ON INSTALL
chrome.runtime.onInstalled.addListener(async (e) => {
const API_URL = 'ws://localhost:8080';
chrome.storage.local.set({ apiUrl: API_URL, count: 0 });
console.info('###Extension installed', e);
chrome.action.setBadgeText({ text: '0' });
chrome.action.setBadgeBackgroundColor({ color: '#FF9900' });
});
// ON SUSPEND
chrome.runtime.onSuspend.addListener(() => {
console.log('Unloading.');
chrome.action.setBadgeText({ text: `off` });
});
function WebSocketClient() {
let instance = null;
const connect = () => {
return new Promise((resolve, reject) => {
const ws = new WebSocket('ws://localhost:8080');
const onOpen = () => {
instance = ws;
console.log('###websocket:connected', instance);
return resolve(ws);
};
const onError = (event) => {
console.log('###INIT-FAILED', event);
ws.close(1000, 'closing due to unknown error');
return reject('failed to connect to websocket');
};
const onClose = () => {
console.log('###websocket:disconnected');
instance = null;
// reconnect is happening in the alarm callback
};
ws.onopen = onOpen;
ws.onerror = onError;
ws.onclose = onClose;
});
};
const getInstance = () => {
return instance;
};
return {
connect,
getInstance,
};
}
self.addEventListener('install', async (event) => {
console.log('====install', event);
chrome.action.setBadgeBackgroundColor({ color: '#a6e22e' });
});
self.addEventListener('activate', async (event) => {
console.log('====activate', event);
chrome.action.setBadgeBackgroundColor({ color: '#FF9900' });
extension.ws = WebSocketClient();
extension.ws.connect();
setupAlarms();
});
self.addEventListener('push', function (event) {
// Keep the service worker alive until the notification is created.
event.waitUntil(
self.registration.showNotification('Testing PUSH API', {
body: 'coming from push event',
})
);
});