npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

firebase-electron-v2

v2.0.8

Published

Receive Firebase notifications in your Electron app using the Firebase JS SDK and Web Push APIs

Readme

firebase-electron-v2

Receive Firebase push notifications in your Electron app using the modern Firebase JS SDK (v9+) and Web Push APIs.

Installation

npm i firebase-electron-v2

Usage

This library separates setup between Electron's main and renderer processes.

In the main process (main.js or main.ts)

The main process setup is minimal. It primarily prepares to listen for events forwarded from the renderer process via IPC.

import { app, BrowserWindow } from 'electron';
import { setup as setupPushReceiver } from 'firebase-electron';

let mainWindow: BrowserWindow | null;

app.on('ready', () => {
  mainWindow = new BrowserWindow({
    // ... your window options
    webPreferences: {
      // Ensure you have appropriate webPreferences for IPC and potentially contextIsolation
      // preload: path.join(__dirname, 'preload.js') // Example if using preload
      nodeIntegration: false, // Recommended: false
      contextIsolation: true, // Recommended: true
      // Service workers require secure origins (https or localhost) or custom protocols
    },
  });

  // Option 1: Serve your index.html via a secure context
  // mainWindow.loadURL('https://localhost:3000'); // Example using a local dev server

  // Option 2: Or register a custom protocol
  // protocol.registerFileProtocol('app', (request, callback) => { /* ... */ });
  // mainWindow.loadURL('app://./index.html');

  // Call setup *after* creating the window but *before* loading content usually works best.
  if (mainWindow) {
    setupPushReceiver(mainWindow.webContents);
  }

  // ... rest of your main process setup
});

In the renderer process (renderer.js or renderer.ts)

This is where the core Firebase messaging initialization happens.

// Assuming you have exposed ipcRenderer.send via contextBridge in your preload script
// Example preload.ts:
// import { contextBridge, ipcRenderer } from 'electron';
// contextBridge.exposeInMainWorld('electronIpc', {
//   send: (channel, ...args) => ipcRenderer.send(channel, ...args),
//   // You might also need to expose ipcRenderer.on for receiving events
//   on: (channel, listener) => {
//     const subscription = (event, ...args) => listener(...args);
//     ipcRenderer.on(channel, subscription);
//     return () => ipcRenderer.removeListener(channel, subscription); // Return cleanup function
//   }
// });

// In renderer.ts:
declare global {
  interface Window {
    electronIpc: {
      send: (channel: string, ...args: any[]) => void;
      on: (channel: string, listener: (...args: any[]) => void) => () => void;
    };
  }
}

// Import the initialization function from the renderer entry point
import { initializeFirebaseMessaging, type IpcSender } from 'firebase-electron-v2/renderer';
// Import constants from the specific constants entry point
import {
  NOTIFICATION_RECEIVED,
  TOKEN_UPDATED,
  // NOTIFICATION_SERVICE_ERROR, // Optional: Listen for errors forwarded from main if needed
} from 'firebase-electron/dist/electron/consts'; // Note the specific path

// Your Firebase project configuration (including VAPID key)
const firebaseCredentials = {
  apiKey: 'YOUR_API_KEY',
  authDomain: 'YOUR_AUTH_DOMAIN',
  projectId: 'YOUR_PROJECT_ID',
  storageBucket: 'YOUR_STORAGE_BUCKET',
  messagingSenderId: 'YOUR_MESSAGING_SENDER_ID',
  appId: 'YOUR_APP_ID',
  vapidKey: 'YOUR_VAPID_KEY',
};

// Create the ipcSender object using the exposed API from preload script
const ipcSender: IpcSender = {
  send: window.electronIpc.send, // Pass the actual send function
};

// Call initializeFirebaseMessaging to start the process, passing credentials AND the ipcSender
initializeFirebaseMessaging(firebaseCredentials, ipcSender)
  .then(() => {
    console.log('Firebase Messaging setup initiated in renderer.');
  })
  .catch((err) => {
    console.error('Error initiating Firebase Messaging setup:', err);
  });

// Listen for the updated FCM token (using the exposed 'on' method from preload)
const unsubscribeTokenUpdates = window.electronIpc.on(TOKEN_UPDATED, (token) => {
  console.log('FCM Token received in renderer:', token);
  // *** IMPORTANT: Send this token to your application server ***
  // Example: sendTokenToServer(token);
});

// Handle notifications received while the app is in the foreground (using the exposed 'on' method)
const unsubscribeNotifications = window.electronIpc.on(NOTIFICATION_RECEIVED, (notificationPayload) => {
  console.log('Notification received in foreground:', notificationPayload);
  // Display the notification using an in-app UI or the Notification API
  // Example: new Notification(notificationPayload.title, { body: notificationPayload.body });
});

// Optional: Listen for errors forwarded from the main process setup
// const unsubscribeErrors = window.electronIpc.on(NOTIFICATION_SERVICE_ERROR, (error) => {
//   console.error('Error reported from main process push receiver setup:', error);
// });

// Remember to clean up listeners when the component unmounts or window closes
// window.addEventListener('beforeunload', () => {
//   unsubscribeTokenUpdates();
//   unsubscribeNotifications();
//   unsubscribeErrors(); // If listening for errors
// });

Service Worker (firebase-messaging-sw.js)

This library relies on a Service Worker to handle push notifications when your application is in the background or closed.

  1. Location: The firebase-messaging-sw.js file must be served from the root of your application (e.g., https://your-app.com/firebase-messaging-sw.js or accessible at / on your local development server). This library's build process places the file in dist/firebase-messaging-sw.js. You need to configure your Electron app's build or server setup to copy and serve this file from the root.
  2. Credentials: The service worker runs independently and needs its own copy of the Firebase configuration. You must configure this. Common methods:
    • Build-Time Substitution: Use build tools (like Webpack DefinePlugin, Vite env variables) to replace placeholders in the service worker file during your application's build process.
    • Fetch Configuration: Have the service worker fetch a JSON configuration file (e.g., /firebase-config.json) that you serve alongside your application.

The library's dist/firebase-messaging-sw.js contains placeholders like PLACEHOLDER_API_KEY. You need to modify it or use a build process to inject the correct values.

Example firebase-messaging-sw.js (after replacing placeholders):

// firebase-messaging-sw.js (ensure this is served from your app root)

import { initializeApp } from 'firebase/app';
import { getMessaging, onBackgroundMessage } from 'firebase/messaging/sw';

// *** Replace these with your actual credentials (e.g., via build process) ***
const firebaseConfig = {
  apiKey: 'YOUR_ACTUAL_API_KEY',
  authDomain: 'YOUR_ACTUAL_AUTH_DOMAIN',
  projectId: 'YOUR_ACTUAL_PROJECT_ID',
  storageBucket: 'YOUR_ACTUAL_STORAGE_BUCKET',
  messagingSenderId: 'YOUR_ACTUAL_SENDER_ID',
  appId: 'YOUR_ACTUAL_APP_ID',
};

// Initialize Firebase
try {
  const app = initializeApp(firebaseConfig);
  const messaging = getMessaging(app);

  onBackgroundMessage(messaging, (payload) => {
    console.log('[firebase-messaging-sw.js] Received background message ', payload);
    const notificationTitle = payload.notification?.title || 'Background Message';
    const notificationOptions = {
      body: payload.notification?.body || '',
      icon: payload.notification?.icon || '/default-icon.png', // Provide a default icon
    };
    // Use self.registration to show the notification
    self.registration.showNotification(notificationTitle, notificationOptions);
  });
} catch (e) {
  console.error('Error initializing Firebase in SW:', e);
}

// Optional: Add notification click handler
self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  console.log('Notification clicked:', event.notification);
  // Add logic here to open/focus a window, e.g.:
  // event.waitUntil(
  //   clients.matchAll({ type: 'window', includeUncontrolled: true }).then(windowClients => {
  //     // Check if a window is already open
  //     for (let client of windowClients) {
  //       if (client.url === '/' && 'focus' in client) { // Adjust URL check as needed
  //         return client.focus();
  //       }
  //     }
  //     // If not, open a new window
  //     if (clients.openWindow) {
  //       return clients.openWindow('/'); // Adjust URL to open
  //     }
  //   })
  // );
});

Where to find appId, apiKey, projectId and vapidKey

  1. Go to Firebase Console & login to your account
  2. Select your project
  3. Click on the Project Settings cog icon
  4. Click on Project Settings
  5. Make sure you're on the General tab
  6. Scroll down to the Your apps section
  7. If you don't have a Web app, click on Add app
    • Select Web (</>) icon
    • Fill in the required fields
    • Click on Register app
  8. Copy the configuration object values (apiKey, authDomain, projectId, storageBucket, messagingSenderId, appId) shown under the SDK setup and configuration section (select Config or CDN).
  9. Go to the Cloud Messaging tab.
  10. Under Web configuration > Web Push certificates, click Generate key pair if you don't have one.
  11. Copy the value in the Key pair column. This is your vapidKey.

Moving from electron-push-receiver

electron-push-receiver used the Legacy FCM API which is deprecated and being shut down.

This package (firebase-electron-v2) is a refactored solution using the modern Firebase JS SDK (v9+) that relies on standard Web Push APIs via a Service Worker.

Key Differences & Migration Steps:

  1. Architecture: The core logic now resides in the renderer process and a service worker, not the main process.
  2. Main Process: The setup function (imported from firebase-electron-v2) is still called in the main process, but it primarily sets up IPC listeners.
  3. Renderer Process:
    • You must import initializeFirebaseMessaging from firebase-electron-v2/renderer.
    • Call initializeFirebaseMessaging(firebaseCredentials) instead of sending START_NOTIFICATION_SERVICE via IPC.
    • Listen for TOKEN_UPDATED via ipcRenderer.on to get the FCM token and send it to your server.
    • Listen for NOTIFICATION_RECEIVED via ipcRenderer.on to handle foreground messages.
  4. Service Worker:
    • You must create/configure and serve the firebase-messaging-sw.js file (provided in dist/) from your application root.
    • You must provide Firebase credentials within this service worker file (e.g., via a build step).
  5. Credentials: You now need the full Firebase Web config (apiKey, authDomain, projectId, etc.) plus the vapidKey in your renderer process.

Credits to Matthieu Lemoine and contributors for the original electron-push-receiver.

What's new (in this fork/rewrite)

  • Uses modern Firebase JS SDK (v9+) and standard Web Push APIs.
  • Relies on a Service Worker for background notifications.
  • Clear separation between main (setup) and renderer (initializeFirebaseMessaging) initialization.
  • Uses updated dependencies.
  • Removed legacy dependencies (electron-config, request, protobufjs, etc.).
  • Simplified core logic.
  • Requires Node.js >= 18 (as required by modern Firebase SDK).
  • Uses tsup for building and vitest for testing.
  • Completely written in TypeScript.

[!CAUTION] > Breaking changes: Requires full Firebase Web config + VAPID key. Initialization logic moved to the renderer. Requires manual setup and configuration of the Service Worker (firebase-messaging-sw.js).

Development

  1. Make sure you have Node.js >= v18 installed.
  2. Install dependencies with npm install.
  3. If running tests that require credentials, create a .env file from .env.template and fill in your Firebase project details.
  4. Build with npm run build.
  5. Run tests with npm run test.

[!NOTE] Currently, the automated tests (npm run test) are not implemented for the refactored codebase. Contributions are welcome!