├── .npmignore ├── .babelrc ├── .gitignore ├── .travis.yml ├── src ├── index.js ├── guard.js ├── createObservable.js ├── createIpcDataSource.js └── installIpcHandler.js ├── .eslintrc ├── package.json ├── README.md ├── test └── falcorElectron.test.js └── CODE_OF_CONDUCT.md /.npmignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | .DS_STORE 3 | test/ 4 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-1"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | node_modules/ 3 | npm-debug.log 4 | .DS_STORE 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.0.0" 4 | script: 5 | - npm run check 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export installIpcHandler from './installIpcHandler.js'; 2 | export createIpcDataSource from './createIpcDataSource.js'; 3 | -------------------------------------------------------------------------------- /src/guard.js: -------------------------------------------------------------------------------- 1 | export default function guard(pairs) { 2 | return pairs.reverse().reduce((result, [predicate, value]) => { 3 | return predicate ? value : result; 4 | }, null); 5 | } 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "env": { 4 | "browser": true, 5 | "node": true 6 | }, 7 | "rules": { 8 | "comma-dangle": 0, 9 | "no-shadow": 0 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/createObservable.js: -------------------------------------------------------------------------------- 1 | import partial from 'lodash.partial'; 2 | 3 | function noop() { } 4 | 5 | function subscribe(subscriber, onNext, onError = noop, onCompleted = noop) { 6 | const observer = typeof onNext === 'function' ? 7 | { onNext, onError, onCompleted } : 8 | onNext; 9 | 10 | const disposable = subscriber(observer); 11 | 12 | return typeof disposable === 'function' ? 13 | { dispose: disposable } : 14 | disposable; 15 | } 16 | 17 | export default function createObservable(subscriber) { 18 | return { 19 | subscribe: partial(subscribe, subscriber) 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "falcor-electron", 3 | "version": "0.2.0", 4 | "description": "falcor integration for electron's ipc module", 5 | "main": "lib/index.js", 6 | "jsnext:main": "src/index.js", 7 | "scripts": { 8 | "clean": "rimraf lib", 9 | "lint": "eslint src", 10 | "test": "babel-node ./node_modules/tape/bin/tape ./test/*.test.js", 11 | "check": "npm run lint && npm run test", 12 | "build": "babel src --out-dir lib", 13 | "preversion": "npm run clean && npm run check", 14 | "version": "npm run build", 15 | "postversion": "git push && git push --tags && npm run clean", 16 | "prepublish": "npm run clean && npm run build" 17 | }, 18 | "author": "Luke William Westby ", 19 | "license": "MIT", 20 | "peerDependencies": { 21 | "falcor": "0.1.x", 22 | "falcor-router": "0.2.x" 23 | }, 24 | "devDependencies": { 25 | "babel": "6.0.15", 26 | "babel-cli": "6.1.2", 27 | "babel-eslint": "4.1.4", 28 | "babel-preset-es2015": "6.1.2", 29 | "babel-preset-stage-1": "6.1.2", 30 | "eslint": "1.6.0", 31 | "eslint-config-airbnb": "0.1.0", 32 | "eslint-plugin-react": "3.5.1", 33 | "falcor": "0.1.14", 34 | "falcor-router": "0.2.11", 35 | "rimraf": "2.4.3", 36 | "tape": "4.2.1" 37 | }, 38 | "dependencies": { 39 | "lodash.partial": "3.1.1", 40 | "node-uuid": "1.4.3" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/createIpcDataSource.js: -------------------------------------------------------------------------------- 1 | import uuid from 'node-uuid'; 2 | import createObservable from './createObservable.js'; 3 | 4 | function createRequest(ipc, context) { 5 | return createObservable((observer) => { 6 | const currentRequestId = uuid.v1(); 7 | ipc.on('falcor:response', function handle({ requestId, response }) { 8 | if (requestId !== currentRequestId) { 9 | return; 10 | } 11 | ipc.removeListener('falcor:response', handle); 12 | if (response.error) { 13 | observer.onError(Error(response.error)); 14 | } else { 15 | observer.onNext(response.data); 16 | observer.onCompleted(); 17 | } 18 | }); 19 | ipc.send('falcor:request', { 20 | requestId: currentRequestId, 21 | context 22 | }); 23 | }); 24 | } 25 | 26 | export default function createIpcDataSource(ipc) { 27 | return { 28 | get(paths) { 29 | return createRequest(ipc, { 30 | method: 'get', 31 | paths 32 | }); 33 | }, 34 | 35 | set(jsonGraph) { 36 | return createRequest(ipc, { 37 | method: 'set', 38 | jsonGraph 39 | }); 40 | }, 41 | 42 | call(callPath, args = [], pathSuffixes = [], paths = []) { 43 | return createRequest(ipc, { 44 | callPath, 45 | pathSuffixes, 46 | paths, 47 | arguments: args, 48 | method: 'call' 49 | }); 50 | } 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /src/installIpcHandler.js: -------------------------------------------------------------------------------- 1 | import guard from './guard.js'; 2 | 3 | export default function installIpcHandler(ipc, getDataSource) { 4 | function sendResponse(sender, requestId, response) { 5 | sender.send('falcor:response', { 6 | requestId, 7 | response 8 | }); 9 | } 10 | 11 | function handleRequest({ 12 | sender 13 | }, { 14 | context, 15 | requestId 16 | }) { 17 | const dataSource = getDataSource(); 18 | 19 | const error = guard([ 20 | [Object.keys(context).length === 0, 'Request not supported.'], 21 | [typeof context.method === 'undefined', 'No query method provided.'], 22 | [typeof dataSource[context.method] === 'undefined', 'Data source does not implement the requested method.'] 23 | ]); 24 | 25 | if (error) { 26 | return sendResponse(sender, requestId, { error }); 27 | } 28 | 29 | const args = guard([ 30 | [context.method === 'set', [context.jsonGraph]], 31 | [context.method === 'call', [context.callPath, context.arguments, context.pathSuffixes, context.paths]], 32 | [true, [[].concat(context.paths)]] 33 | ]); 34 | 35 | const observable = dataSource[context.method](...args); 36 | 37 | observable.subscribe( 38 | (data) => sendResponse(sender, requestId, { data }), 39 | (error) => sendResponse(sender, requestId, { error: error.message }) 40 | ); 41 | } 42 | 43 | ipc.on('falcor:request', handleRequest); 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # falcor-electron 2 | [![Build Status](https://travis-ci.org/lukewestby/falcor-electron.svg)](https://travis-ci.org/lukewestby/falcor-electron) 3 | 4 | [Falcor](https://github.com/Netflix/falcor) integration for 5 | [electron](https://github.com/atom/electron) using the `ipc` module. 6 | 7 | ## Usage 8 | 9 | Install from npm. 10 | 11 | ``` 12 | npm install --save falcor-electron 13 | ``` 14 | 15 | Install your `Router` as a data source in the main process. 16 | 17 | ```javascript 18 | import ipc from 'ipc'; 19 | import Router from 'falcor-router'; 20 | import { installIpcHandler } from 'falcor-electron'; 21 | 22 | installIpcHandler(ipc, () => { 23 | return new Router([ 24 | // ... routes 25 | ]); 26 | }); 27 | ``` 28 | 29 | Configure your `Model` with an `IpcDataSource` in the renderer process. 30 | 31 | ```javascript 32 | import ipc from 'ipc'; 33 | import { Model } from 'falcor'; 34 | import { createIpcDataSource } from 'falcor-electron'; 35 | 36 | const model = new Model({ 37 | source: createIpcDataSource(ipc) 38 | }); 39 | ``` 40 | 41 | ## Contributing 42 | 43 | Feature requests and bugs/bug fixes are happily accepted and can be submitted 44 | either as issues or pull requests. 45 | 46 | - Source is compiled with `babel` 47 | - Presets: 48 | - `es2015` 49 | - `stage-1` 50 | - Source is linted with `eslint` 51 | - Tests are run with `tape` 52 | 53 | For code contributions, please fork `develop` and submit a PR. 54 | -------------------------------------------------------------------------------- /test/falcorElectron.test.js: -------------------------------------------------------------------------------- 1 | import test from 'tape'; 2 | import { installIpcHandler, createIpcDataSource } from '../src/index.js'; 3 | import Router from 'falcor-router'; 4 | import { Model } from 'falcor'; 5 | import EventEmitter from 'events'; 6 | 7 | function createIpc() { 8 | const ipc = new EventEmitter(); 9 | ipc.send = function(name, ...args) { 10 | ipc.emit(name, { 11 | sender: { 12 | send(name, ...args) { 13 | ipc.emit(name, ...args); 14 | } 15 | } 16 | }, ...args); 17 | }; 18 | return ipc; 19 | } 20 | 21 | function setupTest() { 22 | 23 | const ipc = createIpc(); 24 | const db = { test: 1 }; 25 | 26 | installIpcHandler(ipc, () => { 27 | return new Router([ 28 | { 29 | route: 'test', 30 | get() { 31 | return { path: ['test'], value: db.test }; 32 | }, 33 | set(jsonGraph) { 34 | db.test = jsonGraph.test; 35 | return { path: ['test'], value: db.test }; 36 | } 37 | }, 38 | { 39 | route: 'addValue', 40 | call(callPath, [name, value]) { 41 | db[name] = value; 42 | return { path: [name], value }; 43 | } 44 | } 45 | ]); 46 | }); 47 | 48 | return new Model({ 49 | source: createIpcDataSource(ipc) 50 | }); 51 | } 52 | 53 | test('IpcDataSource', (t) => { 54 | 55 | t.test('get()', (t) => { 56 | t.plan(1); 57 | 58 | const model = setupTest(); 59 | 60 | model 61 | .get(['test']) 62 | .then((response) => { 63 | t.deepEqual(response, { json: { test: 1 } }, 'get should succeed'); 64 | }); 65 | }); 66 | 67 | t.test('set()', (t) => { 68 | t.plan(1); 69 | 70 | const model = setupTest(); 71 | 72 | model 73 | .set({ path: ['test'], value: 2 }) 74 | .then((response) => { 75 | t.deepEqual(response, { json: { test: 2 } }, 'set should succeed'); 76 | }); 77 | }); 78 | 79 | t.test('call()', (t) => { 80 | t.plan(1); 81 | 82 | const model = setupTest(); 83 | 84 | model 85 | .call(['addValue'], ['test1', 4]) 86 | .then((response) => { 87 | t.deepEqual(response, { json: { test1: 4 } }, 'call should succeed'); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at opensource@lukewestby.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | --------------------------------------------------------------------------------