electron-redux
Advanced tools
Comparing version 2.0.0-alpha.7 to 2.0.0-alpha.8
@@ -1,6 +0,13 @@ | ||
import { webContents, ipcMain, ipcRenderer } from 'electron'; | ||
import { compose } from 'redux'; | ||
import { webContents, ipcRenderer, ipcMain } from 'electron'; | ||
import isPlainObject from 'lodash.isplainobject'; | ||
import isString from 'lodash.isstring'; | ||
var IPCEvents; | ||
(function (IPCEvents) { | ||
IPCEvents["INIT_STATE"] = "electron-redux.INIT_STATE"; | ||
IPCEvents["INIT_STATE_ASYNC"] = "electron-redux.INIT_STATE_ASYNC"; | ||
IPCEvents["ACTION"] = "electron-redux.ACTION"; | ||
})(IPCEvents || (IPCEvents = {})); | ||
function _defineProperty(obj, key, value) { | ||
@@ -55,12 +62,2 @@ if (key in obj) { | ||
var IPCEvents; | ||
(function (IPCEvents) { | ||
IPCEvents["INIT_STATE"] = "electron-redux.INIT_STATE"; | ||
IPCEvents["INIT_STATE_ASYNC"] = "electron-redux.INIT_STATE_ASYNC"; | ||
IPCEvents["ACTION"] = "electron-redux.ACTION"; | ||
})(IPCEvents || (IPCEvents = {})); | ||
const defaultMainOptions = {}; | ||
const isFSA = action => isPlainObject(action) && isString(action.type) && Object.keys(action).every(isValidKey); | ||
@@ -106,5 +103,46 @@ | ||
}; | ||
const isRenderer = process.type === 'renderer'; | ||
const isMain = process.type === 'browser'; | ||
function createMiddleware(options) { | ||
const middleware = store => { | ||
const processActionMain = (action, options = {}) => { | ||
if (validateAction(action, options.denyList)) { | ||
webContents.getAllWebContents().forEach(contents => { | ||
// Ignore chromium devtools | ||
if (contents.getURL().startsWith('devtools://')) return; | ||
contents.send(IPCEvents.ACTION, action); | ||
}); | ||
} | ||
}; | ||
const processActionRenderer = (action, options = {}) => { | ||
if (validateAction(action, options.denyList)) { | ||
ipcRenderer.send(IPCEvents.ACTION, action); | ||
} | ||
}; | ||
const forwardAction = (store, options) => { | ||
return _objectSpread2(_objectSpread2({}, store), {}, { | ||
dispatch: action => { | ||
const value = store.dispatch(action); | ||
if (!(options === null || options === void 0 ? void 0 : options.preventActionReplay)) { | ||
if (isMain) { | ||
processActionMain(action, options); | ||
} else if (isRenderer) { | ||
processActionRenderer(action, options); | ||
} | ||
} | ||
return value; | ||
} | ||
}); | ||
}; | ||
/** | ||
* Creates new instance of main process redux enhancer. | ||
* @param {MainStateSyncEnhancerOptions} options Additional enhancer options | ||
* @returns StoreEnhancer | ||
*/ | ||
const mainStateSyncEnhancer = (options = {}) => createStore => { | ||
return (reducer, preloadedState) => { | ||
const store = createStore(reducer, preloadedState); | ||
ipcMain.handle(IPCEvents.INIT_STATE_ASYNC, async () => { | ||
@@ -128,39 +166,4 @@ return JSON.stringify(store.getState(), options.serializer); | ||
}); | ||
return next => action => { | ||
if (validateAction(action, options.denyList)) { | ||
webContents.getAllWebContents().forEach(contents => { | ||
// Ignore chromium devtools | ||
if (contents.getURL().startsWith('devtools://')) return; | ||
contents.send(IPCEvents.ACTION, action); | ||
}); | ||
} | ||
return next(action); | ||
}; | ||
return forwardAction(store, options); | ||
}; | ||
return middleware; | ||
} | ||
/** | ||
* Creates new instance of main process redux enhancer. | ||
* @param {MainStateSyncEnhancerOptions} options Additional enhancer options | ||
* @returns StoreEnhancer | ||
*/ | ||
const mainStateSyncEnhancer = (options = defaultMainOptions) => createStore => { | ||
preventDoubleInitialization(); | ||
const middleware = createMiddleware(options); | ||
return (reducer, preloadedState) => { | ||
const store = createStore(reducer, preloadedState); | ||
let dispatch = store.dispatch; | ||
const middlewareAPI = { | ||
getState: store.getState, | ||
dispatch | ||
}; | ||
dispatch = compose(middleware(middlewareAPI))(dispatch); | ||
return _objectSpread2(_objectSpread2({}, store), {}, { | ||
dispatch | ||
}); | ||
}; | ||
}; | ||
@@ -209,17 +212,2 @@ | ||
const defaultRendererOptions = {}; | ||
const createMiddleware$1 = options => store => { | ||
// When receiving an action from main | ||
ipcRenderer.on(IPCEvents.ACTION, (_, action) => { | ||
store.dispatch(stopForwarding(action)); | ||
}); | ||
return next => action => { | ||
if (validateAction(action, options.denyList)) { | ||
ipcRenderer.send(IPCEvents.ACTION, action); | ||
} | ||
return next(action); | ||
}; | ||
}; | ||
/** | ||
@@ -233,7 +221,4 @@ * Creates new instance of renderer process redux enhancer. | ||
const rendererStateSyncEnhancer = (options = defaultRendererOptions) => createStore => { | ||
preventDoubleInitialization(); | ||
const rendererStateSyncEnhancer = (options = {}) => createStore => { | ||
return (reducer, state) => { | ||
const middleware = createMiddleware$1(options); | ||
const initialState = options.lazyInit ? state : fetchInitialState(options); | ||
@@ -246,16 +231,51 @@ const store = createStore(options.lazyInit ? withStoreReplacer(reducer) : reducer, initialState); | ||
}); | ||
} | ||
} // When receiving an action from main | ||
let dispatch = store.dispatch; | ||
const middlewareAPI = { | ||
getState: store.getState, | ||
dispatch | ||
}; | ||
dispatch = compose(middleware(middlewareAPI))(dispatch); | ||
return _objectSpread2(_objectSpread2({}, store), {}, { | ||
dispatch | ||
ipcRenderer.on(IPCEvents.ACTION, (_, action) => { | ||
store.dispatch(stopForwarding(action)); | ||
}); | ||
return forwardAction(store, options); | ||
}; | ||
}; | ||
export { mainStateSyncEnhancer, rendererStateSyncEnhancer, stopForwarding }; | ||
const stateSyncEnhancer = (config = {}) => { | ||
var _process; | ||
preventDoubleInitialization(); | ||
if (isRenderer) { | ||
return rendererStateSyncEnhancer(config); | ||
} else if (isMain) { | ||
return mainStateSyncEnhancer(config); | ||
} | ||
throw new Error(`Unsupported process: process.type = ${(_process = process) === null || _process === void 0 ? void 0 : _process.type}`); | ||
}; | ||
const forwardActionEnhancer = options => createStore => (reducer, preloadedState) => { | ||
const store = createStore(reducer, preloadedState); | ||
return forwardAction(store, options); | ||
}; | ||
const extensionCompose = options => (...funcs) => { | ||
return createStore => { | ||
return [stateSyncEnhancer(_objectSpread2(_objectSpread2({}, options), {}, { | ||
preventActionReplay: true | ||
})), ...funcs, forwardActionEnhancer(options)].reduceRight((composed, f) => f(composed), createStore); | ||
}; | ||
}; | ||
function composeWithStateSync(firstFuncOrOpts, ...funcs) { | ||
if (arguments.length === 0) { | ||
return stateSyncEnhancer(); | ||
} | ||
if (arguments.length === 1 && typeof firstFuncOrOpts === 'object') { | ||
return extensionCompose(firstFuncOrOpts); | ||
} | ||
return extensionCompose({})(firstFuncOrOpts, ...funcs); | ||
} | ||
export { composeWithStateSync, mainStateSyncEnhancer, rendererStateSyncEnhancer, stateSyncEnhancer, stopForwarding }; |
@@ -6,3 +6,2 @@ 'use strict'; | ||
var electron = require('electron'); | ||
var redux = require('redux'); | ||
var isPlainObject = require('lodash.isplainobject'); | ||
@@ -16,2 +15,10 @@ var isString = require('lodash.isstring'); | ||
var IPCEvents; | ||
(function (IPCEvents) { | ||
IPCEvents["INIT_STATE"] = "electron-redux.INIT_STATE"; | ||
IPCEvents["INIT_STATE_ASYNC"] = "electron-redux.INIT_STATE_ASYNC"; | ||
IPCEvents["ACTION"] = "electron-redux.ACTION"; | ||
})(IPCEvents || (IPCEvents = {})); | ||
function _defineProperty(obj, key, value) { | ||
@@ -66,12 +73,2 @@ if (key in obj) { | ||
var IPCEvents; | ||
(function (IPCEvents) { | ||
IPCEvents["INIT_STATE"] = "electron-redux.INIT_STATE"; | ||
IPCEvents["INIT_STATE_ASYNC"] = "electron-redux.INIT_STATE_ASYNC"; | ||
IPCEvents["ACTION"] = "electron-redux.ACTION"; | ||
})(IPCEvents || (IPCEvents = {})); | ||
const defaultMainOptions = {}; | ||
const isFSA = action => isPlainObject__default['default'](action) && isString__default['default'](action.type) && Object.keys(action).every(isValidKey); | ||
@@ -117,5 +114,46 @@ | ||
}; | ||
const isRenderer = process.type === 'renderer'; | ||
const isMain = process.type === 'browser'; | ||
function createMiddleware(options) { | ||
const middleware = store => { | ||
const processActionMain = (action, options = {}) => { | ||
if (validateAction(action, options.denyList)) { | ||
electron.webContents.getAllWebContents().forEach(contents => { | ||
// Ignore chromium devtools | ||
if (contents.getURL().startsWith('devtools://')) return; | ||
contents.send(IPCEvents.ACTION, action); | ||
}); | ||
} | ||
}; | ||
const processActionRenderer = (action, options = {}) => { | ||
if (validateAction(action, options.denyList)) { | ||
electron.ipcRenderer.send(IPCEvents.ACTION, action); | ||
} | ||
}; | ||
const forwardAction = (store, options) => { | ||
return _objectSpread2(_objectSpread2({}, store), {}, { | ||
dispatch: action => { | ||
const value = store.dispatch(action); | ||
if (!(options === null || options === void 0 ? void 0 : options.preventActionReplay)) { | ||
if (isMain) { | ||
processActionMain(action, options); | ||
} else if (isRenderer) { | ||
processActionRenderer(action, options); | ||
} | ||
} | ||
return value; | ||
} | ||
}); | ||
}; | ||
/** | ||
* Creates new instance of main process redux enhancer. | ||
* @param {MainStateSyncEnhancerOptions} options Additional enhancer options | ||
* @returns StoreEnhancer | ||
*/ | ||
const mainStateSyncEnhancer = (options = {}) => createStore => { | ||
return (reducer, preloadedState) => { | ||
const store = createStore(reducer, preloadedState); | ||
electron.ipcMain.handle(IPCEvents.INIT_STATE_ASYNC, async () => { | ||
@@ -139,39 +177,4 @@ return JSON.stringify(store.getState(), options.serializer); | ||
}); | ||
return next => action => { | ||
if (validateAction(action, options.denyList)) { | ||
electron.webContents.getAllWebContents().forEach(contents => { | ||
// Ignore chromium devtools | ||
if (contents.getURL().startsWith('devtools://')) return; | ||
contents.send(IPCEvents.ACTION, action); | ||
}); | ||
} | ||
return next(action); | ||
}; | ||
return forwardAction(store, options); | ||
}; | ||
return middleware; | ||
} | ||
/** | ||
* Creates new instance of main process redux enhancer. | ||
* @param {MainStateSyncEnhancerOptions} options Additional enhancer options | ||
* @returns StoreEnhancer | ||
*/ | ||
const mainStateSyncEnhancer = (options = defaultMainOptions) => createStore => { | ||
preventDoubleInitialization(); | ||
const middleware = createMiddleware(options); | ||
return (reducer, preloadedState) => { | ||
const store = createStore(reducer, preloadedState); | ||
let dispatch = store.dispatch; | ||
const middlewareAPI = { | ||
getState: store.getState, | ||
dispatch | ||
}; | ||
dispatch = redux.compose(middleware(middlewareAPI))(dispatch); | ||
return _objectSpread2(_objectSpread2({}, store), {}, { | ||
dispatch | ||
}); | ||
}; | ||
}; | ||
@@ -220,17 +223,2 @@ | ||
const defaultRendererOptions = {}; | ||
const createMiddleware$1 = options => store => { | ||
// When receiving an action from main | ||
electron.ipcRenderer.on(IPCEvents.ACTION, (_, action) => { | ||
store.dispatch(stopForwarding(action)); | ||
}); | ||
return next => action => { | ||
if (validateAction(action, options.denyList)) { | ||
electron.ipcRenderer.send(IPCEvents.ACTION, action); | ||
} | ||
return next(action); | ||
}; | ||
}; | ||
/** | ||
@@ -244,7 +232,4 @@ * Creates new instance of renderer process redux enhancer. | ||
const rendererStateSyncEnhancer = (options = defaultRendererOptions) => createStore => { | ||
preventDoubleInitialization(); | ||
const rendererStateSyncEnhancer = (options = {}) => createStore => { | ||
return (reducer, state) => { | ||
const middleware = createMiddleware$1(options); | ||
const initialState = options.lazyInit ? state : fetchInitialState(options); | ||
@@ -257,18 +242,55 @@ const store = createStore(options.lazyInit ? withStoreReplacer(reducer) : reducer, initialState); | ||
}); | ||
} | ||
} // When receiving an action from main | ||
let dispatch = store.dispatch; | ||
const middlewareAPI = { | ||
getState: store.getState, | ||
dispatch | ||
}; | ||
dispatch = redux.compose(middleware(middlewareAPI))(dispatch); | ||
return _objectSpread2(_objectSpread2({}, store), {}, { | ||
dispatch | ||
electron.ipcRenderer.on(IPCEvents.ACTION, (_, action) => { | ||
store.dispatch(stopForwarding(action)); | ||
}); | ||
return forwardAction(store, options); | ||
}; | ||
}; | ||
const stateSyncEnhancer = (config = {}) => { | ||
var _process; | ||
preventDoubleInitialization(); | ||
if (isRenderer) { | ||
return rendererStateSyncEnhancer(config); | ||
} else if (isMain) { | ||
return mainStateSyncEnhancer(config); | ||
} | ||
throw new Error(`Unsupported process: process.type = ${(_process = process) === null || _process === void 0 ? void 0 : _process.type}`); | ||
}; | ||
const forwardActionEnhancer = options => createStore => (reducer, preloadedState) => { | ||
const store = createStore(reducer, preloadedState); | ||
return forwardAction(store, options); | ||
}; | ||
const extensionCompose = options => (...funcs) => { | ||
return createStore => { | ||
return [stateSyncEnhancer(_objectSpread2(_objectSpread2({}, options), {}, { | ||
preventActionReplay: true | ||
})), ...funcs, forwardActionEnhancer(options)].reduceRight((composed, f) => f(composed), createStore); | ||
}; | ||
}; | ||
function composeWithStateSync(firstFuncOrOpts, ...funcs) { | ||
if (arguments.length === 0) { | ||
return stateSyncEnhancer(); | ||
} | ||
if (arguments.length === 1 && typeof firstFuncOrOpts === 'object') { | ||
return extensionCompose(firstFuncOrOpts); | ||
} | ||
return extensionCompose({})(firstFuncOrOpts, ...funcs); | ||
} | ||
exports.composeWithStateSync = composeWithStateSync; | ||
exports.mainStateSyncEnhancer = mainStateSyncEnhancer; | ||
exports.rendererStateSyncEnhancer = rendererStateSyncEnhancer; | ||
exports.stateSyncEnhancer = stateSyncEnhancer; | ||
exports.stopForwarding = stopForwarding; |
{ | ||
"name": "electron-redux", | ||
"version": "2.0.0-alpha.7", | ||
"version": "2.0.0-alpha.8", | ||
"description": "Redux & Electron: Make sure all your stores are on the same page", | ||
@@ -5,0 +5,0 @@ "repository": "https://github.com/klarna/electron-redux.git", |
@@ -38,9 +38,11 @@ 🚧 THIS IS **ALPHA** version of the library - the API still might change 🚧 | ||
electron-redux comes as a [Redux StoreEnhancer](https://redux.js.org/understanding/thinking-in-redux/glossary#store-enhancer). To initialize your stores, you just need to decorate them in the `main` and `renderer` processes of electron with their respective enhancers: | ||
### Basic setup | ||
If you have a setup without any enhancers, also including middleware, you can use the basic setup. For the basic setup, electron redux exposes a [Redux StoreEnhancer](https://redux.js.org/understanding/thinking-in-redux/glossary#store-enhancer). You simply add the enhancer to your createStore function to set it up. | ||
```ts | ||
// main.ts | ||
import { mainStateSyncEnhancer } from 'electron-redux' | ||
import { stateSyncEnhancer } from 'electron-redux' | ||
const store = createStore(reducer, mainStateSyncEnhancer()) | ||
const store = createStore(reducer, stateSyncEnhancer()) | ||
``` | ||
@@ -50,7 +52,25 @@ | ||
// renderer.ts | ||
import { rendererStateSyncEnhancer } from 'electron-redux' | ||
import { stateSyncEnhancer } from 'electron-redux' | ||
const store = createStore(reducer, rendererStateSyncEnhancer()) | ||
const store = createStore(reducer, stateSyncEnhancer()) | ||
``` | ||
### Multi-enhancer setup | ||
> This setup is required when you have other enhancers/middleware. This is especially the case for enhancers or middleware which dispatch actions, such as **redux-saga** and **redux-observable** | ||
For this setup we will use the `composeWithStateSync` function. This function is created to wrap around your enhancers, just like the [compose](https://redux.js.org/api/compose) function from redux. When using this, you will not need `stateSyncEnhancer` as this does the same thing under the hood. If you do, it will throw an error. | ||
```ts | ||
import { createStore, applyMiddleware, compose } from 'redux' | ||
import { composeWithStateSync } from 'electron-redux' | ||
const middleware = applyMiddleware(...middleware) | ||
// add other enhances here if you have any, works like `compose` from redux | ||
const enhancer: StoreEnhancer = composeWithStateSync(middleware /* ... other enhancers ... */) | ||
const store = createStore(reducer, enhancer) | ||
``` | ||
That's it! | ||
@@ -57,0 +77,0 @@ |
import { mainStateSyncEnhancer } from './mainStateSyncEnhancer'; | ||
import { stopForwarding } from './utils'; | ||
import { rendererStateSyncEnhancer } from './rendererStateSyncEnhancer'; | ||
export { mainStateSyncEnhancer, rendererStateSyncEnhancer, stopForwarding }; | ||
import { stateSyncEnhancer } from './stateSyncEnhancer'; | ||
import { composeWithStateSync } from './composeWithStateSync'; | ||
export { mainStateSyncEnhancer, rendererStateSyncEnhancer, stopForwarding, stateSyncEnhancer, composeWithStateSync, }; |
@@ -1,2 +0,3 @@ | ||
export declare type MainStateSyncEnhancerOptions = { | ||
import { StateSyncOptions } from './StateSyncOptions'; | ||
export interface MainStateSyncEnhancerOptions extends StateSyncOptions { | ||
/** | ||
@@ -8,7 +9,2 @@ * Custom store serialization function. | ||
serializer?: (this: unknown, key: string, value: unknown) => unknown; | ||
/** | ||
* Custom list for actions that should never replay across stores | ||
*/ | ||
denyList?: RegExp[]; | ||
}; | ||
export declare const defaultMainOptions: MainStateSyncEnhancerOptions; | ||
} |
@@ -1,2 +0,3 @@ | ||
export declare type RendererStateSyncEnhancerOptions = { | ||
import { StateSyncOptions } from './StateSyncOptions'; | ||
export interface RendererStateSyncEnhancerOptions extends StateSyncOptions { | ||
/** | ||
@@ -9,6 +10,2 @@ * Custom function used during de-serialization of the redux store to transform the object. | ||
/** | ||
* Custom list for actions that should never replay across stores | ||
*/ | ||
denyList?: RegExp[]; | ||
/** | ||
* By default, the renderer store is initialized from the main store synchronously. | ||
@@ -20,3 +17,2 @@ * Since the synchronous fetching of the state is blocking the renderer process until it gets the state | ||
lazyInit?: boolean; | ||
}; | ||
export declare const defaultRendererOptions: RendererStateSyncEnhancerOptions; | ||
} |
@@ -9,9 +9,3 @@ import { FluxStandardAction } from './isFSA'; | ||
*/ | ||
export declare const stopForwarding: (action: FluxStandardAction<ActionMeta>) => { | ||
meta: { | ||
scope: string; | ||
}; | ||
type: string; | ||
payload?: unknown; | ||
}; | ||
export declare const stopForwarding: (action: FluxStandardAction<ActionMeta>) => any; | ||
/** | ||
@@ -18,0 +12,0 @@ * validateAction ensures that the action meets the right format and isn't scoped locally |
@@ -11,1 +11,3 @@ export declare const preventDoubleInitialization: () => void; | ||
export declare const trimProperties: <T extends keyof X, X>(props: T[], obj: X) => Pick<X, Exclude<keyof X, T>>; | ||
export declare const isRenderer: boolean; | ||
export declare const isMain: boolean; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
35528
23
601
114