UNPKG

@zubridge/electron

Version:

A streamlined state management library for Electron applications using Zustand.

122 lines (117 loc) 4.76 kB
'use strict'; var zustand = require('zustand'); var vanilla = require('zustand/vanilla'); /** * Determines if the application is running in development mode * * Uses a combination of checks to ensure consistent behavior: * 1. Checks if app is packaged (production builds are packaged) * 2. Checks NODE_ENV environment variable * 3. Checks ELECTRON_IS_DEV environment variable (set by electron-is-dev or similar utilities) * * @returns {boolean} True if running in development mode, false otherwise */ const isDev = async () => { // Ensure we have access to the app object (should be in the main process) const { app } = await import('electron'); if (typeof app !== 'undefined') { return !app.isPackaged || process.env.NODE_ENV === 'development' || process.env.ELECTRON_IS_DEV === '1'; } // Fallback for renderer process or when app isn't available return (process.env.NODE_ENV === 'development' || process.env.ELECTRON_IS_DEV === '1' || !process.env.VITE_DEV_SERVER_URL); // Vite-specific check }; // Store registry to implement singleton pattern // Maps handler objects to their corresponding stores const storeRegistry = new WeakMap(); // Internal implementation of store creation const createStore = (bridge) => { // Check if a store already exists for these handlers if (storeRegistry.has(bridge)) { return storeRegistry.get(bridge); } // Create a new store if one doesn't exist const newStore = vanilla.createStore((setState) => { // subscribe to changes bridge.subscribe((state) => setState(state)); // get initial state bridge.getState().then((state) => setState(state)); // no state keys - they will all come from main return {}; }); // Register the store storeRegistry.set(bridge, newStore); return newStore; }; // Create Electron-specific handlers const createHandlers = () => { if (typeof window === 'undefined' || !window.zubridge) { throw new Error('Zubridge handlers not found in window. Make sure the preload script is properly set up.'); } return window.zubridge; }; /** * Creates a hook for accessing the store state in React components */ const createUseStore = (customHandlers) => { const handlers = customHandlers || createHandlers(); const vanillaStore = createStore(handlers); const useBoundStore = (selector) => zustand.useStore(vanillaStore, selector); Object.assign(useBoundStore, vanillaStore); // return store hook return useBoundStore; }; /** * Creates a dispatch function for sending actions to the main process * * @template S The state type * @template TActions A record of action types to payload types mapping (optional) * @param customHandlers Optional custom handlers to use instead of window.zubridge * @returns A typed dispatch function * * @example * // Basic usage * const dispatch = useDispatch(); * * @example * // With typed actions * type CounterActions = { * 'COUNTER:INCREMENT': void; * 'COUNTER:DECREMENT': void; * 'COUNTER:SET': number; * }; * const dispatch = useDispatch<State, CounterActions>(); * dispatch({ type: 'COUNTER:SET', payload: 5 }); // Type-safe payload * dispatch({ type: 'UNKNOWN' }); // Type error */ const useDispatch = (customHandlers) => { const handlers = customHandlers || createHandlers(); // Ensure we have a store for these handlers const store = storeRegistry.has(handlers) ? storeRegistry.get(handlers) : createStore(handlers); // Create a dispatch function that will handle both generic and typed actions const dispatch = ((action, payload) => { if (typeof action === 'function') { // Handle thunks - execute them with the store's getState and our dispatch function return action(store.getState, dispatch); } // Handle string action type with payload if (typeof action === 'string') { // Only pass the payload parameter if it's not undefined, and handle promise return value return payload !== undefined ? handlers.dispatch(action, payload) : handlers.dispatch(action); } // For action objects, normalize to standard Action format if (typeof action.type !== 'string') { throw new Error(`Invalid action type: ${action.type}. Expected a string.`); } const normalizedAction = { type: action.type, payload: action.payload, }; // Return the promise from dispatch return handlers.dispatch(normalizedAction); }); return dispatch; }; exports.createHandlers = createHandlers; exports.createUseStore = createUseStore; exports.isDev = isDev; exports.useDispatch = useDispatch;