├── public ├── favicon.ico ├── tables.html ├── assets │ └── style │ │ └── application.css ├── frontend │ └── app.jsx └── index.html ├── src ├── middleware │ ├── not-found-handler.js │ ├── index.js │ └── logger.js ├── index.js ├── services │ ├── authentication │ │ └── index.js │ ├── index.js │ ├── table │ │ ├── hooks │ │ │ └── index.js │ │ └── index.js │ └── user │ │ ├── index.js │ │ └── hooks │ │ └── index.js ├── hooks │ └── index.js └── app.js ├── config ├── production.json └── default.json ├── .editorconfig ├── test ├── services │ ├── table │ │ └── index.test.js │ └── user │ │ └── index.test.js └── app.test.js ├── webpack.config.js ├── .jshintrc ├── .npmignore ├── .gitignore ├── LICENSE ├── README.md └── package.json /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ekryski/feathers-poker/master/public/favicon.ico -------------------------------------------------------------------------------- /src/middleware/not-found-handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const errors = require('feathers-errors'); 4 | 5 | module.exports = function() { 6 | return function(req, res, next) { 7 | next(new errors.NotFound('Page not found')); 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const app = require('./app'); 4 | const port = app.get('port'); 5 | const server = app.listen(port); 6 | 7 | server.on('listening', () => 8 | console.log(`Poker application started on ${app.get('host')}:${port}`) 9 | ); 10 | -------------------------------------------------------------------------------- /config/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "Poker-app.feathersjs.com", 3 | "port": 80, 4 | "nedb": "NEDB_BASE_PATH", 5 | "public": "../public/", 6 | "auth": { 7 | "token": { 8 | "secret": "FEATHERS_AUTH_SECRET" 9 | }, 10 | "local": {} 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/services/authentication/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const authentication = require('feathers-authentication'); 4 | 5 | 6 | module.exports = function() { 7 | const app = this; 8 | 9 | let config = app.get('auth'); 10 | 11 | app.configure(authentication(config)); 12 | }; 13 | -------------------------------------------------------------------------------- /test/services/table/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const app = require('../../../src/app'); 5 | 6 | describe('table service', function() { 7 | it('registered the tables service', () => { 8 | assert.ok(app.service('tables')); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/services/user/index.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const app = require('../../../src/app'); 5 | 6 | describe('user service', function() { 7 | it('registered the users service', () => { 8 | assert.ok(app.service('users')); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /public/tables.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Poker! 6 | 7 | 8 |
9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/services/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const table = require('./table'); 3 | const authentication = require('./authentication'); 4 | const user = require('./user'); 5 | 6 | module.exports = function() { 7 | const app = this; 8 | 9 | 10 | app.configure(authentication); 11 | app.configure(user); 12 | app.configure(table); 13 | }; 14 | -------------------------------------------------------------------------------- /config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "localhost", 3 | "port": 3030, 4 | "nedb": "../data/", 5 | "public": "../public/", 6 | "auth": { 7 | "token": { 8 | "secret": "W/0YgfQ+bqKJ5Xxu5DNasj7vrd3eEYWXZqIQ7MLtlOboV7SFpGSj0vROYGPmFyXY9URSynvkZLukCo12sBCPAw==" 9 | }, 10 | "local": { 11 | "usernameField": "username" 12 | }, 13 | "successRedirect": "/tables.html" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/hooks/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Add any common hooks you want to share across services in here. 4 | // 5 | // Below is an example of how a hook is written and exported. Please 6 | // see http://docs.feathersjs.com/hooks/readme.html for more details 7 | // on hooks. 8 | 9 | exports.myHook = function(options) { 10 | return function(hook) { 11 | console.log('My custom global hook ran. Feathers is awesome!'); 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /src/services/table/hooks/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const globalHooks = require('../../../hooks'); 4 | const hooks = require('feathers-hooks'); 5 | 6 | 7 | exports.before = { 8 | all: [], 9 | find: [], 10 | get: [], 11 | create: [], 12 | update: [], 13 | patch: [], 14 | remove: [] 15 | }; 16 | 17 | exports.after = { 18 | all: [], 19 | find: [], 20 | get: [], 21 | create: [], 22 | update: [], 23 | patch: [], 24 | remove: [] 25 | }; 26 | -------------------------------------------------------------------------------- /src/middleware/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const handler = require('feathers-errors/handler'); 4 | const notFound = require('./not-found-handler'); 5 | const logger = require('./logger'); 6 | 7 | module.exports = function() { 8 | // Add your custom middleware here. Remember, that 9 | // just like Express the order matters, so error 10 | // handling middleware should go last. 11 | const app = this; 12 | 13 | app.use(notFound()); 14 | app.use(logger(app)); 15 | app.use(handler()); 16 | }; 17 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | 3 | module.exports = { 4 | context: __dirname, 5 | entry: "./public/frontend/app.jsx", 6 | output: { 7 | path: path.join(__dirname, 'public'), 8 | filename: "./bundle.js" 9 | }, 10 | module: { 11 | loaders: [ 12 | { 13 | test: [/\.jsx?$/, /\.js?$/], 14 | exclude: /(node_modules|bower_components)/, 15 | loader: 'babel', 16 | query: { 17 | presets: ['es2015', 'react'] 18 | } 19 | } 20 | ] 21 | }, 22 | devtool: 'source-maps', 23 | resolve: { 24 | extensions: ["", ".js", ".jsx" ] 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/middleware/logger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const winston = require('winston'); 4 | 5 | module.exports = function(app) { 6 | // Add a logger to our app object for convenience 7 | app.logger = winston; 8 | 9 | return function(error, req, res, next) { 10 | if (error) { 11 | const message = `${error.code ? `(${error.code}) ` : '' }Route: ${req.url} - ${error.message}`; 12 | 13 | if (error.code === 404) { 14 | winston.info(message); 15 | } 16 | else { 17 | winston.error(message); 18 | winston.info(error.stack); 19 | } 20 | } 21 | 22 | next(error); 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": "nofunc", 11 | "newcap": false, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "regexp": true, 15 | "undef": true, 16 | "unused": false, 17 | "strict": false, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "white": false, 21 | "globals": { 22 | "it": true, 23 | "describe": true, 24 | "before": true, 25 | "beforeEach": true, 26 | "after": true, 27 | "afterEach": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /public/assets/style/application.css: -------------------------------------------------------------------------------- 1 | /* --- RESET --- */ 2 | 3 | html, body, header, nav, h1, h2, h3, a, ul, li, 4 | strong, main, section, img, div, p, 5 | form, fieldset, label, select, input, textarea, 6 | button, article, footer, small { 7 | margin: 0; 8 | padding: 0; 9 | border: 0; 10 | outline: 0; 11 | font: inherit; 12 | box-sizing: inherit; 13 | text-align: inherit; 14 | text-decoration: inherit; 15 | vertical-align: inherit; 16 | color: inherit; 17 | background: transparent; 18 | } 19 | 20 | body { 21 | box-sizing: content-box; 22 | font-family: sans-serif; 23 | font-size: 12px; 24 | color: #000000; 25 | background-color: #e6e8e3; 26 | } 27 | 28 | ul { 29 | list-style: none; 30 | } 31 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | 30 | data/ 31 | -------------------------------------------------------------------------------- /public/frontend/app.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | const feathers = require('feathers/client'); 4 | const socketio = require('feathers-socketio/client'); 5 | const hooks = require('feathers-hooks'); 6 | const authentication = require('feathers-authentication/client'); 7 | const io = require('socket.io-client'); 8 | 9 | const socket = io(); 10 | const app = feathers() 11 | .configure(socketio(socket)) 12 | .configure(hooks()) 13 | .configure(authentication({ 14 | storage: window.localStorage 15 | })); 16 | 17 | const tables = app.service('tables'); 18 | 19 | document.addEventListener('DOMContentLoaded', () => { 20 | ReactDOM.render(

React loaded!

, document.getElementById('root')); 21 | }); 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | 30 | lib/ 31 | data/ 32 | node_modules/ 33 | bundle.js 34 | bundle.js.map 35 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Poker! 5 | 6 | 7 |
Sign Up 8 |
9 | 10 | 11 | 12 |
13 |
14 |
Log In 15 |
16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /src/services/user/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const NeDB = require('nedb'); 5 | const service = require('feathers-nedb'); 6 | const hooks = require('./hooks'); 7 | 8 | module.exports = function(){ 9 | const app = this; 10 | 11 | const db = new NeDB({ 12 | filename: path.join(app.get('nedb'), 'users.db'), 13 | autoload: true 14 | }); 15 | 16 | let options = { 17 | Model: db, 18 | paginate: { 19 | default: 5, 20 | max: 25 21 | } 22 | }; 23 | 24 | // Initialize our service with any options it requires 25 | app.use('/users', service(options)); 26 | 27 | // Get our initialize service to that we can bind hooks 28 | const userService = app.service('/users'); 29 | 30 | // Set up our before hooks 31 | userService.before(hooks.before); 32 | 33 | // Set up our after hooks 34 | userService.after(hooks.after); 35 | }; 36 | -------------------------------------------------------------------------------- /src/services/table/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const NeDB = require('nedb'); 5 | const service = require('feathers-nedb'); 6 | const hooks = require('./hooks'); 7 | 8 | module.exports = function(){ 9 | const app = this; 10 | 11 | const db = new NeDB({ 12 | filename: path.join(app.get('nedb'), 'tables.db'), 13 | autoload: true 14 | }); 15 | 16 | let options = { 17 | Model: db, 18 | paginate: { 19 | default: 5, 20 | max: 25 21 | } 22 | }; 23 | 24 | // Initialize our service with any options it requires 25 | app.use('/tables', service(options)); 26 | 27 | // Get our initialize service to that we can bind hooks 28 | const tableService = app.service('/tables'); 29 | 30 | // Set up our before hooks 31 | tableService.before(hooks.before); 32 | 33 | // Set up our after hooks 34 | tableService.after(hooks.after); 35 | }; 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Feathers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const serveStatic = require('feathers').static; 5 | const favicon = require('serve-favicon'); 6 | const compress = require('compression'); 7 | const cors = require('cors'); 8 | const feathers = require('feathers'); 9 | const configuration = require('feathers-configuration'); 10 | const hooks = require('feathers-hooks'); 11 | const rest = require('feathers-rest'); 12 | const bodyParser = require('body-parser'); 13 | const socketio = require('feathers-socketio'); 14 | const middleware = require('./middleware'); 15 | const services = require('./services'); 16 | const authentication = require('feathers-authentication'); 17 | 18 | 19 | const app = feathers(); 20 | 21 | app.configure(configuration(path.join(__dirname, '..'))); 22 | 23 | app.use(compress()) 24 | .options('*', cors()) 25 | .use(cors()) 26 | .use(favicon( path.join(app.get('public'), 'favicon.ico') )) 27 | .use('/', serveStatic( app.get('public') )) 28 | .use(bodyParser.json()) 29 | .use(bodyParser.urlencoded({ extended: true })) 30 | .configure(hooks()) 31 | .configure(rest()) 32 | .configure(socketio()) 33 | .configure(services) 34 | .configure(middleware); 35 | 36 | module.exports = app; 37 | -------------------------------------------------------------------------------- /src/services/user/hooks/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const globalHooks = require('../../../hooks'); 4 | const hooks = require('feathers-hooks'); 5 | const auth = require('feathers-authentication').hooks; 6 | 7 | exports.before = { 8 | all: [], 9 | find: [ 10 | auth.verifyToken(), 11 | auth.populateUser(), 12 | auth.restrictToAuthenticated() 13 | ], 14 | get: [ 15 | auth.verifyToken(), 16 | auth.populateUser(), 17 | auth.restrictToAuthenticated(), 18 | auth.restrictToOwner({ ownerField: '_id' }) 19 | ], 20 | create: [ 21 | auth.hashPassword() 22 | ], 23 | update: [ 24 | auth.verifyToken(), 25 | auth.populateUser(), 26 | auth.restrictToAuthenticated(), 27 | auth.restrictToOwner({ ownerField: '_id' }) 28 | ], 29 | patch: [ 30 | auth.verifyToken(), 31 | auth.populateUser(), 32 | auth.restrictToAuthenticated(), 33 | auth.restrictToOwner({ ownerField: '_id' }) 34 | ], 35 | remove: [ 36 | auth.verifyToken(), 37 | auth.populateUser(), 38 | auth.restrictToAuthenticated(), 39 | auth.restrictToOwner({ ownerField: '_id' }) 40 | ] 41 | }; 42 | 43 | exports.after = { 44 | all: [hooks.remove('password')], 45 | find: [], 46 | get: [], 47 | create: [], 48 | update: [], 49 | patch: [], 50 | remove: [] 51 | }; 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Poker 2 | 3 | > 2-player poker game 4 | 5 | ## About 6 | 7 | This project uses [Feathers](http://feathersjs.com). An open source web framework for building modern real-time applications. 8 | 9 | ## Getting Started 10 | 11 | Getting up and running is as easy as 1, 2, 3. 12 | 13 | 1. Make sure you have [NodeJS](https://nodejs.org/) and [npm](https://www.npmjs.com/) installed. 14 | 2. Install your dependencies 15 | 16 | ``` 17 | cd path/to/Poker; npm install 18 | ``` 19 | 20 | 3. Start your app 21 | 22 | ``` 23 | npm start 24 | ``` 25 | 26 | ## Testing 27 | 28 | Simply run `npm test` and all your tests in the `test/` directory will be run. 29 | 30 | ## Scaffolding 31 | 32 | Feathers has a powerful command line interface. Here are a few things it can do: 33 | 34 | ``` 35 | $ npm install -g feathers-cli # Install Feathers CLI 36 | 37 | $ feathers generate service # Generate a new Service 38 | $ feathers generate hook # Generate a new Hook 39 | $ feathers generate model # Generate a new Model 40 | $ feathers help # Show all commands 41 | ``` 42 | 43 | ## Help 44 | 45 | For more information on all the things you can do with Feathers visit [docs.feathersjs.com](http://docs.feathersjs.com). 46 | 47 | ## Changelog 48 | 49 | __0.1.0__ 50 | 51 | - Initial release 52 | 53 | ## License 54 | 55 | Copyright (c) 2016 56 | 57 | Licensed under the [MIT license](LICENSE). 58 | -------------------------------------------------------------------------------- /test/app.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const request = require('request'); 5 | const app = require('../src/app'); 6 | 7 | describe('Feathers application tests', function() { 8 | before(function(done) { 9 | this.server = app.listen(3030); 10 | this.server.once('listening', () => done()); 11 | }); 12 | 13 | after(function(done) { 14 | this.server.close(done); 15 | }); 16 | 17 | it('starts and shows the index page', function(done) { 18 | request('http://localhost:3030', function(err, res, body) { 19 | assert.ok(body.indexOf('') !== -1); 20 | done(err); 21 | }); 22 | }); 23 | 24 | describe('404', function() { 25 | it('shows a 404 HTML page', function(done) { 26 | request({ 27 | url: 'http://localhost:3030/path/to/nowhere', 28 | headers: { 29 | 'Accept': 'text/html' 30 | } 31 | }, function(err, res, body) { 32 | assert.equal(res.statusCode, 404); 33 | assert.ok(body.indexOf('') !== -1); 34 | done(err); 35 | }); 36 | }); 37 | 38 | it('shows a 404 JSON error without stack trace', function(done) { 39 | request({ 40 | url: 'http://localhost:3030/path/to/nowhere', 41 | json: true 42 | }, function(err, res, body) { 43 | assert.equal(res.statusCode, 404); 44 | assert.equal(body.code, 404); 45 | assert.equal(body.message, 'Page not found'); 46 | assert.equal(body.name, 'NotFound'); 47 | done(err); 48 | }); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Poker", 3 | "description": "2-player poker game", 4 | "version": "0.0.0", 5 | "homepage": "", 6 | "main": "src/", 7 | "keywords": [ 8 | "feathers" 9 | ], 10 | "license": "MIT", 11 | "repository": {}, 12 | "author": {}, 13 | "contributors": [], 14 | "bugs": {}, 15 | "engines": { 16 | "node": ">= 0.12.0" 17 | }, 18 | "scripts": { 19 | "test": "npm run jshint && npm run mocha", 20 | "jshint": "jshint src/. test/. --config", 21 | "start": "node src/", 22 | "mocha": "mocha test/ --recursive", 23 | "webpack": "./node_modules/.bin/webpack --watch" 24 | }, 25 | "dependencies": { 26 | "babel-core": "^6.17.0", 27 | "babel-loader": "^6.2.5", 28 | "babel-preset-es2015": "^6.16.0", 29 | "babel-preset-react": "^6.16.0", 30 | "body-parser": "^1.15.2", 31 | "compression": "^1.6.2", 32 | "cors": "^2.8.1", 33 | "feathers": "^2.0.2", 34 | "feathers-authentication": "^0.7.11", 35 | "feathers-configuration": "^0.3.3", 36 | "feathers-errors": "^2.4.0", 37 | "feathers-hooks": "^1.5.8", 38 | "feathers-nedb": "^2.5.1", 39 | "feathers-rest": "^1.5.0", 40 | "feathers-socketio": "^1.4.1", 41 | "lodash": "^4.16.4", 42 | "nedb": "^1.8.0", 43 | "passport": "^0.3.2", 44 | "poker-evaluator": "^0.3.1", 45 | "react": "^15.3.2", 46 | "react-dom": "^15.3.2", 47 | "serve-favicon": "^2.3.0", 48 | "webpack": "^1.13.2", 49 | "winston": "^2.2.0" 50 | }, 51 | "devDependencies": { 52 | "jshint": "^2.9.4", 53 | "mocha": "^3.1.2", 54 | "request": "^2.75.0" 55 | } 56 | } 57 | --------------------------------------------------------------------------------