@superflag/super-ipc-preloader
v0.0.21
Published
A sanely typed convenience wrapper around electron ipc
Downloads
14
Readme
@superflag/super-ipc-preloader
Preload script utilities for Super IPC. This package provides the bridge that safely exposes IPC functionality to your Electron renderer process while maintaining security through context isolation.
Installation
npm install @superflag/super-ipc-preloaderWhat This Package Provides
- Context Bridge Setup: Secure IPC bridge registration with Electron's context isolation
- Type-Safe API: Exposes typed IPC methods to the renderer process
- Security: Maintains Electron's security model while enabling IPC communication
- Zero Configuration: Works out of the box with a single function call
Quick Start
Basic Setup
// preload/preload.ts
import { contextBridge, ipcRenderer } from 'electron';
import { registerElectronApiBridge } from '@superflag/super-ipc-preloader';
// This is all you need! The bridge will be available as window.electronApi
registerElectronApiBridge(contextBridge, ipcRenderer);Webpack Configuration
If you're using Webpack to bundle your preload script:
// webpack.preload.js
module.exports = {
entry: './src/preload/preload.ts',
target: 'electron-preload',
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.ts', '.js'],
},
output: {
filename: 'preload.js',
path: path.resolve(__dirname, 'dist'),
},
};API Reference
registerElectronApiBridge(contextBridge, ipcRenderer, appVersion?)
Registers the IPC bridge that exposes Super IPC functionality to the renderer process.
Parameters:
contextBridge: Electron's contextBridge moduleipcRenderer: Electron's ipcRenderer module
Returns: void
Example:
import { contextBridge, ipcRenderer } from 'electron';
import { registerElectronApiBridge } from '@superflag/super-ipc-preloader';
// Basic usage
registerElectronApiBridge(contextBridge, ipcRenderer);What Gets Exposed
After calling registerElectronApiBridge, the renderer process will have access to:
// Available in renderer process as window.electronApi
interface ElectronApi {
invoke: (channel: string, ...args: any[]) => Promise<any>;
on: (channel: string, callback: (...args: any[]) => void) => void;
removeListener: (channel: string, callback: (...args: any[]) => void) => void;
}Usage Examples
Basic Preload Script
// preload/preload.ts
import { contextBridge, ipcRenderer } from 'electron';
import { registerElectronApiBridge } from '@superflag/super-ipc-preloader';
// Enable the IPC bridge
registerElectronApiBridge(contextBridge, ipcRenderer);
// Optional: Add additional custom APIs
contextBridge.exposeInMainWorld('customAPI', {
getVersion: () => process.versions.electron,
getPlatform: () => process.platform,
});Basic Usage
The bridge is always registered under the electronApi namespace:
// preload/preload.ts
import { contextBridge, ipcRenderer } from 'electron';
import { registerElectronApiBridge } from '@superflag/super-ipc-preloader';
// Register the IPC bridge (always uses 'electronApi' namespace)
registerElectronApiBridge(contextBridge, ipcRenderer);Advanced Configuration
// preload/preload.ts
import { contextBridge, ipcRenderer } from 'electron';
import { registerElectronApiBridge } from '@superflag/super-ipc-preloader';
// Register the Super IPC bridge
registerElectronApiBridge(contextBridge, ipcRenderer);
// Add logging for development
if (process.env.NODE_ENV === 'development') {
contextBridge.exposeInMainWorld('devTools', {
log: (message: string) => console.log(`[Preload]: ${message}`),
ipcStats: () => {
// Custom IPC statistics or debugging info
return {
activeOperations: getActiveOperationCount(),
totalRequests: getTotalRequestCount(),
};
},
});
}
// Add app-specific utilities
contextBridge.exposeInMainWorld('appUtils', {
// Environment info
isProduction: process.env.NODE_ENV === 'production',
isDevelopment: process.env.NODE_ENV === 'development',
// System info
platform: process.platform,
arch: process.arch,
nodeVersion: process.versions.node,
electronVersion: process.versions.electron,
// Custom utilities
formatFileSize: (bytes: number) => {
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
if (bytes === 0) return '0 Bytes';
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
},
});Security Considerations
What This Package Does for Security
- Context Isolation: Uses Electron's
contextBridgeto safely expose APIs - No Direct Access: Renderer never gets direct access to Node.js or Electron APIs
- Controlled Interface: Only exposes the specific IPC methods needed
- Type Safety: Prevents arbitrary IPC calls through TypeScript
Best Practices
DO ✅
// Good: Use the provided bridge
registerElectronApiBridge(contextBridge, ipcRenderer);
// Good: Add minimal, specific utilities
contextBridge.exposeInMainWorld('appInfo', {
version: app.getVersion(), // Static, safe data
platform: process.platform, // Static, safe data
});DON'T ❌
// Bad: Don't expose entire modules
contextBridge.exposeInMainWorld('fs', require('fs'));
// Bad: Don't expose dangerous functions
contextBridge.exposeInMainWorld('dangerousAPI', {
executeCommand: (cmd) => require('child_process').exec(cmd),
readAnyFile: (path) => require('fs').readFileSync(path),
});
// Bad: Don't disable security features
// webPreferences: {
// nodeIntegration: true, // Don't do this
// contextIsolation: false, // Don't do this
// }Troubleshooting
Common Issues
"electronApi is not defined"
Problem: Renderer process can't access window.electronApi
Solutions:
Ensure preload script is correctly specified in
BrowserWindow:new BrowserWindow({ webPreferences: { preload: path.join(__dirname, 'preload.js'), // Correct path contextIsolation: true, // Must be true nodeIntegration: false, // Must be false }, });Verify preload script is being executed:
// Add to preload.ts for debugging console.log('Preload script executed');
TypeScript Errors in Renderer
Problem: TypeScript doesn't recognize window.electronApi
Solution: Add type declarations:
// types/electron.d.ts
export interface IElectronApi {
invoke: (channel: string, ...args: any[]) => Promise<any>;
on: (channel: string, callback: (...args: any[]) => void) => void;
removeListener: (channel: string, callback: (...args: any[]) => void) => void;
version: string;
}
declare global {
interface Window {
electronApi: IElectronApi;
}
}Webpack/Build Issues
Problem: Preload script not being built correctly
Solution: Ensure correct Webpack target:
module.exports = {
target: 'electron-preload', // Important!
entry: './src/preload/preload.ts',
// ... rest of config
};Debugging Tips
Enable IPC Logging
// preload/preload.ts
import { contextBridge, ipcRenderer } from 'electron';
import { registerElectronApiBridge } from '@superflag/super-ipc-preloader';
// Enable debugging in development
if (process.env.NODE_ENV === 'development') {
// Log all IPC calls
const originalInvoke = ipcRenderer.invoke;
ipcRenderer.invoke = (...args) => {
console.log('IPC Invoke:', args);
return originalInvoke.apply(ipcRenderer, args);
};
}
registerElectronApiBridge(contextBridge, ipcRenderer);Check Context Isolation
// In renderer process (for debugging)
console.log('Node integration:', typeof process !== 'undefined');
console.log('Context isolation:', typeof window.electronApi !== 'undefined');Multiple Preload Scripts
If you need multiple preload scripts for different windows:
// preload/main-window.ts
import { contextBridge, ipcRenderer } from 'electron';
import { registerElectronApiBridge } from '@superflag/super-ipc-preloader';
registerElectronApiBridge(contextBridge, ipcRenderer);
// Main window specific APIs
contextBridge.exposeInMainWorld('mainWindowAPI', {
minimize: () => ipcRenderer.invoke('window:minimize'),
maximize: () => ipcRenderer.invoke('window:maximize'),
close: () => ipcRenderer.invoke('window:close'),
});// preload/settings-window.ts
import { contextBridge, ipcRenderer } from 'electron';
import { registerElectronApiBridge } from '@superflag/super-ipc-preloader';
registerElectronApiBridge(contextBridge, ipcRenderer);
// Settings window specific APIs
contextBridge.exposeInMainWorld('settingsAPI', {
resetToDefaults: () => ipcRenderer.invoke('settings:reset'),
exportSettings: () => ipcRenderer.invoke('settings:export'),
});// main/main.ts
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload/main-window.js'),
},
});
const settingsWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload/settings-window.js'),
},
});Best Practices
- Keep It Simple: Only expose what you actually need
- Use TypeScript: Get full type safety in your renderer process
- Test Thoroughly: Verify your preload script works in all scenarios
- Security First: Never expose sensitive Node.js APIs directly
- Consistent Naming: Use clear, consistent names for your exposed APIs
- Document Well: Document any custom APIs you expose
The preload script is your security boundary - keep it minimal and focused!
