Hey team.
It would be great to include a TypeScript implementation in the install/setup script examples of background.js.

Right now, the docs only show a plain JavaScript version, which causes TypeScript to complain about missing types or implicit anys.
Since most projects today use TS by default, having a TypeScript-friendly snippet (with proper typings) would make onboarding much smoother.
Hereβs an example of a version with typings:
// use same paywall id everywhere
export const PAYWALL_ID = XXX;
const connectedPorts = new Set<chrome.runtime.Port>();
chrome.runtime.onConnectExternal.addListener(port => {
if (
port.sender?.url &&
(port.sender.url.includes('onlineapp.pro') ||
port.sender.url.includes('onlineapp.live') ||
port.sender.url.includes('onlineapp.stream'))
) {
connectedPorts.add(port);
port.onDisconnect.addListener(() => {
connectedPorts.delete(port);
});
} else {
console.warn('Connection attempt from unauthorized domain:', port.sender?.url);
port.disconnect();
}
});
function trackEvent(eventName: string, additionalData = {}) {
getUserId((userId: string) => {
fetch('https://onlineapp.pro/api/track-event', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event: eventName,
wallId: PAYWALL_ID,
extensionId: chrome.runtime.id,
userId: userId,
...additionalData,
}),
});
});
}
function notifyConnectedClients(notification: Record<string, any>) {
connectedPorts.forEach(port => {
try {
port.postMessage(notification);
} catch (error) {
console.error('Error sending notification to port:', error);
connectedPorts.delete(port);
}
});
}
function getUserId(callback: (user_id: string) => void) {
chrome.storage.sync.get(['user_id'], result => {
if (result.user_id) {
callback(result.user_id);
} else {
const userId = crypto.randomUUID();
chrome.storage.sync.set({ user_id: userId, ['pw-591-visitor-id']: userId }, () => {
callback(userId);
});
}
});
}
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
if (
sender.url &&
(sender.url.includes('onlineapp.pro') ||
sender.url.includes('onlineapp.live') ||
sender.url.includes('onlineapp.stream'))
) {
if (message.source === 'supabase-auth-adapter') {
switch (message.action) {
case 'ping':
sendResponse({ status: 'ok' });
break;
case 'getItem':
try {
const { key } = message.data;
chrome.storage.sync.get(key, result => {
if (chrome.runtime.lastError) {
const errorMessage = chrome.runtime.lastError.message;
console.error('Storage error:', errorMessage);
sendResponse({ status: 'error', message: errorMessage });
} else {
sendResponse({ status: 'success', value: result[key] || null });
}
});
return true;
} catch (error) {
if (error instanceof Error) {
console.error('Error in getItem:', error);
sendResponse({ status: 'error', message: error.message });
}
}
break;
case 'setItem':
try {
const { key, value } = message.data;
chrome.storage.sync.set({ [key]: value }, () => {
if (chrome.runtime.lastError) {
const errorMessage = chrome.runtime.lastError.message;
console.error('Storage error:', errorMessage);
sendResponse({ status: 'error', message: errorMessage });
} else {
sendResponse({ status: 'success' });
notifyConnectedClients({
type: 'storage_update',
action: 'set',
key,
value,
timestamp: Date.now(),
});
}
});
return true;
} catch (error) {
if (error instanceof Error) {
console.error('Error in setItem:', error);
sendResponse({ status: 'error', message: error.message });
}
}
break;
case 'removeItem':
try {
const { key } = message.data;
chrome.storage.sync.remove(key, () => {
if (chrome.runtime.lastError) {
const errorMessage = chrome.runtime.lastError.message;
console.error('Storage error:', errorMessage);
sendResponse({ status: 'error', message: errorMessage });
} else {
notifyConnectedClients({
type: 'storage_update',
action: 'remove',
key,
timestamp: Date.now(),
});
sendResponse({ status: 'success' });
}
});
return true;
} catch (error) {
if (error instanceof Error) {
console.error('Error in removeItem:', error);
sendResponse({ status: 'error', message: error.message });
}
}
break;
default:
console.warn('Unknown action:', message.action);
sendResponse({ status: 'error', message: 'Unknown action' });
break;
}
} else if (message.type === 'broadcast') {
notifyConnectedClients(message.data || message);
sendResponse({
status: 'success',
message: 'Message broadcasted successfully',
clientsCount: connectedPorts.size,
});
} else {
console.warn('Message has neither source nor type');
sendResponse({ status: 'error', message: 'Invalid message format' });
}
} else {
console.warn('Message from unauthorized domain:', sender.url);
sendResponse({ status: 'error', message: 'Unauthorized domain' });
}
return true;
});Please authenticate to join the conversation.
In Review
π‘ Feature Request
3 months ago

Alexander Kozhurkin
Get notified by email when there are changes.
In Review
π‘ Feature Request
3 months ago

Alexander Kozhurkin
Get notified by email when there are changes.