+
material example
}
showMenuIconButton={false}
iconElementRight={rightMenu}
- iconStyleRight={accountExists ? avatarStyles.wrapper : {}}
+ iconStyleRight={profileExists ? avatarStyles.wrapper : {}}
className={classes.appBar}
/>
)
diff --git a/examples/complete/material/src/routes/Account/components/AccountForm/AccountForm.js b/examples/complete/material/src/routes/Account/components/AccountForm/AccountForm.js
index 45c7d28fe..4e1dd7d83 100644
--- a/examples/complete/material/src/routes/Account/components/AccountForm/AccountForm.js
+++ b/examples/complete/material/src/routes/Account/components/AccountForm/AccountForm.js
@@ -1,4 +1,5 @@
-import React, { PropTypes } from 'react'
+import React from 'react'
+import PropTypes from 'prop-types'
import { Field, reduxForm } from 'redux-form'
import RaisedButton from 'material-ui/RaisedButton'
import TextField from 'components/TextField'
diff --git a/examples/complete/material/src/routes/Account/components/ProviderDataForm/ProviderDataForm.js b/examples/complete/material/src/routes/Account/components/ProviderDataForm/ProviderDataForm.js
index f06be1f0f..fbf906afc 100644
--- a/examples/complete/material/src/routes/Account/components/ProviderDataForm/ProviderDataForm.js
+++ b/examples/complete/material/src/routes/Account/components/ProviderDataForm/ProviderDataForm.js
@@ -1,4 +1,5 @@
-import React, { PropTypes } from 'react'
+import React from 'react'
+import PropTypes from 'prop-types'
import { List, ListItem } from 'material-ui/List'
import classes from './ProviderDataForm.scss'
import AccountCircle from 'material-ui/svg-icons/action/account-circle'
diff --git a/examples/complete/material/src/routes/Account/containers/AccountContainer.js b/examples/complete/material/src/routes/Account/containers/AccountContainer.js
index a79dc3eff..dd08a8732 100644
--- a/examples/complete/material/src/routes/Account/containers/AccountContainer.js
+++ b/examples/complete/material/src/routes/Account/containers/AccountContainer.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import Paper from 'material-ui/Paper'
import { connect } from 'react-redux'
import { firebaseConnect, pathToJS, isLoaded } from 'react-redux-firebase'
@@ -12,14 +13,14 @@ import classes from './AccountContainer.scss'
@UserIsAuthenticated // redirect to /login if user is not authenticated
@firebaseConnect() // add this.props.firebase
@connect( // Map redux state to props
- ({ firebase }) => ({
- auth: pathToJS(firebase, 'auth'),
- account: pathToJS(firebase, 'profile')
+ ({ firebase: { auth, profile } }) => ({
+ auth,
+ profile
})
)
export default class Account extends Component {
static propTypes = {
- account: PropTypes.object,
+ profile: PropTypes.object,
auth: PropTypes.shape({
uid: PropTypes.string
}),
@@ -41,16 +42,16 @@ export default class Account extends Component {
updateAccount = (newData) =>
this.props.firebase
- .update(`${rfConfig.userProfile}/${this.props.auth.uid}`, newData)
+ .updateProfile(newData)
.catch((err) => {
console.error('Error updating account', err) // eslint-disable-line no-console
// TODO: Display error to user
})
render () {
- const { account } = this.props
+ const { profile } = this.props
- if (!isLoaded(account)) {
+ if (!isLoaded(profile)) {
return
}
@@ -61,14 +62,14 @@ export default class Account extends Component {
diff --git a/examples/complete/material/src/routes/Home/components/NewTodoPanel/NewTodoPanel.js b/examples/complete/material/src/routes/Home/components/NewTodoPanel/NewTodoPanel.js
index 3ef8702c3..98b383757 100755
--- a/examples/complete/material/src/routes/Home/components/NewTodoPanel/NewTodoPanel.js
+++ b/examples/complete/material/src/routes/Home/components/NewTodoPanel/NewTodoPanel.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import IconButton from 'material-ui/IconButton'
import Paper from 'material-ui/Paper'
import TextField from 'material-ui/TextField'
diff --git a/examples/complete/material/src/routes/Home/components/TodoItem/TodoItem.js b/examples/complete/material/src/routes/Home/components/TodoItem/TodoItem.js
index f45c9581c..2c427010b 100755
--- a/examples/complete/material/src/routes/Home/components/TodoItem/TodoItem.js
+++ b/examples/complete/material/src/routes/Home/components/TodoItem/TodoItem.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import classes from './TodoItem.scss'
import { ListItem } from 'material-ui/List'
import Checkbox from 'material-ui/Checkbox'
diff --git a/examples/complete/material/src/routes/Home/containers/HomeContainer.js b/examples/complete/material/src/routes/Home/containers/HomeContainer.js
index 6f718b1ac..bfac5190d 100755
--- a/examples/complete/material/src/routes/Home/containers/HomeContainer.js
+++ b/examples/complete/material/src/routes/Home/containers/HomeContainer.js
@@ -1,14 +1,12 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { map } from 'lodash'
import Theme from 'theme'
import {
firebaseConnect,
isLoaded,
- pathToJS,
- dataToJS // needed for full list and once
- // orderedToJS // needed for ordered list
- // populatedDataToJS // needed for populated list
+ populate // for populated list
} from 'react-redux-firebase'
import CircularProgress from 'material-ui/CircularProgress'
import Snackbar from 'material-ui/Snackbar'
@@ -19,21 +17,22 @@ import TodoItem from '../components/TodoItem'
import NewTodoPanel from '../components/NewTodoPanel'
import classes from './HomeContainer.scss'
-// const populates = [{ child: 'owner', root: 'users', keyProp: 'uid' }]
+const populates = [{ child: 'owner', root: 'users' }]
@firebaseConnect([
// 'todos' // sync full list of todos
// { path: 'todos', type: 'once' } // for loading once instead of binding
- { path: 'todos', queryParams: ['orderByKey', 'limitToLast=5'] } // 10 most recent
- // { path: 'todos', populates } // populate
+ // { path: 'todos', queryParams: ['orderByKey', 'limitToLast=5'] } // 10 most recent
+ { path: 'todos', populates } // populate
])
@connect(
- ({firebase}) => ({
- auth: pathToJS(firebase, 'auth'),
- account: pathToJS(firebase, 'profile'),
- todos: dataToJS(firebase, 'todos')
- // todos: populatedDataToJS(firebase, '/todos', populates), // if populating
- // todos: orderedToJS(firebase, '/todos') // if using ordering such as orderByChild
+ // get auth, profile, and data from
+ ({ firebase, firebase: { auth, profile, data: { todos } } }) => ({
+ auth,
+ profile,
+ // todos,
+ todos: populate(firebase, 'todos', populates), // if populating
+ // todos: firebase.ordered.todos // if using ordering such as orderByChild
})
)
export default class Home extends Component {
@@ -102,7 +101,7 @@ export default class Home extends Component {
render () {
const { todos } = this.props
const { error } = this.state
-
+ console.log('todos: ', todos)
return (
{
diff --git a/examples/complete/material/src/routes/Login/components/LoginForm/LoginForm.js b/examples/complete/material/src/routes/Login/components/LoginForm/LoginForm.js
index c0aac901c..b331d5b5f 100644
--- a/examples/complete/material/src/routes/Login/components/LoginForm/LoginForm.js
+++ b/examples/complete/material/src/routes/Login/components/LoginForm/LoginForm.js
@@ -1,4 +1,5 @@
-import React, { PropTypes } from 'react'
+import React from 'react'
+import PropTypes from 'prop-types'
import { Link } from 'react-router'
import { Field, reduxForm } from 'redux-form'
import RaisedButton from 'material-ui/RaisedButton'
diff --git a/examples/complete/material/src/routes/Login/containers/LoginContainer.js b/examples/complete/material/src/routes/Login/containers/LoginContainer.js
index 1a6ff6e5c..3d29df527 100644
--- a/examples/complete/material/src/routes/Login/containers/LoginContainer.js
+++ b/examples/complete/material/src/routes/Login/containers/LoginContainer.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import { Link } from 'react-router'
import GoogleButton from 'react-google-button'
import { connect } from 'react-redux'
diff --git a/examples/complete/material/src/routes/Projects/components/NewProjectDialog/NewProjectDialog.js b/examples/complete/material/src/routes/Projects/components/NewProjectDialog/NewProjectDialog.js
index 8eed2d23a..42ae6d33d 100644
--- a/examples/complete/material/src/routes/Projects/components/NewProjectDialog/NewProjectDialog.js
+++ b/examples/complete/material/src/routes/Projects/components/NewProjectDialog/NewProjectDialog.js
@@ -1,5 +1,6 @@
-import React, { Component, PropTypes } from 'react'
-import { Field, reduxForm } from 'redux-form'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import { reduxForm } from 'redux-form'
import Dialog from 'material-ui/Dialog'
import FlatButton from 'material-ui/FlatButton'
import TextField from 'components/TextField'
diff --git a/examples/complete/material/src/routes/Projects/components/NewProjectTile/NewProjectTile.js b/examples/complete/material/src/routes/Projects/components/NewProjectTile/NewProjectTile.js
index 006996ba5..95a8df5a1 100644
--- a/examples/complete/material/src/routes/Projects/components/NewProjectTile/NewProjectTile.js
+++ b/examples/complete/material/src/routes/Projects/components/NewProjectTile/NewProjectTile.js
@@ -1,4 +1,5 @@
-import React, { PropTypes } from 'react'
+import React from 'react'
+import PropTypes from 'prop-types'
import Paper from 'material-ui/Paper'
import ContentAddCircle from 'material-ui/svg-icons/content/add-circle'
diff --git a/examples/complete/material/src/routes/Projects/components/ProjectTile/ProjectTile.js b/examples/complete/material/src/routes/Projects/components/ProjectTile/ProjectTile.js
index 4575f3daa..0d75ebb0e 100644
--- a/examples/complete/material/src/routes/Projects/components/ProjectTile/ProjectTile.js
+++ b/examples/complete/material/src/routes/Projects/components/ProjectTile/ProjectTile.js
@@ -1,4 +1,5 @@
-import React, { PropTypes } from 'react'
+import React from 'react'
+import PropTypes from 'prop-types'
import Paper from 'material-ui/Paper'
import { isObject } from 'lodash'
import IconButton from 'material-ui/IconButton'
diff --git a/examples/complete/material/src/routes/Projects/containers/ProjectsContainer.js b/examples/complete/material/src/routes/Projects/containers/ProjectsContainer.js
index e8c681dde..c6a8fe891 100644
--- a/examples/complete/material/src/routes/Projects/containers/ProjectsContainer.js
+++ b/examples/complete/material/src/routes/Projects/containers/ProjectsContainer.js
@@ -1,4 +1,5 @@
-import React, { Component, cloneElement, PropTypes } from 'react'
+import React, { Component, cloneElement } from 'react'
+import PropTypes from 'prop-types'
import { map } from 'lodash'
import { connect } from 'react-redux'
import {
@@ -28,7 +29,7 @@ const populates = [
@connect(
({ firebase }, { params }) => ({
auth: pathToJS(firebase, 'auth'),
- projects: populatedDataToJS(firebase, 'projects', populates)
+ projects: firebase.data.projects
})
)
export default class Projects extends Component {
diff --git a/examples/complete/material/src/routes/Projects/routes/Project/components/Project/Project.js b/examples/complete/material/src/routes/Projects/routes/Project/components/Project/Project.js
index e8da92b71..6631177ec 100644
--- a/examples/complete/material/src/routes/Projects/routes/Project/components/Project/Project.js
+++ b/examples/complete/material/src/routes/Projects/routes/Project/components/Project/Project.js
@@ -1,4 +1,5 @@
-import React, { PropTypes } from 'react'
+import React from 'react'
+import PropTypes from 'prop-types'
import classes from './Project.scss'
export const Project = ({ projects, params: { projectname } }) => (
diff --git a/examples/complete/material/src/routes/Recover/components/RecoverForm/RecoverForm.js b/examples/complete/material/src/routes/Recover/components/RecoverForm/RecoverForm.js
index ca673ddd5..41aabd3f4 100644
--- a/examples/complete/material/src/routes/Recover/components/RecoverForm/RecoverForm.js
+++ b/examples/complete/material/src/routes/Recover/components/RecoverForm/RecoverForm.js
@@ -1,4 +1,5 @@
-import React, { PropTypes } from 'react'
+import React from 'react'
+import PropTypes from 'prop-types'
import { Field, reduxForm } from 'redux-form'
import RaisedButton from 'material-ui/RaisedButton'
import Subheader from 'material-ui/Subheader'
diff --git a/examples/complete/material/src/routes/Recover/containers/RecoverContainer.js b/examples/complete/material/src/routes/Recover/containers/RecoverContainer.js
index 561b4c679..212f713c9 100644
--- a/examples/complete/material/src/routes/Recover/containers/RecoverContainer.js
+++ b/examples/complete/material/src/routes/Recover/containers/RecoverContainer.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import { firebaseConnect } from 'react-redux-firebase'
import Snackbar from 'material-ui/Snackbar'
import Paper from 'material-ui/Paper'
diff --git a/examples/complete/material/src/routes/Signup/components/SignupForm/SignupForm.js b/examples/complete/material/src/routes/Signup/components/SignupForm/SignupForm.js
index 8757d5621..35cef55b0 100644
--- a/examples/complete/material/src/routes/Signup/components/SignupForm/SignupForm.js
+++ b/examples/complete/material/src/routes/Signup/components/SignupForm/SignupForm.js
@@ -1,4 +1,5 @@
-import React, { PropTypes } from 'react'
+import React from 'react'
+import PropTypes from 'prop-types'
import RaisedButton from 'material-ui/RaisedButton'
import { Field, reduxForm } from 'redux-form'
import TextField from 'components/TextField'
diff --git a/examples/complete/material/src/routes/Signup/containers/SignupContainer.js b/examples/complete/material/src/routes/Signup/containers/SignupContainer.js
index 8e173459a..b543faae1 100644
--- a/examples/complete/material/src/routes/Signup/containers/SignupContainer.js
+++ b/examples/complete/material/src/routes/Signup/containers/SignupContainer.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import { Link } from 'react-router'
import GoogleButton from 'react-google-button'
import { connect } from 'react-redux'
diff --git a/examples/complete/material/src/store/createStore.js b/examples/complete/material/src/store/createStore.js
index 0727b6995..f7142252f 100755
--- a/examples/complete/material/src/store/createStore.js
+++ b/examples/complete/material/src/store/createStore.js
@@ -1,23 +1,23 @@
import { applyMiddleware, compose, createStore } from 'redux'
import thunk from 'redux-thunk'
-import makeRootReducer from './reducers'
import { browserHistory } from 'react-router'
-import { reactReduxFirebase, getFirebase } from 'react-redux-firebase'
+import { reactReduxFirebase, getFirebase, toJS } from 'react-redux-firebase'
+import logger from 'redux-logger'
+import * as firebase from 'firebase/app'
+import 'firebase/auth'
+import 'firebase/database'
+import 'firebase/storage'
import { firebase as fbConfig, reduxFirebase as reduxConfig } from '../config'
-import { version } from '../../package.json'
+import makeRootReducer from './reducers'
import { updateLocation } from './location'
export default (initialState = {}, history) => {
- // ======================================================
- // Window Vars Config
- // ======================================================
- window.version = version
-
// ======================================================
// Middleware Configuration
// ======================================================
const middleware = [
- thunk.withExtraArgument(getFirebase)
+ thunk.withExtraArgument(getFirebase),
+ // logger
// This is where you add other middleware like redux-observable
]
@@ -32,6 +32,8 @@ export default (initialState = {}, history) => {
}
}
+ firebase.initializeApp(fbConfig)
+
// ======================================================
// Store Instantiation and HMR Setup
// ======================================================
@@ -39,8 +41,8 @@ export default (initialState = {}, history) => {
makeRootReducer(),
initialState,
compose(
+ reactReduxFirebase(firebase, reduxConfig),
applyMiddleware(...middleware),
- reactReduxFirebase(fbConfig, reduxConfig),
...enhancers
)
)
diff --git a/examples/complete/material/src/utils/router.js b/examples/complete/material/src/utils/router.js
index 6c75e3b57..f64e78be6 100644
--- a/examples/complete/material/src/utils/router.js
+++ b/examples/complete/material/src/utils/router.js
@@ -16,10 +16,9 @@ const UNAUTHED_REDIRECT = 'UNAUTHED_REDIRECT'
export const UserIsAuthenticated = UserAuthWrapper({ // eslint-disable-line new-cap
wrapperDisplayName: 'UserIsAuthenticated',
LoadingComponent: LoadingSpinner,
- authSelector: ({ firebase }) => pathToJS(firebase, 'auth'),
- authenticatingSelector: ({ firebase }) =>
- (pathToJS(firebase, 'auth') === undefined) ||
- (pathToJS(firebase, 'isInitializing') === true),
+ authSelector: ({ firebase: { auth } }) => auth,
+ authenticatingSelector: ({ firebase: { auth, isInitializing } }) =>
+ auth === undefined || isInitializing === true,
predicate: auth => auth !== null,
redirectAction: newLoc => (dispatch) => {
browserHistory.replace(newLoc)
@@ -45,10 +44,9 @@ export const UserIsNotAuthenticated = UserAuthWrapper({ // eslint-disable-line n
failureRedirectPath: (state, props) =>
// redirect to page user was on or to list path
props.location.query.redirect || LIST_PATH,
- authSelector: ({ firebase }) => pathToJS(firebase, 'auth'),
- authenticatingSelector: ({ firebase }) =>
- (pathToJS(firebase, 'auth') === undefined) ||
- (pathToJS(firebase, 'isInitializing') === true),
+ authSelector: ({ firebase: { auth } }) => auth,
+ authenticatingSelector: ({ firebase: { auth, isInitializing } }) =>
+ auth === undefined || isInitializing === true,
predicate: auth => auth === null,
redirectAction: newLoc => (dispatch) => {
browserHistory.replace(newLoc)
diff --git a/examples/complete/material/yarn.lock b/examples/complete/material/yarn.lock
index a641463ea..2ded603fe 100644
--- a/examples/complete/material/yarn.lock
+++ b/examples/complete/material/yarn.lock
@@ -1521,6 +1521,10 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+deep-diff@^0.3.5:
+ version "0.3.8"
+ resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84"
+
deep-equal@^1.0.0, deep-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
@@ -2160,7 +2164,7 @@ find-up@^1.0.0, find-up@^1.1.2:
path-exists "^2.0.0"
pinkie-promise "^2.0.0"
-firebase@^3.7.5:
+firebase@^3.9.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/firebase/-/firebase-3.9.0.tgz#c4237f50f58eeb25081b1839d6cbf175f8f7ed9b"
dependencies:
@@ -2943,7 +2947,7 @@ json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1:
dependencies:
jsonify "~0.0.0"
-json-stringify-safe@~5.0.1:
+json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -3079,7 +3083,7 @@ loader-utils@^1.0.2:
emojis-list "^2.0.0"
json5 "^0.5.0"
-lodash-es@^4.17.3, lodash-es@^4.2.1:
+lodash-es@^4.17.3, lodash-es@^4.17.4, lodash-es@^4.2.1:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7"
@@ -4194,7 +4198,7 @@ promise@^7.0.3, promise@^7.1.1:
dependencies:
asap "~2.0.3"
-prop-types@15.5.8, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8:
+prop-types@15.5.8, prop-types@^15.5.4, prop-types@^15.5.6:
version "15.5.8"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394"
dependencies:
@@ -4313,12 +4317,12 @@ react-google-button@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/react-google-button/-/react-google-button-0.1.0.tgz#dc586526f2bfd5c502088d91f338b9b437b85cd5"
-react-redux-firebase@^1.4.0-rc.1:
- version "1.4.0-rc.1"
- resolved "https://registry.yarnpkg.com/react-redux-firebase/-/react-redux-firebase-1.4.0-rc.1.tgz#cabace23fac997e1e2a30843d7e4d0dfc57f71db"
+react-redux-firebase@*:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/react-redux-firebase/-/react-redux-firebase-1.4.0.tgz#e3225622bdb4ecfbddfca5ac5df17d2366ab0384"
dependencies:
es6-promise "^4.1.0"
- firebase "^3.7.5"
+ firebase "^3.9.0"
hoist-non-react-statics "^1.2.0"
immutable "^3.8.1"
jwt-decode "^2.2.0"
@@ -4484,6 +4488,20 @@ redux-form@^6.6.1:
lodash-es "^4.17.3"
prop-types "^15.5.6"
+redux-logger@^3.0.6:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf"
+ dependencies:
+ deep-diff "^0.3.5"
+
+redux-persist@^4.8.0:
+ version "4.8.0"
+ resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-4.8.0.tgz#17fd998949bdeef9275e4cf60ad5bbe1c73675fc"
+ dependencies:
+ json-stringify-safe "^5.0.1"
+ lodash "^4.17.4"
+ lodash-es "^4.17.4"
+
redux-thunk@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5"
diff --git a/examples/complete/react-native/package.json b/examples/complete/react-native/package.json
index 0dc98d97f..43708e3f7 100644
--- a/examples/complete/react-native/package.json
+++ b/examples/complete/react-native/package.json
@@ -9,11 +9,12 @@
"dev": "concurrently 'npm run watch' 'npm run packager'"
},
"dependencies": {
+ "firebase": "^4.1.2",
"react": "15.4.1",
"react-native": "0.42.0",
"react-native-google-signin": "0.9.0",
"react-redux": "^5.0.3",
- "react-redux-firebase": "^1.4.0-alpha",
+ "react-redux-firebase": "^1.4.0",
"redux": "^3.6.0"
},
"devDependencies": {
diff --git a/examples/complete/react-native/src/store.js b/examples/complete/react-native/src/store.js
index 14a6b8dd1..222e6c9e2 100644
--- a/examples/complete/react-native/src/store.js
+++ b/examples/complete/react-native/src/store.js
@@ -3,14 +3,17 @@ import rootReducer from './reducer'
import { firebase as fbConfig } from './config'
import { reactReduxFirebase } from 'react-redux-firebase'
import { AsyncStorage } from 'react-native'
+import firebase from 'firebase'
+
+firebase.initializeApp(fbConfig)
export default function configureStore (initialState, history) {
const createStoreWithMiddleware = compose(
- reactReduxFirebase(fbConfig,
+ reactReduxFirebase(firebase,
{
userProfile: 'users',
enableLogging: false,
- ReactNative: { AsyncStorage },
+ // ReactNative: { AsyncStorage },
}
),
typeof window === 'object' && typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f
diff --git a/examples/complete/simple/package.json b/examples/complete/simple/package.json
index 2d45f4dee..08cd81954 100644
--- a/examples/complete/simple/package.json
+++ b/examples/complete/simple/package.json
@@ -6,6 +6,7 @@
"react-scripts": "0.4.1"
},
"dependencies": {
+ "prop-types": "^15.5.8",
"react": "^15.3.1",
"react-dom": "^15.3.1",
"react-redux": "^4.4.5",
diff --git a/examples/complete/simple/src/Home.js b/examples/complete/simple/src/Home.js
index 534c7c253..23840ff0b 100644
--- a/examples/complete/simple/src/Home.js
+++ b/examples/complete/simple/src/Home.js
@@ -1,4 +1,5 @@
-import react, { Component, PropTypes } from 'react'
+import react, { Component } from 'react'
+import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import {
firebaseConnect,
diff --git a/examples/complete/simple/src/TodoItem.js b/examples/complete/simple/src/TodoItem.js
index 74f916396..84c1d167a 100644
--- a/examples/complete/simple/src/TodoItem.js
+++ b/examples/complete/simple/src/TodoItem.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import { firebase } from 'react-redux-firebase'
import './Todo.css'
diff --git a/examples/snippets/decorators/App.js b/examples/snippets/decorators/App.js
index f7a1bc01d..144925db3 100644
--- a/examples/snippets/decorators/App.js
+++ b/examples/snippets/decorators/App.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import { map } from 'lodash'
import { connect } from 'react-redux'
import {
diff --git a/examples/snippets/decorators/TodoItem.js b/examples/snippets/decorators/TodoItem.js
index fa954b0d3..7734a1374 100644
--- a/examples/snippets/decorators/TodoItem.js
+++ b/examples/snippets/decorators/TodoItem.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import { firebaseConnect } from 'react-redux-firebase'
import './Todo.css'
diff --git a/examples/snippets/multipleQueries/App.js b/examples/snippets/multipleQueries/App.js
index 0041c40d4..49e7bc66f 100644
--- a/examples/snippets/multipleQueries/App.js
+++ b/examples/snippets/multipleQueries/App.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import { map } from 'lodash'
import { connect } from 'react-redux'
import {
diff --git a/examples/snippets/multipleQueries/TodoItem.js b/examples/snippets/multipleQueries/TodoItem.js
index 4e5fe2a2b..36aa6ee5c 100644
--- a/examples/snippets/multipleQueries/TodoItem.js
+++ b/examples/snippets/multipleQueries/TodoItem.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import { firebaseConnect } from 'react-redux-firebase'
import './Todo.css'
diff --git a/examples/snippets/populates/App.js b/examples/snippets/populates/App.js
index afb01cd95..f4c511dda 100644
--- a/examples/snippets/populates/App.js
+++ b/examples/snippets/populates/App.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes } from 'react'
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
import { map } from 'lodash'
import { connect } from 'react-redux'
import {
diff --git a/examples/snippets/stateBasedQuery/App.js b/examples/snippets/stateBasedQuery/App.js
index 7bdd04742..df6f0b382 100644
--- a/examples/snippets/stateBasedQuery/App.js
+++ b/examples/snippets/stateBasedQuery/App.js
@@ -1,4 +1,5 @@
-import react, { Component, PropTypes } from 'react'
+import react, { Component } from 'react'
+import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { isLoaded, isEmpty, pathToJS } from 'react-redux-firebase'
import TodosView from './Todos'
diff --git a/index.d.ts b/index.d.ts
new file mode 100644
index 000000000..a09f298ff
--- /dev/null
+++ b/index.d.ts
@@ -0,0 +1,323 @@
+/** Declaration file generated by dts-gen */
+
+export const actionTypes: {
+ AUTHENTICATION_INIT_FINISHED: string;
+ AUTHENTICATION_INIT_STARTED: string;
+ AUTH_UPDATE_ERROR: string;
+ AUTH_UPDATE_START: string;
+ AUTH_UPDATE_SUCCESS: string;
+ EMAIL_UPDATE_ERROR: string;
+ EMAIL_UPDATE_START: string;
+ EMAIL_UPDATE_SUCCESS: string;
+ ERROR: string;
+ FILE_DELETE_COMPLETE: string;
+ FILE_DELETE_ERROR: string;
+ FILE_DELETE_START: string;
+ FILE_UPLOAD_COMPLETE: string;
+ FILE_UPLOAD_ERROR: string;
+ FILE_UPLOAD_PROGRESS: string;
+ FILE_UPLOAD_START: string;
+ LOGIN: string;
+ LOGIN_ERROR: string;
+ LOGOUT: string;
+ NO_VALUE: string;
+ PROFILE_UPDATE_ERROR: string;
+ PROFILE_UPDATE_START: string;
+ PROFILE_UPDATE_SUCCESS: string;
+ SET: string;
+ SET_PROFILE: string;
+ START: string;
+ UNAUTHORIZED_ERROR: string;
+ UNSET_LISTENER: string;
+};
+
+export const constants: {
+ actionTypes: {
+ AUTHENTICATION_INIT_FINISHED: string;
+ AUTHENTICATION_INIT_STARTED: string;
+ AUTH_UPDATE_ERROR: string;
+ AUTH_UPDATE_START: string;
+ AUTH_UPDATE_SUCCESS: string;
+ EMAIL_UPDATE_ERROR: string;
+ EMAIL_UPDATE_START: string;
+ EMAIL_UPDATE_SUCCESS: string;
+ ERROR: string;
+ FILE_DELETE_COMPLETE: string;
+ FILE_DELETE_ERROR: string;
+ FILE_DELETE_START: string;
+ FILE_UPLOAD_COMPLETE: string;
+ FILE_UPLOAD_ERROR: string;
+ FILE_UPLOAD_PROGRESS: string;
+ FILE_UPLOAD_START: string;
+ LOGIN: string;
+ LOGIN_ERROR: string;
+ LOGOUT: string;
+ NO_VALUE: string;
+ PROFILE_UPDATE_ERROR: string;
+ PROFILE_UPDATE_START: string;
+ PROFILE_UPDATE_SUCCESS: string;
+ SET: string;
+ SET_PROFILE: string;
+ START: string;
+ UNAUTHORIZED_ERROR: string;
+ UNSET_LISTENER: string;
+ };
+ defaultConfig: {
+ autoPopulateProfile: boolean;
+ dispatchOnUnsetListener: boolean;
+ enableEmptyAuthChanges: boolean;
+ enableLogging: boolean;
+ enableRedirectHandling: boolean;
+ setProfilePopulateResults: boolean;
+ updateProfileOnLogin: boolean;
+ userProfile: any;
+ };
+ defaultInitProps: string[];
+ defaultJWTProps: string[];
+ metaParams: string[];
+ paramSplitChar: string;
+ supportedAuthProviders: string[];
+};
+
+/**
+ * Object whose values correspond to different reducer functions.
+ */
+export interface ConfigObject {
+ apiKey: string,
+ authDomain: string,
+ databaseURL: string,
+ storageBucket: string
+}
+
+/**
+ * Object whose values correspond to different reducer functions.
+ */
+export interface SettingsObject {
+ apiKey: string,
+ authDomain: string,
+ databaseURL: string,
+ storageBucket: string
+}
+
+export interface listenerConfigFunc {
+ (): string[] | object[]
+}
+
+export function buildChildList(data: any, list: any, p: any): any;
+
+export function customToJS(data: any, path: any, custom: any, notSetValue: any): any;
+
+export function dataToJS(data: any, path: any, notSetValue: any): any;
+
+export function firebase(...args: any[]): any;
+
+export function firebaseConnect(...args: any[]): any;
+
+export function firebaseStateReducer(...args: any[]): any;
+
+export function fixPath(path: any): any;
+
+export function getFirebase(): any;
+
+export function isEmpty(data: any): any;
+
+export function isLoaded(...args: any[]): any;
+
+export function orderedToJS(data: any, path: any, notSetValue: any): any;
+
+export function pathToJS(data: any, path: any, notSetValue: any): any;
+
+export function populatedDataToJS(data: any, path: any, populates: any, notSetValue: any): any;
+
+export function reactReduxFirebase(fbConfig: ConfigObject, otherConfig: any, ...args: any[]): any;
+
+export function reduxFirebase(fbConfig: ConfigObject, otherConfig: any, ...args: any[]): any;
+
+export function reduxReactFirebase(fbConfig: ConfigObject, otherConfig: any, ...args: any[]): any;
+
+export function toJS(data: any): any;
+
+export namespace buildChildList {
+ const prototype: {
+ };
+
+}
+
+export namespace customToJS {
+ const prototype: {
+ };
+
+}
+
+export namespace dataToJS {
+ const prototype: {
+ };
+
+}
+
+export namespace firebase {
+ const prototype: {
+ };
+
+}
+
+export namespace firebaseConnect {
+ const prototype: {
+ };
+
+}
+
+export namespace firebaseStateReducer {
+ const prototype: {
+ };
+
+}
+
+export namespace fixPath {
+ const prototype: {
+ };
+
+}
+
+export namespace getFirebase {
+ const prototype: {
+ };
+
+}
+
+export namespace helpers {
+ function buildChildList(data: any, list: any, p: any): any;
+
+ function customToJS(data: any, path: any, custom: any, notSetValue: any): any;
+
+ function dataToJS(data: any, path: any, notSetValue: any): any;
+
+ function fixPath(path: any): any;
+
+ function isEmpty(data: any): any;
+
+ function isLoaded(...args: any[]): any;
+
+ function orderedToJS(data: any, path: any, notSetValue: any): any;
+
+ function pathToJS(data: any, path: any, notSetValue: any): any;
+
+ function populatedDataToJS(data: any, path: any, populates: any, notSetValue: any): any;
+
+ function toJS(data: any): any;
+
+ namespace buildChildList {
+ const prototype: {
+ };
+
+ }
+
+ namespace customToJS {
+ const prototype: {
+ };
+
+ }
+
+ namespace dataToJS {
+ const prototype: {
+ };
+
+ }
+
+ namespace fixPath {
+ const prototype: {
+ };
+
+ }
+
+ namespace isEmpty {
+ const prototype: {
+ };
+
+ }
+
+ namespace isLoaded {
+ const prototype: {
+ };
+
+ }
+
+ namespace orderedToJS {
+ const prototype: {
+ };
+
+ }
+
+ namespace pathToJS {
+ const prototype: {
+ };
+
+ }
+
+ namespace populatedDataToJS {
+ const prototype: {
+ };
+
+ }
+
+ namespace toJS {
+ const prototype: {
+ };
+
+ }
+
+}
+
+export namespace isEmpty {
+ const prototype: {
+ };
+
+}
+
+export namespace isLoaded {
+ const prototype: {
+ };
+
+}
+
+export namespace orderedToJS {
+ const prototype: {
+ };
+
+}
+
+export namespace pathToJS {
+ const prototype: {
+ };
+
+}
+
+export namespace populatedDataToJS {
+ const prototype: {
+ };
+
+}
+
+export namespace reactReduxFirebase {
+ const prototype: {
+ };
+
+}
+
+export namespace reduxFirebase {
+ const prototype: {
+ };
+
+}
+
+export namespace reduxReactFirebase {
+ const prototype: {
+ };
+
+}
+
+export namespace toJS {
+ const prototype: {
+ };
+
+}
diff --git a/package.json b/package.json
index bb731628d..db27d8a1b 100644
--- a/package.json
+++ b/package.json
@@ -1,14 +1,14 @@
{
"name": "react-redux-firebase",
- "version": "1.4.1",
+ "version": "2.0.0-alpha",
"description": "Redux integration for Firebase. Comes with a Higher Order Component for use with React.",
- "browser": "dist/react-redux-firebase.js",
"main": "lib/index.js",
"module": "es/index.js",
"jsnext:main": "es/index.js",
+ "typings": "./index.d.ts",
"scripts": {
"clean": "rimraf dist",
- "lint": "eslint src/** test/**",
+ "lint": "eslint src/** tests/**",
"lint:fix": "npm run lint -- --fix",
"test": "mocha -R spec --compilers js:babel-core/register ./tests/setup.js ./tests/**/*.spec.js",
"test:cov": "istanbul cover ./node_modules/mocha/bin/_mocha -- ./tests/** --recursive --report lcov --compilers js:babel-register --require babel-polyfill",
@@ -19,7 +19,7 @@
"build:umd:min": "cross-env BABEL_ENV=commonjs NODE_ENV=production webpack src/index.js dist/react-redux-firebase.min.js",
"build:size": "cross-env SIZE=true BABEL_ENV=commonjs NODE_ENV=production webpack src/index.js dist/react-redux-firebase.min.js",
"build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
- "watch": "npm run build:umd -- --watch",
+ "watch": "npm run build:commonjs -- --watch",
"prepublish": "npm run clean && npm run build",
"prepush": "npm run lint:fix",
"docs:clean": "rimraf _book",
@@ -29,25 +29,30 @@
"docs:watch": "npm run docs:prepare && gitbook serve",
"docs:publish": "npm run docs:clean && npm run docs:build && cp CNAME _book && cd _book && git init && git commit --allow-empty -m 'update book' && git checkout -b gh-pages && touch .nojekyll && git add . && git commit -am 'update book' && git push git@github.com:prescottprue/react-redux-firebase gh-pages --force"
},
- "contributors": [
- {
- "name": "Prescott Prue",
- "email": "sprue.dev@gmail.com",
- "url": "https://github.com/prescottprue"
- },
- {
- "name": "Bojhan",
- "url": "https://github.com/Bojhan"
- },
- {
- "name": "Marshall",
- "email": "mmoutenot@gmail.com",
- "url": "https://github.com/mmoutenot"
- },
- {
- "name": "Rahav Lussato",
- "url": "https://github.com/RahavLussato"
- }
+ "license": "MIT",
+ "homepage": "https://github.com/prescottprue/react-redux-firebase#readme",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/prescottprue/react-redux-firebase.git"
+ },
+ "bugs": {
+ "url": "https://github.com/prescottprue/react-redux-firebase/issues"
+ },
+ "author": {
+ "name": "Prescott Prue",
+ "email": "sprue.dev@gmail.com",
+ "url": "https://github.com/prescottprue"
+ },
+ "keywords": [
+ "firebase",
+ "redux",
+ "react",
+ "react-redux",
+ "redux-firebase",
+ "react",
+ "babel",
+ "hoc",
+ "redux-react-firebase"
],
"dependencies": {
"es6-promise": "^4.1.0",
@@ -55,7 +60,8 @@
"hoist-non-react-statics": "^1.2.0",
"immutable": "^3.8.1",
"jwt-decode": "^2.2.0",
- "lodash": "^4.17.4"
+ "lodash": "^4.17.4",
+ "prop-types": "^15.5.8"
},
"peerDependencies": {
"react": "^0.14.6 || ^15.0.0"
@@ -93,6 +99,7 @@
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^6.10.3",
"eslint-plugin-standard": "^2.2.0",
+ "firebase-server": "^0.10.1",
"gitbook-cli": "^2.3.0",
"istanbul": "^1.1.0-alpha.1",
"jsdom": "^9.12.0",
@@ -104,43 +111,9 @@
"rimraf": "^2.6.1",
"sinon": "^2.1.0",
"sinon-chai": "^2.9.0",
- "webpack": "^1.14.0",
+ "webpack": "^2.6.1",
"webpack-bundle-analyzer": "^2.3.1",
+ "ws": "^3.0.0",
"xmlhttprequest": "^1.8.0"
- },
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/prescottprue/react-redux-firebase.git"
- },
- "bugs": {
- "url": "https://github.com/prescottprue/react-redux-firebase/issues"
- },
- "homepage": "https://github.com/prescottprue/react-redux-firebase#readme",
- "keywords": [
- "firebase",
- "redux",
- "react",
- "react-redux",
- "redux-firebase",
- "react",
- "babel",
- "hoc",
- "react-redux-firebase"
- ],
- "npmName": "react-redux-firebase",
- "files": [
- "dist",
- "src",
- "es",
- "lib"
- ],
- "npmFileMap": [
- {
- "basePath": "/dist/",
- "files": [
- "*.js"
- ]
- }
- ]
+ }
}
diff --git a/src/actions/auth.js b/src/actions/auth.js
index 7342d0120..995b06dd6 100644
--- a/src/actions/auth.js
+++ b/src/actions/auth.js
@@ -11,12 +11,12 @@ import {
} from 'lodash'
import jwtDecode from 'jwt-decode'
import { actionTypes, defaultJWTProps } from '../constants'
+import { getLoginMethodAndParams } from '../utils/auth'
import {
promisesForPopulate,
getPopulateObjs,
getChildType
} from '../utils/populate'
-import { getLoginMethodAndParams } from '../utils/auth'
const {
SET,
@@ -242,6 +242,10 @@ export const init = (dispatch, firebase) => {
firebase.auth().onAuthStateChanged(authData => {
if (!authData) {
+ // Run onAuthStateChanged if it exists in config and enableEmptyAuthChanges is set to true
+ if (isFunction(firebase._.config.onAuthStateChanged) && firebase._.config.enableEmptyAuthChanges) {
+ firebase._.config.onAuthStateChanged(authData, firebase, dispatch)
+ }
return dispatch({ type: LOGOUT })
}
@@ -251,11 +255,12 @@ export const init = (dispatch, firebase) => {
dispatchLogin(dispatch, authData)
// Run onAuthStateChanged if it exists in config
- if (firebase._.config.onAuthStateChanged) {
+ if (isFunction(firebase._.config.onAuthStateChanged)) {
firebase._.config.onAuthStateChanged(authData, firebase, dispatch)
}
})
+ // set redirect result callback if enableRedirectHandling set to true
if (firebase._.config.enableRedirectHandling) {
firebase.auth().getRedirectResult()
.then((authData) => {
@@ -361,13 +366,15 @@ export const login = (dispatch, firebase, credentials) => {
* @param {Object} firebase - Internal firebase object
* @private
*/
-export const logout = (dispatch, firebase) => {
- firebase.auth().signOut()
- dispatch({ type: LOGOUT })
- firebase._.authUid = null
- unWatchUserProfile(firebase)
- return Promise.resolve(firebase)
-}
+export const logout = (dispatch, firebase) =>
+ firebase.auth()
+ .signOut()
+ .then(() => {
+ dispatch({ type: LOGOUT })
+ firebase._.authUid = null
+ unWatchUserProfile(firebase)
+ return firebase
+ })
/**
* @description Create a new user in auth and add an account to userProfile root
@@ -499,6 +506,122 @@ export const verifyPasswordResetCode = (dispatch, firebase, code) => {
})
}
+/**
+ * @description Update user profile
+ * @param {Function} dispatch - Action dispatch function
+ * @param {Object} firebase - Internal firebase object
+ * @param {Object} userData - User data object (response from authenticating)
+ * @param {Object} profile - Profile data to place in new profile
+ * @return {Promise}
+ * @private
+ */
+export const updateProfile = (dispatch, firebase, profileUpdate) => {
+ const { database, _: { config, authUid } } = firebase
+ dispatch({
+ type: actionTypes.PROFILE_UPDATE_START,
+ payload: profileUpdate
+ })
+ return database()
+ .ref(`${config.userProfile}/${authUid}`)
+ .update(profileUpdate)
+ .then((snap) => {
+ dispatch({
+ type: actionTypes.PROFILE_UPDATE_SUCCESS,
+ payload: snap.val()
+ })
+ })
+ .catch((payload) => {
+ dispatch({
+ type: actionTypes.PROFILE_UPDATE_ERROR,
+ payload
+ })
+ })
+}
+
+ /**
+ * @description Update Auth Object. Internally calls
+ * `firebase.auth().currentUser.updateProfile` as seen [in the firebase docs](https://firebase.google.com/docs/auth/web/manage-users#update_a_users_profile).
+ * @param {Function} dispatch - Action dispatch function
+ * @param {Object} firebase - Internal firebase object
+ * @param {Object} profileUpdate - Update to be auth object
+ * @return {Promise}
+ * @private
+ */
+export const updateAuth = (dispatch, firebase, authUpdate, updateInProfile) => {
+ dispatch({
+ type: actionTypes.AUTH_UPDATE_START,
+ payload: authUpdate
+ })
+ if (!firebase.auth().currentUser) {
+ const msg = 'User must be logged in to update auth.'
+ dispatch({
+ type: actionTypes.AUTH_UPDATE_ERROR,
+ payload: msg
+ })
+ return Promise.reject(msg)
+ }
+ return firebase.auth().currentUser
+ .updateProfile(authUpdate)
+ .then((payload) => {
+ dispatch({
+ type: actionTypes.AUTH_UPDATE_SUCCESS,
+ payload: firebase.auth().currentUser
+ })
+ if (updateInProfile) {
+ return updateProfile(dispatch, firebase, authUpdate)
+ }
+ return payload
+ })
+ .catch((payload) => {
+ dispatch({
+ type: actionTypes.AUTH_UPDATE_ERROR,
+ payload
+ })
+ })
+}
+
+/**
+ * @description Update user's email. Internally calls
+ * `firebase.auth().currentUser.updateEmail` as seen [in the firebase docs](https://firebase.google.com/docs/auth/web/manage-users#update_a_users_profile).
+ * @param {Function} dispatch - Action dispatch function
+ * @param {Object} firebase - Internal firebase object
+ * @param {String} newEmail - Update to be auth object
+ * @return {Promise}
+ * @private
+ */
+export const updateEmail = (dispatch, firebase, newEmail, updateInProfile) => {
+ dispatch({
+ type: actionTypes.EMAIL_UPDATE_START,
+ payload: newEmail
+ })
+ if (!firebase.auth().currentUser) {
+ const msg = 'User must be logged in to update email.'
+ dispatch({
+ type: actionTypes.EMAIL_UPDATE_ERROR,
+ payload: msg
+ })
+ return Promise.reject(msg)
+ }
+ return firebase.auth().currentUser
+ .updateEmail(newEmail)
+ .then((payload) => {
+ dispatch({
+ type: actionTypes.EMAIL_UPDATE_SUCCESS,
+ payload: newEmail
+ })
+ if (updateInProfile) {
+ return updateProfile(dispatch, firebase, { email: newEmail })
+ }
+ return payload
+ })
+ .catch((payload) => {
+ dispatch({
+ type: actionTypes.EMAIL_UPDATE_ERROR,
+ payload
+ })
+ })
+}
+
export default {
dispatchLoginError,
dispatchUnauthorizedError,
@@ -512,5 +635,8 @@ export default {
createUser,
resetPassword,
confirmPasswordReset,
- verifyPasswordResetCode
+ verifyPasswordResetCode,
+ updateAuth,
+ updateProfile,
+ updateEmail
}
diff --git a/src/actions/query.js b/src/actions/query.js
index 5addb6322..746b1adf6 100644
--- a/src/actions/query.js
+++ b/src/actions/query.js
@@ -132,7 +132,8 @@ export const watchEvent = (firebase, dispatch, { type, path, populates, queryPar
// TODO: Allow setting of unpopulated data before starting population through config
// TODO: Set ordered for populate queries
// TODO: Allow config to toggle Combining into one SET action
- promisesForPopulate(firebase, data, populates)
+ const dataKey = snapshot.key
+ promisesForPopulate(firebase, dataKey, data, populates)
.then((results) => {
// dispatch child sets first so isLoaded is only set to true for
// populatedDataToJS after all data is in redux (Issue #121)
@@ -148,7 +149,7 @@ export const watchEvent = (firebase, dispatch, { type, path, populates, queryPar
})
dispatch({
type: SET,
- path: resultPath,
+ path: storeAs || resultPath,
data,
timestamp: Date.now(),
requesting: false,
@@ -172,8 +173,10 @@ export const watchEvent = (firebase, dispatch, { type, path, populates, queryPar
* @param {String} event - Event for which to remove the watcher
* @param {String} path - Path of watcher to remove
*/
-export const unWatchEvent = (firebase, dispatch, event, path, queryId = undefined) =>
- unsetWatcher(firebase, dispatch, event, path, queryId)
+export const unWatchEvent = (firebase, dispatch, { type, path, storeAs, queryId = undefined }) => {
+ const watchPath = !storeAs ? path : `${path}@${storeAs}`
+ unsetWatcher(firebase, dispatch, type, watchPath, queryId)
+}
/**
* @description Add watchers to a list of events
@@ -192,8 +195,8 @@ export const watchEvents = (firebase, dispatch, events) =>
* @param {Array} events - List of events for which to remove watchers
*/
export const unWatchEvents = (firebase, dispatch, events) =>
- events.forEach(event =>
- unWatchEvent(firebase, dispatch, event.type, event.path)
- )
+ events.forEach(event =>
+ unWatchEvent(firebase, dispatch, event)
+ )
export default { watchEvents, unWatchEvents }
diff --git a/src/compose.js b/src/compose.js
index ea744462e..640980431 100644
--- a/src/compose.js
+++ b/src/compose.js
@@ -2,11 +2,10 @@ import * as firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/database'
import 'firebase/storage'
-import { isObject } from 'lodash'
+import { createFirebaseInstance } from './createFirebaseInstance'
import { defaultConfig } from './constants'
import { validateConfig } from './utils'
-import { authActions, queryActions, storageActions } from './actions'
-let firebaseInstance
+import { authActions } from './actions'
/**
* @name reactReduxFirebase
@@ -14,7 +13,7 @@ let firebaseInstance
* @description Middleware that handles configuration (placed in redux's
* `compose` call)
* @property {Object} fbConfig - Object containing Firebase config including
- * databaseURL
+ * databaseURL or Firebase instance
* @property {String} fbConfig.apiKey - Firebase apiKey
* @property {String} fbConfig.authDomain - Firebase auth domain
* @property {String} fbConfig.databaseURL - Firebase database url
@@ -31,11 +30,17 @@ let firebaseInstance
* auth redirect handling listener. (default: `true`)
* @property {Function} config.onAuthStateChanged - Function run when auth state
* changes. Argument Pattern: `(authData, firebase, dispatch)`
+ * @property {Boolean} config.enableEmptyAuthChanges - Whether or not to enable
+ * empty auth changes. When set to true, `onAuthStateChanged` will be fired with,
+ * empty auth changes such as undefined on initialization. See
+ * [#137](https://github.com/prescottprue/react-redux-firebase/issues/137) for
+ * more details. (default: `false`)
* @property {Function} config.onRedirectResult - Function run when redirect
* result is returned. Argument Pattern: `(authData, firebase, dispatch)`
* @property {Object} config.customAuthParameters - Object for setting which
* customAuthParameters are passed to external auth providers.
- * @property {Function} config.profileFactory - Factory for modifying how user profile is saved.
+ * @property {Function} config.profileFactory - Factory for modifying how user
+ * profile is saved.
* @property {Function} config.uploadFileDataFactory - Factory for modifying
* how file meta data is written during file uploads
* @property {Array|String} config.profileParamsToPopulate - Parameters within
@@ -83,423 +88,27 @@ export default (fbConfig, otherConfig) => next =>
const store = next(reducer, initialState, middleware)
const { dispatch } = store
- // Combine all configs
- const configs = Object.assign({}, defaultConfig, fbConfig, otherConfig)
+ let firebaseInstance
- validateConfig(configs)
+ // handle firebase instance being passed in as first argument
+ if (typeof fbConfig.initializeApp === 'function') {
+ firebaseInstance = createFirebaseInstance(fbConfig, otherConfig, dispatch)
+ } else {
+ // Combine all configs
+ const configs = Object.assign({}, defaultConfig, fbConfig, otherConfig)
- // Initialize Firebase
- try {
- firebase.initializeApp(fbConfig)
- } catch (err) {} // silence reinitialize warning (hot-reloading)
+ validateConfig(configs)
- // Enable Logging based on config
- if (configs.enableLogging) {
- firebase.database.enableLogging(configs.enableLogging)
- }
-
- // Handle react-native
- if (configs.ReactNative) {
- configs.enableRedirectHandling = false
- const { AsyncStorage } = configs.ReactNative
- // Stub firebase's internal's with react-native (based on firebase's react-native index file)
- firebase.INTERNAL.extendNamespace({
- INTERNAL: {
- reactNative: {
- AsyncStorage
- }
- }
- })
- }
-
- const rootRef = firebase.database().ref()
-
- const instance = Object.defineProperty(firebase, '_', {
- value: {
- watchers: {},
- config: configs,
- authUid: null
- },
- writable: true,
- enumerable: true,
- configurable: true
- })
+ // Initialize Firebase
+ try {
+ firebase.initializeApp(fbConfig)
+ } catch (err) {} // silence reinitialize warning (hot-reloading)
- /**
- * @private
- * @description Calls a method and attaches meta to value object
- * @param {String} method - Method to run with meta attached
- * @param {String} path - Path to location on Firebase which to set
- * @param {Object|String|Boolean|Number} value - Value to write to Firebase
- * @param {Function} onComplete - Function to run on complete
- * @return {Promise} Containing reference snapshot
- */
- const withMeta = (method, path, value, onComplete) => {
- if (isObject(value)) {
- const prefix = method === 'update' ? 'updated' : 'created'
- const dataWithMeta = {
- ...value,
- [`${prefix}At`]: firebase.database.ServerValue.TIMESTAMP
- }
- if (instance.auth().currentUser) {
- dataWithMeta[`${prefix}By`] = instance.auth().currentUser.uid
- }
- return rootRef.child(path)[method](dataWithMeta, onComplete)
- }
- return rootRef.child(path)[method](value, onComplete)
+ firebaseInstance = createFirebaseInstance(firebase, configs, dispatch)
}
- /**
- * @description Sets data to Firebase.
- * @param {String} path - Path to location on Firebase which to set
- * @param {Object|String|Boolean|Number} value - Value to write to Firebase
- * @param {Function} onComplete - Function to run on complete (`not required`)
- * @return {Promise} Containing reference snapshot
- * @example
Basic
- * import React, { Component, PropTypes } from 'react'
- * import { firebaseConnect } from 'react-redux-firebase'
- * const Example = ({ firebase: { set } }) => (
- * set('some/path', { here: 'is a value' })}>
- * Set To Firebase
- *
- * )
- * export default firebaseConnect()(Example)
- */
- const set = (path, value, onComplete) =>
- rootRef.child(path).set(value, onComplete)
-
- /**
- * @description Sets data to Firebase along with meta data. Currently,
- * this includes createdAt and createdBy. *Warning* using this function
- * may have unintented consequences (setting createdAt even if data already
- * exists)
- * @param {String} path - Path to location on Firebase which to set
- * @param {Object|String|Boolean|Number} value - Value to write to Firebase
- * @param {Function} onComplete - Function to run on complete (`not required`)
- * @return {Promise} Containing reference snapshot
- */
- const setWithMeta = (path, value, onComplete) =>
- withMeta('set', path, value, onComplete)
-
- /**
- * @description Pushes data to Firebase.
- * @param {String} path - Path to location on Firebase which to push
- * @param {Object|String|Boolean|Number} value - Value to push to Firebase
- * @param {Function} onComplete - Function to run on complete (`not required`)
- * @return {Promise} Containing reference snapshot
- * @example Basic
- * import React, { Component, PropTypes } from 'react'
- * import { firebaseConnect } from 'react-redux-firebase'
- * const Example = ({ firebase: { push } }) => (
- * push('some/path', true)}>
- * Push To Firebase
- *
- * )
- * export default firebaseConnect()(Example)
- */
- const push = (path, value, onComplete) =>
- rootRef.child(path).push(value, onComplete)
-
- /**
- * @description Pushes data to Firebase along with meta data. Currently,
- * this includes createdAt and createdBy.
- * @param {String} path - Path to location on Firebase which to set
- * @param {Object|String|Boolean|Number} value - Value to write to Firebase
- * @param {Function} onComplete - Function to run on complete (`not required`)
- * @return {Promise} Containing reference snapshot
- */
- const pushWithMeta = (path, value, onComplete) =>
- withMeta('push', path, value, onComplete)
-
- /**
- * @description Updates data on Firebase and sends new data.
- * @param {String} path - Path to location on Firebase which to update
- * @param {Object|String|Boolean|Number} value - Value to update to Firebase
- * @param {Function} onComplete - Function to run on complete (`not required`)
- * @return {Promise} Containing reference snapshot
- * @example Basic
- * import React, { Component, PropTypes } from 'react'
- * import { firebaseConnect } from 'react-redux-firebase'
- * const Example = ({ firebase: { update } }) => (
- * update('some/path', { here: 'is a value' })}>
- * Update To Firebase
- *
- * )
- * export default firebaseConnect()(Example)
- */
- const update = (path, value, onComplete) =>
- rootRef.child(path).update(value, onComplete)
-
- /**
- * @description Updates data on Firebase along with meta. *Warning*
- * using this function may have unintented consequences (setting
- * createdAt even if data already exists)
- * @param {String} path - Path to location on Firebase which to update
- * @param {Object|String|Boolean|Number} value - Value to update to Firebase
- * @param {Function} onComplete - Function to run on complete (`not required`)
- * @return {Promise} Containing reference snapshot
- */
- const updateWithMeta = (path, value, onComplete) =>
- withMeta('update', path, value, onComplete)
-
- /**
- * @description Removes data from Firebase at a given path.
- * @param {String} path - Path to location on Firebase which to remove
- * @param {Function} onComplete - Function to run on complete (`not required`)
- * @return {Promise} Containing reference snapshot
- * @example Basic
- * import React, { Component, PropTypes } from 'react'
- * import { firebaseConnect } from 'react-redux-firebase'
- * const Example = ({ firebase: { remove } }) => (
- * remove('some/path')}>
- * Remove From Firebase
- *
- * )
- * export default firebaseConnect()(Example)
- */
- const remove = (path, onComplete) =>
- rootRef.child(path).remove(onComplete)
-
- /**
- * @description Sets data to Firebase only if the path does not already
- * exist, otherwise it rejects.
- * @param {String} path - Path to location on Firebase which to set
- * @param {Object|String|Boolean|Number} value - Value to write to Firebase
- * @param {Function} onComplete - Function to run on complete (`not required`)
- * @return {Promise} Containing reference snapshot
- * @example Basic
- * import React, { Component, PropTypes } from 'react'
- * import { firebaseConnect } from 'react-redux-firebase'
- * const Example = ({ firebase: { uniqueSet } }) => (
- * uniqueSet('some/unique/path', true)}>
- * Unique Set To Firebase
- *
- * )
- * export default firebaseConnect()(Example)
- */
- const uniqueSet = (path, value, onComplete) =>
- rootRef.child(path)
- .once('value')
- .then(snap => {
- if (snap.val && snap.val() !== null) {
- const err = new Error('Path already exists.')
- if (onComplete) onComplete(err)
- return Promise.reject(err)
- }
- return rootRef.child(path).set(value, onComplete)
- })
-
- /**
- * @description Upload a file to Firebase Storage with the option to store
- * its metadata in Firebase Database
- * @param {String} path - Path to location on Firebase which to set
- * @param {File} file - File object to upload (usually first element from
- * array output of select-file or a drag/drop `onDrop`)
- * @param {String} dbPath - Database path to place uploaded file metadata
- * @return {Promise} Containing the File object
- */
- const uploadFile = (path, file, dbPath) =>
- storageActions.uploadFile(dispatch, instance, { path, file, dbPath })
-
- /**
- * @description Upload multiple files to Firebase Storage with the option
- * to store their metadata in Firebase Database
- * @param {String} path - Path to location on Firebase which to set
- * @param {Array} files - Array of File objects to upload (usually from
- * a select-file or a drag/drop `onDrop`)
- * @param {String} dbPath - Database path to place uploaded files metadata.
- * @return {Promise} Containing an array of File objects
- */
- const uploadFiles = (path, files, dbPath) =>
- storageActions.uploadFiles(dispatch, instance, { path, files, dbPath })
-
- /**
- * @description Delete a file from Firebase Storage with the option to
- * remove its metadata in Firebase Database
- * @param {String} path - Path to location on Firebase which to set
- * @param {String} dbPath - Database path to place uploaded file metadata
- * @return {Promise} Containing the File object
- */
- const deleteFile = (path, dbPath) =>
- storageActions.deleteFile(dispatch, instance, { path, dbPath })
-
- /**
- * @description Watch event. **Note:** this method is used internally
- * so examples have not yet been created, and it may not work as expected.
- * @param {String} type - Type of watch event
- * @param {String} dbPath - Database path on which to setup watch event
- * @param {String} storeAs - Name of listener results within redux store
- * @return {Promise}
- */
- const watchEvent = (type, path, storeAs) =>
- queryActions.watchEvent(instance, dispatch, { type, path, storeAs })
-
- /**
- * @description Unset a listener watch event. **Note:** this method is used
- * internally so examples have not yet been created, and it may not work
- * as expected.
- * @param {String} eventName - Type of watch event
- * @param {String} eventPath - Database path on which to setup watch event
- * @param {String} storeAs - Name of listener results within redux store
- * @return {Promise}
- */
- const unWatchEvent = (eventName, eventPath, queryId = undefined) =>
- queryActions.unWatchEvent(instance, dispatch, eventName, eventPath, queryId)
-
- /**
- * @description Logs user into Firebase. For examples, visit the [auth section](/docs/auth.md)
- * @param {Object} credentials - Credentials for authenticating
- * @param {String} credentials.provider - External provider (google | facebook | twitter)
- * @param {String} credentials.type - Type of external authentication (popup | redirect) (only used with provider)
- * @param {String} credentials.email - Credentials for authenticating
- * @param {String} credentials.password - Credentials for authenticating (only used with email)
- * @return {Promise} Containing user's auth data
- */
- const login = credentials =>
- authActions.login(dispatch, instance, credentials)
-
- /**
- * @description Logs user out of Firebase and empties firebase state from
- * redux store
- * @return {Promise}
- */
- const logout = () =>
- authActions.logout(dispatch, instance)
-
- /**
- * @description Creates a new user in Firebase authentication. If
- * `userProfile` config option is set, user profiles will be set to this
- * location.
- * @param {Object} credentials - Credentials for authenticating
- * @param {String} credentials.email - Credentials for authenticating
- * @param {String} credentials.password - Credentials for authenticating (only used with email)
- * @param {Object} profile - Data to include within new user profile
- * @return {Promise} Containing user's auth data
- */
- const createUser = (credentials, profile) =>
- authActions.createUser(dispatch, instance, credentials, profile)
-
- /**
- * @description Sends password reset email
- * @param {Object} credentials - Credentials for authenticating
- * @param {String} credentials.email - Credentials for authenticating
- * @return {Promise}
- */
- const resetPassword = (credentials) =>
- authActions.resetPassword(dispatch, instance, credentials)
-
- /**
- * @description Confirm that a user's password has been reset
- * @param {String} code - Password reset code to verify
- * @param {String} password - New Password to confirm reset to
- * @return {Promise}
- */
- const confirmPasswordReset = (code, password) =>
- authActions.confirmPasswordReset(dispatch, instance, code, password)
-
- /**
- * @description Verify that a password reset code from a password reset
- * email is valid
- * @param {String} code - Password reset code to verify
- * @return {Promise} Containing user auth info
- */
- const verifyPasswordResetCode = (code) =>
- authActions.verifyPasswordResetCode(dispatch, instance, code)
-
- /**
- * @name ref
- * @description Firebase ref function
- * @return {database.Reference}
- */
- /**
- * @name database
- * @description Firebase database service instance including all Firebase storage methods
- * @return {Database} Firebase database service
- */
- /**
- * @name storage
- * @description Firebase storage service instance including all Firebase storage methods
- * @return {Storage} Firebase storage service
- */
- /**
- * @name auth
- * @description Firebase auth service instance including all Firebase auth methods
- * @return {Auth}
- */
- firebase.helpers = {
- ref: path => firebase.database().ref(path),
- set,
- setWithMeta,
- uniqueSet,
- push,
- pushWithMeta,
- remove,
- update,
- updateWithMeta,
- login,
- logout,
- uploadFile,
- uploadFiles,
- deleteFile,
- createUser,
- resetPassword,
- confirmPasswordReset,
- verifyPasswordResetCode,
- watchEvent,
- unWatchEvent,
- storage: () => firebase.storage()
- }
-
- authActions.init(dispatch, instance)
-
- store.firebase = instance
- firebaseInstance = Object.assign({}, instance, instance.helpers)
+ authActions.init(dispatch, firebaseInstance)
+ store.firebase = firebaseInstance
return store
}
-
-/**
- * @external
- * @description Expose Firebase instance created internally. Useful for
- * integrations into external libraries such as redux-thunk and redux-observable.
- * @example redux-thunk integration
- * import { applyMiddleware, compose, createStore } from 'redux';
- * import thunk from 'redux-thunk';
- * import { reactReduxFirebase } from 'react-redux-firebase';
- * import makeRootReducer from './reducers';
- * import { getFirebase } from 'react-redux-firebase';
- *
- * const fbConfig = {} // your firebase config
- *
- * const store = createStore(
- * makeRootReducer(),
- * initialState,
- * compose(
- * applyMiddleware([
- * // Pass getFirebase function as extra argument
- * thunk.withExtraArgument(getFirebase)
- * ]),
- * reactReduxFirebase(fbConfig)
- * )
- * );
- * // then later
- * export const addTodo = (newTodo) =>
- * (dispatch, getState, getFirebase) => {
- * const firebase = getFirebase()
- * firebase
- * .push('todos', newTodo)
- * .then(() => {
- * dispatch({ type: 'SOME_ACTION' })
- * })
- * };
- *
- */
-export const getFirebase = () => {
- // TODO: Handle recieveing config and creating firebase instance if it doesn't exist
- /* istanbul ignore next: Firebase instance always exists during tests */
- if (!firebaseInstance) {
- throw new Error('Firebase instance does not yet exist. Check your compose function.') // eslint-disable-line no-console
- }
- // TODO: Create new firebase here with config passed in
- return firebaseInstance
-}
diff --git a/src/connect.js b/src/connect.js
deleted file mode 100644
index 4636b398c..000000000
--- a/src/connect.js
+++ /dev/null
@@ -1,110 +0,0 @@
-import React, { Component, PropTypes } from 'react'
-import { isEqual } from 'lodash'
-import hoistStatics from 'hoist-non-react-statics'
-import { watchEvents, unWatchEvents } from './actions/query'
-import { getEventsFromInput, createCallable } from './utils'
-
-/**
- * @name firebaseConnect
- * @extends React.Component
- * @description Higher Order Component that automatically listens/unListens
- * to provided firebase paths using React's Lifecycle hooks.
- * @param {Array} watchArray - Array of objects or strings for paths to sync from Firebase. Can also be a function that returns the array. The function is passed the current props and the firebase object.
- * @return {Function} - that accepts a component to wrap and returns the wrapped component
- * @example Basic
- * // this.props.firebase set on App component as firebase object with helpers
- * import { firebaseConnect } from 'react-redux-firebase'
- * export default firebaseConnect()(App)
- * @example Data
- * import { connect } from 'react-redux'
- * import { firebaseConnect, dataToJS } from 'react-redux-firebase'
- *
- * // sync /todos from firebase into redux
- * const fbWrapped = firebaseConnect([
- * 'todos'
- * ])(App)
- *
- * // pass todos list from redux as this.props.todosList
- * export default connect(({ firebase }) => ({
- * todosList: dataToJS(firebase, 'todos'),
- * profile: pathToJS(firebase, 'profile'), // pass profile data as this.props.profile
- * auth: pathToJS(firebase, 'auth') // pass auth data as this.props.auth
- * }))(fbWrapped)
- * @example Data that depends on props
- * import { connect } from 'react-redux'
- * import { firebaseConnect, dataToJS } from 'react-redux-firebase'
- *
- * // sync /todos from firebase into redux
- * const fbWrapped = firebaseConnect((props, firebase) => ([
- * `todos/${firebase.database().currentUser.uid}/${props.type}`
- * ])(App)
- *
- * // pass todos list for the specified type of todos from redux as `this.props.todosList`
- * export default connect(({ firebase, type }) => ({
- * todosList: dataToJS(firebase, `data/todos/${firebase.getIn(['auth', 'uid'])}/${type}`),
- * profile: pathToJS(firebase, 'profile'), // pass profile data as this.props.profile
- * auth: pathToJS(firebase, 'auth') // pass auth data as this.props.auth
- * }))(fbWrapped)
- */
-export default (dataOrFn = []) => WrappedComponent => {
- class FirebaseConnect extends Component {
- constructor (props, context) {
- super(props, context)
- this._firebaseEvents = []
- this.firebase = null
- }
-
- static contextTypes = {
- store: PropTypes.object.isRequired
- };
-
- componentWillMount () {
- const { firebase, dispatch } = this.context.store
-
- // Allow function to be passed
- const inputAsFunc = createCallable(dataOrFn)
- this.prevData = inputAsFunc(this.props, firebase)
-
- const { ref, helpers, storage, database, auth } = firebase
- this.firebase = { ref, storage, database, auth, ...helpers }
-
- this._firebaseEvents = getEventsFromInput(this.prevData)
-
- watchEvents(firebase, dispatch, this._firebaseEvents)
- }
-
- componentWillUnmount () {
- const { firebase, dispatch } = this.context.store
- unWatchEvents(firebase, dispatch, this._firebaseEvents)
- }
-
- componentWillReceiveProps (np) {
- const { firebase, dispatch } = this.context.store
- const inputAsFunc = createCallable(dataOrFn)
- const data = inputAsFunc(np, firebase)
-
- // Handle a data parameter having changed
- if (!isEqual(data, this.prevData)) {
- this.prevData = data
- // UnWatch all current events
- unWatchEvents(firebase, dispatch, this._firebaseEvents)
- // Get watch events from new data
- this._firebaseEvents = getEventsFromInput(data)
- // Watch new events
- watchEvents(firebase, dispatch, this._firebaseEvents)
- }
- }
-
- render () {
- return (
-
- )
- }
- }
-
- return hoistStatics(FirebaseConnect, WrappedComponent)
-}
diff --git a/src/constants.js b/src/constants.js
index 9679f7ebe..8b2b9ded0 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -13,6 +13,7 @@ export const actionsPrefix = '@@reactReduxFirebase'
* @description Object containing all action types
* @property {String} START - `@@reactReduxFirebase/START`
* @property {String} SET - `@@reactReduxFirebase/SET`
+ * @property {String} SET_ORDERED - `@@reactReduxFirebase/SET_ORDERED`
* @property {String} SET_PROFILE - `@@reactReduxFirebase/SET_PROFILE`
* @property {String} LOGIN - `@@reactReduxFirebase/LOGIN`
* @property {String} LOGOUT - `@@reactReduxFirebase/LOGOUT`
@@ -29,6 +30,15 @@ export const actionsPrefix = '@@reactReduxFirebase'
* @property {String} FILE_DELETE_START - `@@reactReduxFirebase/FILE_DELETE_START`
* @property {String} FILE_DELETE_ERROR - `@@reactReduxFirebase/FILE_DELETE_ERROR`
* @property {String} FILE_DELETE_COMPLETE - `@@reactReduxFirebase/FILE_DELETE_COMPLETE`
+ * @property {String} AUTH_UPDATE_START - `@@reactReduxFirebase/AUTH_UPDATE_START`
+ * @property {String} AUTH_UPDATE_ERROR - `@@reactReduxFirebase/AUTH_UPDATE_ERROR`
+ * @property {String} AUTH_UPDATE_COMPLETE - `@@reactReduxFirebase/AUTH_UPDATE_COMPLETE`
+ * @property {String} PROFILE_UPDATE_START - `@@reactReduxFirebase/PROFILE_UPDATE_START`
+ * @property {String} PROFILE_UPDATE_ERROR - `@@reactReduxFirebase/PROFILE_UPDATE_ERROR`
+ * @property {String} PROFILE_UPDATE_COMPLETE - `@@reactReduxFirebase/PROFILE_UPDATE_COMPLETE`
+ * @property {String} EMAIL_UPDATE_START - `@@reactReduxFirebase/EMAIL_UPDATE_START`
+ * @property {String} EMAIL_UPDATE_ERROR - `@@reactReduxFirebase/EMAIL_UPDATE_ERROR`
+ * @property {String} EMAIL_UPDATE_COMPLETE - `@@reactReduxFirebase/EMAIL_UPDATE_COMPLETE`
* @example
* import { actionTypes } from 'react-redux-firebase'
* actionTypes.SET === '@@reactReduxFirebase/SET' // true
@@ -52,7 +62,16 @@ export const actionTypes = {
FILE_UPLOAD_COMPLETE: `${actionsPrefix}/FILE_UPLOAD_COMPLETE`,
FILE_DELETE_START: `${actionsPrefix}/FILE_DELETE_START`,
FILE_DELETE_ERROR: `${actionsPrefix}/FILE_DELETE_ERROR`,
- FILE_DELETE_COMPLETE: `${actionsPrefix}/FILE_DELETE_COMPLETE`
+ FILE_DELETE_COMPLETE: `${actionsPrefix}/FILE_DELETE_COMPLETE`,
+ AUTH_UPDATE_START: `${actionsPrefix}/AUTH_UPDATE_START`,
+ AUTH_UPDATE_SUCCESS: `${actionsPrefix}/AUTH_UPDATE_SUCCESS`,
+ AUTH_UPDATE_ERROR: `${actionsPrefix}/AUTH_UPDATE_ERROR`,
+ PROFILE_UPDATE_START: `${actionsPrefix}/PROFILE_UPDATE_START`,
+ PROFILE_UPDATE_SUCCESS: `${actionsPrefix}/PROFILE_UPDATE_SUCCESS`,
+ PROFILE_UPDATE_ERROR: `${actionsPrefix}/PROFILE_UPDATE_ERROR`,
+ EMAIL_UPDATE_START: `${actionsPrefix}/EMAIL_UPDATE_START`,
+ EMAIL_UPDATE_SUCCESS: `${actionsPrefix}/EMAIL_UPDATE_SUCCESS`,
+ EMAIL_UPDATE_ERROR: `${actionsPrefix}/EMAIL_UPDATE_ERROR`
}
/** @constant
@@ -66,6 +85,11 @@ export const actionTypes = {
* @property {Boolean} enableRedirectHandling - `true` Whether or not to enable
* redirect handling. This must be disabled if environment is not http/https
* such as with react-native.
+ * @property {Boolean} enableEmptyAuthChanges - `false` Whether or not to enable
+ * empty auth changes. When set to true, `onAuthStateChanged` will be fired with,
+ * empty auth changes such as `undefined` on initialization
+ * (see [#137](https://github.com/prescottprue/react-redux-firebase/issues/137)).
+ * Requires `v1.5.0-alpha` or higher.
* @property {Boolean} autoPopulateProfile - `true` Whether or not to
* automatically populate profile with data loaded through
* profileParamsToPopulate config.
@@ -74,11 +98,11 @@ export const actionTypes = {
* the data path. For example: role paramter on profile populated from 'roles'
* root. True will call SET_PROFILE as well as a SET action with the role that
* is loaded (places it in data/roles).
- * @property {Boolean} distpatchOnUnsetListener - `false` Whether or not to
+ * @property {Boolean} dispatchOnUnsetListener - `false` Whether or not to
* dispatch UNSET_LISTENER when disabling listeners for a specific path. USE WITH CAUTION
* Setting this to true allows an action to be called that removes data
* from redux (which might not always be expected).
- * @type {Array}
+ * @type {Object}
*/
export const defaultConfig = {
userProfile: null,
@@ -87,7 +111,8 @@ export const defaultConfig = {
enableRedirectHandling: true,
autoPopulateProfile: true,
setProfilePopulateResults: false,
- distpatchOnUnsetListener: false
+ dispatchOnUnsetListener: false,
+ enableEmptyAuthChanges: false
}
/** @constant
@@ -156,13 +181,3 @@ export default {
metaParams,
paramSplitChar
}
-
-module.exports = {
- defaultJWTProps,
- actionTypes,
- defaultConfig,
- supportedAuthProviders,
- defaultInitProps,
- metaParams,
- paramSplitChar
-}
diff --git a/src/createFirebaseConnect.js b/src/createFirebaseConnect.js
new file mode 100644
index 000000000..b2aac37ae
--- /dev/null
+++ b/src/createFirebaseConnect.js
@@ -0,0 +1,120 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import { isEqual } from 'lodash'
+import hoistStatics from 'hoist-non-react-statics'
+import { watchEvents, unWatchEvents } from './actions/query'
+import { getEventsFromInput, createCallable } from './utils'
+
+/**
+ * @name createFirebaseConnect
+ * @description WARNING!! Advanced feature, and only be used when needing to
+ * access a firebase instance created under a different store key
+ * @param {String} storeKey - Name of key of store to connect to (store that contains state.firebase)
+ * @return {Function} - that returns a firebaseConnect function, which is later used to wrap a component
+ * @example Data
+ * import { connect } from 'react-redux'
+ * import { createFirebaseConnect } from 'react-redux-firebase'
+ *
+ * // sync /todos from firebase (in other store) into redux
+ * const fbWrapped = createFirebaseConnect('someOtherName')(['todos'])
+ *
+ * // pass todos list from redux as this.props.todosList
+ * export default connect(({ firebase: data: { todos }, auth, profile }) => ({
+ * todos,
+ * profile, // pass profile data as this.props.profile
+ * auth // pass auth data as this.props.auth
+ * }))(fbWrapped)
+ */
+export const createFirebaseConnect = (storeKey = 'store') => (dataOrFn = []) => WrappedComponent => {
+ /**
+ * @name firebaseConnect
+ * @extends React.Component
+ * @description Higher Order Component that automatically listens/unListens
+ * to provided firebase paths using React's Lifecycle hooks.
+ * @param {Array} watchArray - Array of objects or strings for paths to sync
+ * from Firebase. Can also be a function that returns the array. The function
+ * is passed the current props and the firebase object.
+ * @return {Function} - that accepts a component to wrap and returns the wrapped component
+ * @example Basic
+ * // this.props.firebase set on App component as firebase object with helpers
+ * import { firebaseConnect } from 'react-redux-firebase'
+ * export default firebaseConnect()(App)
+ * @example Data
+ * import { connect } from 'react-redux'
+ * import { firebaseConnect, dataToJS } from 'react-redux-firebase'
+ *
+ * // sync /todos from firebase into redux
+ * const fbWrapped = firebaseConnect([
+ * 'todos'
+ * ])(App)
+ *
+ * // pass todos list from redux as this.props.todosList
+ * export default connect(({ firebase: data: { todos }, auth, profile }) => ({
+ * todos,
+ * profile, // pass profile data as this.props.profile
+ * auth // pass auth data as this.props.auth
+ * }))(fbWrapped)
+ */
+ class FirebaseConnect extends Component {
+ constructor (props, context) {
+ super(props, context)
+ this._firebaseEvents = []
+ this.firebase = null
+ }
+
+ static contextTypes = {
+ [storeKey]: PropTypes.object.isRequired
+ };
+
+ componentWillMount () {
+ const { firebase, dispatch } = this.context[storeKey]
+
+ // Allow function to be passed
+ const inputAsFunc = createCallable(dataOrFn)
+ this.prevData = inputAsFunc(this.props, firebase)
+
+ const { ref, helpers, storage, database, auth } = firebase
+ this.firebase = { ref, storage, database, auth, ...helpers }
+
+ this._firebaseEvents = getEventsFromInput(this.prevData)
+
+ watchEvents(firebase, dispatch, this._firebaseEvents)
+ }
+
+ componentWillUnmount () {
+ const { firebase, dispatch } = this.context.store
+ unWatchEvents(firebase, dispatch, this._firebaseEvents)
+ }
+
+ componentWillReceiveProps (np) {
+ const { firebase, dispatch } = this.context.store
+ const inputAsFunc = createCallable(dataOrFn)
+ const data = inputAsFunc(np, firebase)
+
+ // Handle a data parameter having changed
+ if (!isEqual(data, this.prevData)) {
+ this.prevData = data
+ // UnWatch all current events
+ unWatchEvents(firebase, dispatch, this._firebaseEvents)
+ // Get watch events from new data
+ this._firebaseEvents = getEventsFromInput(data)
+ // Watch new events
+ watchEvents(firebase, dispatch, this._firebaseEvents)
+ }
+ }
+
+ render () {
+ return (
+
+ )
+ }
+ }
+
+ return hoistStatics(FirebaseConnect, WrappedComponent)
+}
+
+export default createFirebaseConnect()
diff --git a/src/createFirebaseInstance.js b/src/createFirebaseInstance.js
new file mode 100644
index 000000000..40db567a5
--- /dev/null
+++ b/src/createFirebaseInstance.js
@@ -0,0 +1,398 @@
+import { isObject } from 'lodash'
+import { authActions, queryActions, storageActions } from './actions'
+
+let firebaseInstance
+
+export const createFirebaseInstance = (firebase, configs, dispatch) => {
+ // Enable Logging based on config
+ if (configs.enableLogging) {
+ firebase.database.enableLogging(configs.enableLogging)
+ }
+
+ const rootRef = firebase.database().ref()
+
+ const instance = Object.defineProperty(firebase, '_', {
+ value: {
+ watchers: {},
+ config: configs,
+ authUid: null
+ },
+ writable: true,
+ enumerable: true,
+ configurable: true
+ })
+
+ /**
+ * @private
+ * @description Calls a method and attaches meta to value object
+ * @param {String} method - Method to run with meta attached
+ * @param {String} path - Path to location on Firebase which to set
+ * @param {Object|String|Boolean|Number} value - Value to write to Firebase
+ * @param {Function} onComplete - Function to run on complete
+ * @return {Promise} Containing reference snapshot
+ */
+ const withMeta = (method, path, value, onComplete) => {
+ if (isObject(value)) {
+ const prefix = method === 'update' ? 'updated' : 'created'
+ const dataWithMeta = {
+ ...value,
+ [`${prefix}At`]: firebase.database.ServerValue.TIMESTAMP
+ }
+ if (instance.auth().currentUser) {
+ dataWithMeta[`${prefix}By`] = instance.auth().currentUser.uid
+ }
+ return rootRef.child(path)[method](dataWithMeta, onComplete)
+ }
+ return rootRef.child(path)[method](value, onComplete)
+ }
+
+ /**
+ * @description Sets data to Firebase.
+ * @param {String} path - Path to location on Firebase which to set
+ * @param {Object|String|Boolean|Number} value - Value to write to Firebase
+ * @param {Function} onComplete - Function to run on complete (`not required`)
+ * @return {Promise} Containing reference snapshot
+ * @example Basic
+ * import React, { Component, PropTypes } from 'react'
+ * import { firebaseConnect } from 'react-redux-firebase'
+ * const Example = ({ firebase: { set } }) => (
+ * set('some/path', { here: 'is a value' })}>
+ * Set To Firebase
+ *
+ * )
+ * export default firebaseConnect()(Example)
+ */
+ const set = (path, value, onComplete) =>
+ rootRef.child(path).set(value, onComplete)
+
+ /**
+ * @description Sets data to Firebase along with meta data. Currently,
+ * this includes createdAt and createdBy. *Warning* using this function
+ * may have unintented consequences (setting createdAt even if data already
+ * exists)
+ * @param {String} path - Path to location on Firebase which to set
+ * @param {Object|String|Boolean|Number} value - Value to write to Firebase
+ * @param {Function} onComplete - Function to run on complete (`not required`)
+ * @return {Promise} Containing reference snapshot
+ */
+ const setWithMeta = (path, value, onComplete) =>
+ withMeta('set', path, value, onComplete)
+
+ /**
+ * @description Pushes data to Firebase.
+ * @param {String} path - Path to location on Firebase which to push
+ * @param {Object|String|Boolean|Number} value - Value to push to Firebase
+ * @param {Function} onComplete - Function to run on complete (`not required`)
+ * @return {Promise} Containing reference snapshot
+ * @example Basic
+ * import React, { Component, PropTypes } from 'react'
+ * import { firebaseConnect } from 'react-redux-firebase'
+ * const Example = ({ firebase: { push } }) => (
+ * push('some/path', true)}>
+ * Push To Firebase
+ *
+ * )
+ * export default firebaseConnect()(Example)
+ */
+ const push = (path, value, onComplete) =>
+ rootRef.child(path).push(value, onComplete)
+
+ /**
+ * @description Pushes data to Firebase along with meta data. Currently,
+ * this includes createdAt and createdBy.
+ * @param {String} path - Path to location on Firebase which to set
+ * @param {Object|String|Boolean|Number} value - Value to write to Firebase
+ * @param {Function} onComplete - Function to run on complete (`not required`)
+ * @return {Promise} Containing reference snapshot
+ */
+ const pushWithMeta = (path, value, onComplete) =>
+ withMeta('push', path, value, onComplete)
+
+ /**
+ * @description Updates data on Firebase and sends new data.
+ * @param {String} path - Path to location on Firebase which to update
+ * @param {Object|String|Boolean|Number} value - Value to update to Firebase
+ * @param {Function} onComplete - Function to run on complete (`not required`)
+ * @return {Promise} Containing reference snapshot
+ * @example Basic
+ * import React, { Component, PropTypes } from 'react'
+ * import { firebaseConnect } from 'react-redux-firebase'
+ * const Example = ({ firebase: { update } }) => (
+ * update('some/path', { here: 'is a value' })}>
+ * Update To Firebase
+ *
+ * )
+ * export default firebaseConnect()(Example)
+ */
+ const update = (path, value, onComplete) =>
+ rootRef.child(path).update(value, onComplete)
+
+ /**
+ * @description Updates data on Firebase along with meta. *Warning*
+ * using this function may have unintented consequences (setting
+ * createdAt even if data already exists)
+ * @param {String} path - Path to location on Firebase which to update
+ * @param {Object|String|Boolean|Number} value - Value to update to Firebase
+ * @param {Function} onComplete - Function to run on complete (`not required`)
+ * @return {Promise} Containing reference snapshot
+ */
+ const updateWithMeta = (path, value, onComplete) =>
+ withMeta('update', path, value, onComplete)
+
+ /**
+ * @description Removes data from Firebase at a given path.
+ * @param {String} path - Path to location on Firebase which to remove
+ * @param {Function} onComplete - Function to run on complete (`not required`)
+ * @return {Promise} Containing reference snapshot
+ * @example Basic
+ * import React, { Component, PropTypes } from 'react'
+ * import { firebaseConnect } from 'react-redux-firebase'
+ * const Example = ({ firebase: { remove } }) => (
+ * remove('some/path')}>
+ * Remove From Firebase
+ *
+ * )
+ * export default firebaseConnect()(Example)
+ */
+ const remove = (path, onComplete) =>
+ rootRef.child(path).remove(onComplete)
+
+ /**
+ * @description Sets data to Firebase only if the path does not already
+ * exist, otherwise it rejects.
+ * @param {String} path - Path to location on Firebase which to set
+ * @param {Object|String|Boolean|Number} value - Value to write to Firebase
+ * @param {Function} onComplete - Function to run on complete (`not required`)
+ * @return {Promise} Containing reference snapshot
+ * @example Basic
+ * import React, { Component, PropTypes } from 'react'
+ * import { firebaseConnect } from 'react-redux-firebase'
+ * const Example = ({ firebase: { uniqueSet } }) => (
+ * uniqueSet('some/unique/path', true)}>
+ * Unique Set To Firebase
+ *
+ * )
+ * export default firebaseConnect()(Example)
+ */
+ const uniqueSet = (path, value, onComplete) =>
+ rootRef.child(path)
+ .once('value')
+ .then(snap => {
+ if (snap.val && snap.val() !== null) {
+ const err = new Error('Path already exists.')
+ if (onComplete) onComplete(err)
+ return Promise.reject(err)
+ }
+ return rootRef.child(path).set(value, onComplete)
+ })
+
+ /**
+ * @description Upload a file to Firebase Storage with the option to store
+ * its metadata in Firebase Database
+ * @param {String} path - Path to location on Firebase which to set
+ * @param {File} file - File object to upload (usually first element from
+ * array output of select-file or a drag/drop `onDrop`)
+ * @param {String} dbPath - Database path to place uploaded file metadata
+ * @return {Promise} Containing the File object
+ */
+ const uploadFile = (path, file, dbPath) =>
+ storageActions.uploadFile(dispatch, instance, { path, file, dbPath })
+
+ /**
+ * @description Upload multiple files to Firebase Storage with the option
+ * to store their metadata in Firebase Database
+ * @param {String} path - Path to location on Firebase which to set
+ * @param {Array} files - Array of File objects to upload (usually from
+ * a select-file or a drag/drop `onDrop`)
+ * @param {String} dbPath - Database path to place uploaded files metadata.
+ * @return {Promise} Containing an array of File objects
+ */
+ const uploadFiles = (path, files, dbPath) =>
+ storageActions.uploadFiles(dispatch, instance, { path, files, dbPath })
+
+ /**
+ * @description Delete a file from Firebase Storage with the option to
+ * remove its metadata in Firebase Database
+ * @param {String} path - Path to location on Firebase which to set
+ * @param {String} dbPath - Database path to place uploaded file metadata
+ * @return {Promise} Containing the File object
+ */
+ const deleteFile = (path, dbPath) =>
+ storageActions.deleteFile(dispatch, instance, { path, dbPath })
+
+ /**
+ * @description Watch event. **Note:** this method is used internally
+ * so examples have not yet been created, and it may not work as expected.
+ * @param {String} type - Type of watch event
+ * @param {String} dbPath - Database path on which to setup watch event
+ * @param {String} storeAs - Name of listener results within redux store
+ * @return {Promise}
+ */
+ const watchEvent = (type, path, storeAs) =>
+ queryActions.watchEvent(instance, dispatch, { type, path, storeAs })
+
+ /**
+ * @description Unset a listener watch event. **Note:** this method is used
+ * internally so examples have not yet been created, and it may not work
+ * as expected.
+ * @param {String} eventName - Type of watch event
+ * @param {String} eventPath - Database path on which to setup watch event
+ * @param {String} storeAs - Name of listener results within redux store
+ * @return {Promise}
+ */
+ const unWatchEvent = (eventName, eventPath, queryId = undefined) =>
+ queryActions.unWatchEvent(instance, dispatch, eventName, eventPath, queryId)
+
+ /**
+ * @description Logs user into Firebase. For examples, visit the [auth section](/docs/auth.md)
+ * @param {Object} credentials - Credentials for authenticating
+ * @param {String} credentials.provider - External provider (google | facebook | twitter)
+ * @param {String} credentials.type - Type of external authentication (popup | redirect) (only used with provider)
+ * @param {String} credentials.email - Credentials for authenticating
+ * @param {String} credentials.password - Credentials for authenticating (only used with email)
+ * @return {Promise} Containing user's auth data
+ */
+ const login = credentials =>
+ authActions.login(dispatch, instance, credentials)
+
+ /**
+ * @description Logs user out of Firebase and empties firebase state from
+ * redux store
+ * @return {Promise}
+ */
+ const logout = () =>
+ authActions.logout(dispatch, instance)
+
+ /**
+ * @description Creates a new user in Firebase authentication. If
+ * `userProfile` config option is set, user profiles will be set to this
+ * location.
+ * @param {Object} credentials - Credentials for authenticating
+ * @param {String} credentials.email - Credentials for authenticating
+ * @param {String} credentials.password - Credentials for authenticating (only used with email)
+ * @param {Object} profile - Data to include within new user profile
+ * @return {Promise} Containing user's auth data
+ */
+ const createUser = (credentials, profile) =>
+ authActions.createUser(dispatch, instance, credentials, profile)
+
+ /**
+ * @description Sends password reset email
+ * @param {Object} credentials - Credentials for authenticating
+ * @param {String} credentials.email - Credentials for authenticating
+ * @return {Promise}
+ */
+ const resetPassword = (credentials) =>
+ authActions.resetPassword(dispatch, instance, credentials)
+
+ /**
+ * @description Confirm that a user's password has been reset
+ * @param {String} code - Password reset code to verify
+ * @param {String} password - New Password to confirm reset to
+ * @return {Promise}
+ */
+ const confirmPasswordReset = (code, password) =>
+ authActions.confirmPasswordReset(dispatch, instance, code, password)
+
+ /**
+ * @description Verify that a password reset code from a password reset
+ * email is valid
+ * @param {String} code - Password reset code to verify
+ * @return {Promise} Containing user auth info
+ */
+ const verifyPasswordResetCode = (code) =>
+ authActions.verifyPasswordResetCode(dispatch, instance, code)
+
+ /**
+ * @name ref
+ * @description Firebase ref function
+ * @return {database.Reference}
+ */
+ /**
+ * @name database
+ * @description Firebase database service instance including all Firebase storage methods
+ * @return {Database} Firebase database service
+ */
+ /**
+ * @name storage
+ * @description Firebase storage service instance including all Firebase storage methods
+ * @return {Storage} Firebase storage service
+ */
+ /**
+ * @name auth
+ * @description Firebase auth service instance including all Firebase auth methods
+ * @return {Auth}
+ */
+ return {
+ ...instance,
+ helpers: {
+ ref: path => firebase.database().ref(path),
+ storage: () => firebase.storage(),
+ set,
+ setWithMeta,
+ uniqueSet,
+ push,
+ pushWithMeta,
+ remove,
+ update,
+ updateWithMeta,
+ login,
+ logout,
+ uploadFile,
+ uploadFiles,
+ deleteFile,
+ createUser,
+ resetPassword,
+ confirmPasswordReset,
+ verifyPasswordResetCode,
+ watchEvent,
+ unWatchEvent
+ }
+ }
+}
+
+/**
+ * @external
+ * @description Expose Firebase instance created internally. Useful for
+ * integrations into external libraries such as redux-thunk and redux-observable.
+ * @example redux-thunk integration
+ * import { applyMiddleware, compose, createStore } from 'redux';
+ * import thunk from 'redux-thunk';
+ * import { reactReduxFirebase } from 'react-redux-firebase';
+ * import makeRootReducer from './reducers';
+ * import { getFirebase } from 'react-redux-firebase';
+ *
+ * const fbConfig = {} // your firebase config
+ *
+ * const store = createStore(
+ * makeRootReducer(),
+ * initialState,
+ * compose(
+ * applyMiddleware([
+ * // Pass getFirebase function as extra argument
+ * thunk.withExtraArgument(getFirebase)
+ * ]),
+ * reactReduxFirebase(fbConfig)
+ * )
+ * );
+ * // then later
+ * export const addTodo = (newTodo) =>
+ * (dispatch, getState, getFirebase) => {
+ * const firebase = getFirebase()
+ * firebase
+ * .push('todos', newTodo)
+ * .then(() => {
+ * dispatch({ type: 'SOME_ACTION' })
+ * })
+ * };
+ *
+ */
+export const getFirebase = () => {
+ // TODO: Handle recieveing config and creating firebase instance if it doesn't exist
+ /* istanbul ignore next: Firebase instance always exists during tests */
+ if (!firebaseInstance) {
+ throw new Error('Firebase instance does not yet exist. Check your compose function.') // eslint-disable-line no-console
+ }
+ // TODO: Create new firebase here with config passed in
+ return firebaseInstance
+}
diff --git a/src/helpers.js b/src/helpers.js
index be893b45b..31a191532 100644
--- a/src/helpers.js
+++ b/src/helpers.js
@@ -1,30 +1,34 @@
import {
size,
map,
- some,
- first,
- drop,
mapValues,
+ get,
+ isFunction,
+ every,
+ has,
+ set,
+ split,
+ last,
reduce,
- isString,
- defaultsDeep
+ defaultsDeep,
+ isString
} from 'lodash'
import { getPopulateObjs } from './utils/populate'
-import { metaParams, paramSplitChar } from './constants'
/**
* @description Detect whether items are loaded yet or not
* @param {Object} item - Item to check loaded status of. A comma seperated list is also acceptable.
* @return {Boolean} Whether or not item is loaded
* @example
- * import React, { Component, PropTypes } from 'react'
+ * import React, { Component } from 'react'
+ * import PropTypes from 'prop-types'
* import { connect } from 'react-redux'
- * import { firebaseConnect, isLoaded, dataToJS } from 'react-redux-firebase'
+ * import { firebaseConnect, isLoaded } from 'react-redux-firebase'
*
* @firebaseConnect(['/todos'])
* @connect(
- * ({ firebase }) => ({
- * todos: dataToJS(firebase, '/todos'),
+ * ({ firebase: { data: { todos } } }) => ({
+ * todos,
* })
* )
* class Todos extends Component {
@@ -57,14 +61,15 @@ export const isLoaded = function () {
* @param {Object} item - Item to check loaded status of. A comma seperated list is also acceptable.
* @return {Boolean} Whether or not item is empty
* @example
- * import React, { Component, PropTypes } from 'react'
+ * import React, { Component } from 'react'
+ * import PropTypes from 'prop-types'
* import { connect } from 'react-redux'
- * import { firebaseConnect, isEmpty, dataToJS } from 'react-redux-firebase'
+ * import { firebaseConnect, isEmpty } from 'react-redux-firebase'
*
* @firebaseConnect(['/todos'])
* @connect(
- * ({ firebase }) => ({
- * todos: dataToJS(firebase, '/todos'),
+ * ({ firebase: { data: { todos } } }) => ({
+ * todos // state.firebase.data.todos from redux passed as todos prop
* })
* )
* class Todos extends Component {
@@ -86,156 +91,14 @@ export const isLoaded = function () {
*/
export const isEmpty = data => !(data && size(data))
-/**
- * @description Fix path by adding "/" to path if needed
- * @param {String} path - Path string to fix
- * @return {String} - Fixed path
- * @private
- */
-export const fixPath = path =>
- ((path.substring(0, 1) === '/') ? '' : '/') + path
-
-/**
- * @description Convert Immutable Map to a Javascript object
- * @param {Object} data - Immutable Map to be converted to JS object (state.firebase)
- * @return {Object} data - Javascript version of Immutable Map
- * @return {Object} Data located at path within Immutable Map
- */
-export const toJS = data =>
- data && data.toJS
- ? data.toJS()
- : data
-
-/**
- * @description Convert parameter from Immutable Map to a Javascript object
- * @param {Map} firebase - Immutable Map to be converted to JS object (state.firebase)
- * @param {String} path - Path from state.firebase to convert to JS object
- * @param {Object|String|Boolean} notSetValue - Value to use if data is not available
- * @return {Object} Data located at path within Immutable Map
- * @example Basic
- * import { connect } from 'react-redux'
- * import { firebaseConnect, pathToJS } from 'react-redux-firebase'
- *
- * @firebaseConnect()
- * @connect(({ firebase }) => ({
- * profile: pathToJS(firebase, 'profile'),
- * auth: pathToJS(firebase, 'auth')
- * })
- * export default class MyComponent extends Component {
- * ...
- */
-export const pathToJS = (data, path, notSetValue) => {
- if (!data) {
- return notSetValue
- }
- const pathArr = fixPath(path).split(/\//).slice(1)
-
- if (data.getIn) {
- // Handle meta params (stored by string key)
- if (some(metaParams, (v) => pathArr.indexOf(v) !== -1)) {
- return toJS(
- data.getIn([
- first(pathArr),
- drop(pathArr).join(paramSplitChar)
- ], notSetValue)
- )
- }
- return toJS(data.getIn(pathArr, notSetValue))
- }
-
- return data
-}
-
-/**
- * @description Convert parameter under "data" path of Immutable Map to a Javascript object.
- * **NOTE:** Setting a default value will cause `isLoaded` to always return true
- * @param {Map} firebase - Immutable Map to be converted to JS object (state.firebase)
- * @param {String} path - Path of parameter to load
- * @param {Object|String|Boolean} notSetValue - Value to return if value is not
- * found in redux. This will cause `isLoaded` to always return true (since
- * value is set from the start).
- * @return {Object} Data located at path within Immutable Map
- * @example Basic
- * import { connect } from 'react-redux'
- * import { firebaseConnect, dataToJS } from 'react-redux-firebase'
- *
- * @firebaseConnect(['/todos'])
- * @connect(({ firebase }) => ({
- * // this.props.todos loaded from state.firebase.data.todos
- * todos: dataToJS(firebase, 'todos')
- * })
- * @example Default Value
- * import { connect } from 'react-redux'
- * import { firebaseConnect, dataToJS } from 'react-redux-firebase'
- * const defaultValue = {
- * 1: {
- * text: 'Example Todo'
- * }
- * }
- * @firebaseConnect(['/todos'])
- * @connect(({ firebase }) => ({
- * // this.props.todos loaded from state.firebase.data.todos
- * todos: dataToJS(firebase, 'todos', defaultValue)
- * })
- */
-export const dataToJS = (data, path, notSetValue) => {
- if (!data) {
- return notSetValue
- }
-
- const pathArr = `/data${fixPath(path)}`.split(/\//).slice(1)
-
- if (data.getIn) {
- return toJS(data.getIn(pathArr, notSetValue))
- }
-
- return data
-}
-
-/**
- * @description Convert parameter under "ordered" path of Immutable Map to a
- * Javascript array. This preserves order set by query.
- * @param {Map} firebase - Immutable Map to be converted to JS object (state.firebase)
- * @param {String} path - Path of parameter to load
- * @param {Object|String|Boolean} notSetValue - Value to return if value is not found
- * @return {Object} Data located at path within Immutable Map
- * @example Basic
- * import { connect } from 'react-redux'
- * import { firebaseConnect, orderedToJS } from 'react-redux-firebase'
- *
- * @firebaseConnect([
- * {
- * path: 'todos',
- * queryParams: ['orderByChild=text'] // order alphabetically based on text
- * },
- * ])
- * @connect(({ firebase }) => ({
- * // this.props.todos loaded from state.firebase.ordered.todos
- * todos: orderedToJS(firebase, 'todos')
- * })
- */
-export const orderedToJS = (data, path, notSetValue) => {
- if (!data) {
- return notSetValue
- }
-
- const pathArr = `/ordered${fixPath(path)}`.split(/\//).slice(1)
-
- if (data.getIn) {
- return toJS(data.getIn(pathArr, notSetValue))
- }
-
- return data
-}
-
/**
* @private
* @description Build child list based on populate
- * @param {Map} data - Immutable Map to be converted to JS object (state.firebase)
+ * @param {Object} data - Immutable Object to be converted to JS object (state.firebase)
* @param {Object} list - Path of parameter to load
* @param {Object} populate - Object with population settings
*/
-export const buildChildList = (data, list, p) =>
+export const buildChildList = (state, list, p) =>
mapValues(list, (val, key) => {
let getKey = val
// Handle key: true lists
@@ -245,149 +108,117 @@ export const buildChildList = (data, list, p) =>
const pathString = p.childParam
? `${p.root}/${getKey}/${p.childParam}`
: `${p.root}/${getKey}`
+
+ // console.log('path string:', { pathString, state })
// Set to child under key if populate child exists
- if (dataToJS(data, pathString)) {
+ if (state.data[pathString]) {
return p.keyProp
- ? { [p.keyProp]: getKey, ...dataToJS(data, pathString) }
- : dataToJS(data, pathString)
+ ? { [p.keyProp]: getKey, ...state.data[pathString] }
+ : get(state.data, pathString)
}
// Populate child does not exist
return val === true ? val : getKey
})
/**
- * @description Convert parameter under "data" path of Immutable Map to a
+ * @description Convert parameter under "data" path of Immutable Object to a
* Javascript object with parameters populated based on populates array
- * @param {Map} firebase - Immutable Map to be converted to JS object (state.firebase)
+ * @param {Object} firebase - Immutable Object to be converted to JS object (state.firebase)
* @param {String} path - Path of parameter to load
* @param {Array} populates - Array of populate objects
* @param {Object|String|Boolean} notSetValue - Value to return if value is not found
- * @return {Object} Data located at path within Immutable Map
+ * @return {Object} Data located at path within Immutable Object
* @example Basic
* import { connect } from 'react-redux'
- * import { firebaseConnect, helpers } from 'react-redux-firebase'
- * const { dataToJS } = helpers
+ * import { firebaseConnect } from 'react-redux-firebase'
* const populates = [{ child: 'owner', root: 'users' }]
*
* const fbWrapped = firebaseConnect([
* { path: '/todos', populates } // load "todos" and matching "users" to redux
* ])(App)
*
- * export default connect(({ firebase }) => ({
+ * export default connect((state) => ({
* // this.props.todos loaded from state.firebase.data.todos
* // each todo has child 'owner' populated from matching uid in 'users' root
- * // for loading un-populated todos use dataToJS(firebase, 'todos')
- * todos: populatedDataToJS(firebase, 'todos', populates),
+ * // for loading un-populated todos use state.firebase.data.todos
+ * todos: populate(state.firebase, 'todos', populates),
* }))(fbWrapped)
*/
-export const populatedDataToJS = (data, path, populates, notSetValue) => {
- if (!data) {
- return notSetValue
- }
+export const populate = (state, path, populates, notSetValue) => {
+ // TODO: Handle slash and lodash notation
// Handle undefined child
- if (!dataToJS(data, path, notSetValue)) {
- return dataToJS(data, path, notSetValue)
+ if (!state || !state.data[path]) {
+ return notSetValue
}
- const populateObjs = getPopulateObjs(populates)
- // reduce array of populates to object of combined populated data
- return reduce(
- map(populateObjs, (p, obj) => {
- // single item with iterable child
- if (dataToJS(data, path)[p.child]) {
+ // test if data is a single object vs a list of objects, try generating
+ // populates and testing for key presence
+ const populatesForData = getPopulateObjs(isFunction(populates)
+ ? populates(last(split(path, '/')), state.data[path])
+ : populates)
+ const dataHasPopluateChilds = every(populatesForData, (populate) => (
+ has(state.data[path], populate.child)
+ ))
+ if (dataHasPopluateChilds) {
+ // Data is a single object, resolve populates directly
+ return reduce(
+ map(populatesForData, (p, obj) => {
// populate child is key
- if (isString(dataToJS(data, path)[p.child])) {
- const key = dataToJS(data, path)[p.child]
+ if (isString(get(state.data[path], p.child))) {
+ const key = get(state.data[path], p.child)
const pathString = p.childParam
- ? `${p.root}/${key}/${p.childParam}`
- : `${p.root}/${key}`
- if (dataToJS(data, pathString)) {
- return {
- [p.child]: p.keyProp
- ? { [p.keyProp]: key, ...dataToJS(data, pathString) }
- : dataToJS(data, pathString)
- }
+ ? `${p.root}.${key}.${p.childParam}`
+ : `${p.root}.${key}`
+
+ if (get(state.data, pathString)) {
+ return set({}, p.child, p.keyProp
+ ? { [p.keyProp]: key, ...get(state.data, pathString) }
+ : get(state.data, pathString)
+ )
}
-
// matching child does not exist
- return dataToJS(data, path)
- }
-
- return {
- [p.child]: buildChildList(data, dataToJS(data, path)[p.child], p)
+ return get(state.data, path)
}
- }
- // list with child param in each item
- return mapValues(dataToJS(data, path), (child, i) => {
+ return set({}, p.child, buildChildList(state, get(state.data[path], p.child), p))
+ }),
+ // combine data from all populates to one object starting with original data
+ (obj, v) => defaultsDeep(v, obj), state.data[path])
+ } else {
+ // Data is a map of objects, each value has parameters to be populated
+ return mapValues(state.data[path], (child, childKey) => {
+ const populatesForDataItem = getPopulateObjs(isFunction(populates)
+ ? populates(childKey, child)
+ : populates)
+ const resolvedPopulates = map(populatesForDataItem, (p, obj) => {
// no matching child parameter
- if (!child || !child[p.child]) {
+ if (!child || !get(child, p.child)) {
return child
}
// populate child is key
- if (isString(child[p.child])) {
- const key = child[p.child]
+ if (isString(get(child, p.child))) {
+ const key = get(child, p.child)
+ // attach child paramter if it exists
const pathString = p.childParam
- ? `${p.root}/${key}/${p.childParam}`
- : `${p.root}/${key}`
- if (dataToJS(data, pathString)) {
- return {
- [p.child]: p.keyProp
- ? { [p.keyProp]: key, ...dataToJS(data, pathString) }
- : dataToJS(data, pathString)
- }
+ ? `${p.root}.${key}.${p.childParam}`
+ : `${p.root}.${key}`
+ if (get(state.data, pathString)) {
+ return set({}, p.child, (p.keyProp
+ ? { [p.keyProp]: key, ...get(state.data, pathString) }
+ : get(state.data, pathString)
+ ))
}
// matching child does not exist
return child
}
// populate child list
- return {
- [p.child]: buildChildList(data, child[p.child], p)
- }
+ return set({}, p.child, buildChildList(state, get(child, p.child), p))
})
- }),
- // combine data from all populates to one object starting with original data
- (obj, v) => defaultsDeep(v, obj), dataToJS(data, path))
-}
-
-/**
- * @description Load custom object from within store
- * @param {Map} firebase - Immutable Map to be converted to JS object (state.firebase)
- * @param {String} path - Path of parameter to load
- * @param {String} customPath - Part of store from which to load
- * @param {Object|String|Boolean} notSetValue - Value to return if value is not found
- * @return {Object} Data located at path within state
- * @example Basic
- * import { connect } from 'react-redux'
- * import { firebaseConnect, helpers } from 'react-redux-firebase'
- * const { customToJS } = helpers
- *
- * const fbWrapped = firebaseConnect(['/todos'])(App)
- *
- * export default connect(({ firebase }) => ({
- * // this.props.todos loaded from state.firebase.data.todos
- * requesting: customToJS(firebase, 'todos', 'requesting')
- * }))(fbWrapped)
- */
-export const customToJS = (data, path, custom, notSetValue) => {
- if (!data) {
- return notSetValue
- }
- const pathArr = `/${custom}${fixPath(path)}`.split(/\//).slice(1)
-
- if (data.getIn) {
- return toJS(data.getIn(pathArr, notSetValue))
+ // combine data from all populates to one object starting with original data
+ return reduce(
+ resolvedPopulates,
+ (obj, v) => defaultsDeep(v, obj),
+ child
+ )
+ })
}
-
- return data
-}
-
-export default {
- toJS,
- pathToJS,
- dataToJS,
- orderedToJS,
- populatedDataToJS,
- customToJS,
- isLoaded,
- isEmpty
}
diff --git a/src/reducer.js b/src/reducer.js
index 9948de1e3..68d909e05 100644
--- a/src/reducer.js
+++ b/src/reducer.js
@@ -1,164 +1,174 @@
-import { fromJS } from 'immutable'
-import { dropRight } from 'lodash'
-import { actionTypes, paramSplitChar } from './constants'
+import { combineReducers } from 'redux'
+import { set } from 'lodash'
+import { actionTypes } from './constants'
const {
START,
SET,
+ SET_ORDERED,
SET_PROFILE,
LOGIN,
LOGOUT,
LOGIN_ERROR,
NO_VALUE,
- UNSET_LISTENER,
+ // UNSET_LISTENER,
AUTHENTICATION_INIT_STARTED,
AUTHENTICATION_INIT_FINISHED,
- UNAUTHORIZED_ERROR
+ UNAUTHORIZED_ERROR,
+ AUTH_UPDATE_SUCCESS
} = actionTypes
-const emptyState = {
- auth: undefined,
- authError: undefined,
- profile: undefined,
- isInitializing: undefined,
- data: {},
- timestamp: {},
- requesting: {},
- requested: {}
-}
-
-const initialState = fromJS(emptyState)
-
const pathToArr = path => path ? path.split(/\//).filter(p => !!p) : []
/**
- * @name firebaseStateReducer
- * @description Reducer for react redux firebase. This function is called
- * automatically by redux every time an action is fired. Based on which action
- * is called and its payload, the reducer will update redux state with relevant
- * changes.
- * @param {Map} state - Current Redux State
- * @param {Object} action - Action which will modify state
- * @param {String} action.type - Type of Action being called
- * @param {String} action.data - Type of Action which will modify state
- * @return {Map} Redux State.
+ * Reducer for requesting state. Changed by `START` and `SET` actions.
+ * @param {Object} state - Current requesting redux state
+ * @param {object} action - Object containing the action that was dispatched
+ * @return {Object} Profile state after reduction
*/
-export default (state = initialState, action = {}) => {
- const { path, timestamp, requesting, requested } = action
- let pathArr
- let retVal
-
+const requestingReducer = (state = {}, action) => {
+ const { path, requesting } = action
switch (action.type) {
case START:
- pathArr = pathToArr(path)
- retVal = (requesting !== undefined)
- ? state.setIn(['requesting', pathArr.join(paramSplitChar)], fromJS(requesting))
- : state.deleteIn(['requesting', pathArr.join(paramSplitChar)])
-
- retVal = (requested !== undefined)
- ? retVal.setIn(['requested', pathArr.join(paramSplitChar)], fromJS(requested))
- : retVal.deleteIn(['requested', pathArr.join(paramSplitChar)])
-
- return retVal
-
case SET:
-
- const { data, ordered } = action
- pathArr = pathToArr(path)
-
- // Handle invalid keyPath error caused by deep setting to a null value
- if (data !== undefined && state.getIn(['data', ...pathArr]) === null) {
- retVal = state.deleteIn(['data', ...pathArr])
- } else if (state.getIn(dropRight(['data', ...pathArr])) === null) {
- retVal = state.deleteIn(dropRight(['data', ...pathArr]))
- } else {
- retVal = state // start with state
+ return {
+ ...state,
+ [pathToArr(path).join('/')]: requesting
}
+ // TODO: Handle NO_VALUE case
+ // case NO_VALUE:
+ default:
+ return state
+ }
+}
- retVal = (data !== undefined)
- ? retVal.setIn(['data', ...pathArr], fromJS(data))
- : retVal.deleteIn(['data', ...pathArr])
-
- retVal = (ordered !== undefined)
- ? retVal.setIn(['ordered', ...pathArr], fromJS(ordered))
- : retVal.deleteIn(['ordered', ...pathArr])
-
- retVal = (timestamp !== undefined)
- ? retVal.setIn(['timestamp', pathArr.join(paramSplitChar)], fromJS(timestamp))
- : retVal.deleteIn(['timestamp', pathArr.join(paramSplitChar)])
-
- retVal = (requesting !== undefined)
- ? retVal.setIn(['requesting', pathArr.join(paramSplitChar)], fromJS(requesting))
- : retVal.deleteIn(['requesting', pathArr.join(paramSplitChar)])
-
- retVal = (requested !== undefined)
- ? retVal.setIn(['requested', pathArr.join(paramSplitChar)], fromJS(requested))
- : retVal.deleteIn(['requested', pathArr.join(paramSplitChar)])
-
- return retVal
+const getPathStr = (path) => path ? path.replace('/', '.') : ''
+/**
+ * Reducer for data state. Changed by `LOGIN`, `LOGOUT`, and `LOGIN_ERROR`
+ * actions.
+ * @param {Object} state - Current data redux state
+ * @param {object} action - Object containing the action that was dispatched
+ * @return {Object} Profile state after reduction
+ */
+const dataReducer = (state = {}, action) => {
+ const { path, data, ordered } = action
+ switch (action.type) {
+ case SET:
+ return {
+ ...state,
+ ...set({}, getPathStr(path), data)
+ }
+ case SET_ORDERED:
+ return {
+ ...state,
+ ...set({}, getPathStr(path), ordered)
+ }
case NO_VALUE:
- pathArr = pathToArr(path)
- retVal = state.setIn(['data', ...pathArr], fromJS({}))
-
- retVal = (timestamp !== undefined)
- ? retVal.setIn(['timestamp', pathArr.join(paramSplitChar)], fromJS(timestamp))
- : retVal.deleteIn(['timestamp', pathArr.join(paramSplitChar)])
-
- retVal = (requesting !== undefined)
- ? retVal.setIn(['requesting', pathArr.join(paramSplitChar)], fromJS(requesting))
- : retVal.deleteIn(['requesting', pathArr.join(paramSplitChar)])
-
- retVal = (requested !== undefined)
- ? retVal.setIn(['requested', pathArr.join(paramSplitChar)], fromJS(requested))
- : retVal.deleteIn(['requested', pathArr.join(paramSplitChar)])
-
- return retVal
-
- case UNSET_LISTENER:
- pathArr = pathToArr(path)
- retVal = state.deleteIn(['data', ...pathArr])
- retVal = retVal.deleteIn(['timestamp', pathArr.join(paramSplitChar)])
- retVal = retVal.deleteIn(['requesting', pathArr.join(paramSplitChar)])
- retVal = retVal.deleteIn(['requested', pathArr.join(paramSplitChar)])
+ return {
+ ...state,
+ ...set({}, getPathStr(path), {})
+ }
+ default:
+ return state
+ }
+}
- return retVal
+/**
+ * Reducer for auth state. Changed by `LOGIN`, `LOGOUT`, and `LOGIN_ERROR`
+ * actions.
+ * @param {Object} state - Current auth redux state
+ * @param {object} action - Object containing the action that was dispatched
+ * @return {Object} Profile state after reduction
+ */
+const authReducer = (state = {}, action) => {
+ switch (action.type) {
+ case LOGIN:
+ case AUTH_UPDATE_SUCCESS:
+ return action.auth || undefined
+ case LOGOUT:
+ case LOGIN_ERROR:
+ return null
+ default:
+ return state
+ }
+}
+/**
+ * Reducer for profile state. Changed by `SET_PROFILE`, `LOGOUT`, and
+ * `LOGIN_ERROR` actions.
+ * @param {Object} state - Current profile redux state
+ * @param {object} action - Object containing the action that was dispatched
+ * @return {Object} Profile state after reduction
+ */
+const profileReducer = (state = {}, action) => {
+ switch (action.type) {
case SET_PROFILE:
- return (action.profile !== undefined)
- ? state.setIn(['profile'], fromJS(action.profile))
- : state.deleteIn(['profile'])
-
+ return {
+ ...state,
+ ...action.profile
+ }
case LOGOUT:
- return fromJS({
- auth: null,
- authError: null,
- profile: null,
- isInitializing: false,
- data: {}
- })
-
- case LOGIN:
- return state.setIn(['auth'], fromJS(action.auth))
- .setIn(['authError'], null)
-
case LOGIN_ERROR:
+ return null
+ default:
return state
- .setIn(['authError'], action.authError)
- .setIn(['auth'], null)
- .setIn(['profile'], null)
+ }
+}
+/**
+ * Reducer for isInitializing state. Changed by `AUTHENTICATION_INIT_STARTED`
+ * and `AUTHENTICATION_INIT_FINISHED` actions.
+ * @param {Object} state - Current isInitializing redux state
+ * @param {object} action - Object containing the action that was dispatched
+ * @return {Object} Profile state after reduction
+ */
+const isInitializingReducer = (state = false, action) => {
+ switch (action.type) {
case AUTHENTICATION_INIT_STARTED:
- return initialState.setIn(['isInitializing'], true)
- // return state.setIn(['isInitializing'], true) // throws state.setIn not a function error
-
+ return true
case AUTHENTICATION_INIT_FINISHED:
- return state.setIn(['isInitializing'], false)
+ return false
+ default:
+ return state
+ }
+}
+/**
+ * Reducer for errors state. Changed by `UNAUTHORIZED_ERROR`
+ * and `LOGOUT` actions.
+ * @param {Object} state - Current authError redux state
+ * @param {object} action - Object containing the action that was dispatched
+ * @return {Object} Profile state after reduction
+ */
+const errorsReducer = (state = [], action) => {
+ switch (action.type) {
case UNAUTHORIZED_ERROR:
- return state.setIn(['authError'], action.authError)
-
+ return [...state, action.payload]
+ case LOGOUT:
+ return null
default:
return state
}
}
+
+/**
+ * @name firebaseStateReducer
+ * @description Reducer for react redux firebase. This function is called
+ * automatically by redux every time an action is fired. Based on which action
+ * is called and its payload, the reducer will update redux state with relevant
+ * changes.
+ * @param {Map} state - Current Redux State
+ * @param {Object} action - Action which will modify state
+ * @param {String} action.type - Type of Action being called
+ * @param {String} action.data - Type of Action which will modify state
+ * @return {Map} State
+ */
+export default combineReducers({
+ requesting: requestingReducer,
+ data: dataReducer,
+ auth: authReducer,
+ profile: profileReducer,
+ isInitializing: isInitializingReducer,
+ errors: errorsReducer
+})
diff --git a/src/utils/actions.js b/src/utils/actions.js
index 151d0e53e..bdbc4c260 100644
--- a/src/utils/actions.js
+++ b/src/utils/actions.js
@@ -1,3 +1,5 @@
+import { isObject } from 'lodash'
+
/**
* @description Wrap method call in dispatched actions
* @param {Function} dispatch - Action dispatch function
@@ -9,7 +11,8 @@
*/
export const wrapInDispatch = (dispatch, { method, args, types }) => {
dispatch({
- type: types[0]
+ type: isObject(types[0]) ? types[0].type : types[0],
+ payload: isObject(types[0]) ? types[0].payload : { args }
})
return method(...args)
.then((val) => {
@@ -17,11 +20,13 @@ export const wrapInDispatch = (dispatch, { method, args, types }) => {
type: types[1],
payload: val
})
+ return val
})
.catch((err) => {
dispatch({
type: types[2],
payload: err
})
+ return Promise.reject(err)
})
}
diff --git a/src/utils/populate.js b/src/utils/populate.js
index ef9913e19..7a61c9010 100644
--- a/src/utils/populate.js
+++ b/src/utils/populate.js
@@ -2,12 +2,14 @@ import {
filter,
isString,
isArray,
+ isFunction,
isObject,
map,
get,
forEach,
set,
- has
+ has,
+ every
} from 'lodash'
/**
@@ -127,23 +129,30 @@ export const populateList = (firebase, list, p, results) => {
* @param {Object} originalObj - Object to have parameter populated
* @param {Object} populateString - String containg population data
*/
-export const promisesForPopulate = (firebase, originalData, populatesIn) => {
+export const promisesForPopulate = (firebase, dataKey, originalData, populatesIn) => {
// TODO: Handle selecting of parameter to populate with (i.e. displayName of users/user)
let promisesArray = []
let results = {}
- const populates = getPopulateObjs(populatesIn)
- // Loop over all populates
- forEach(populates, (p) => {
- // Data is single parameter
- if (has(originalData, p.child)) {
- // Single Parameter is single ID
- if (isString(originalData[p.child])) {
+
+ // test if data is a single object, try generating populates and looking for the child
+ const populatesForData = getPopulateObj(isFunction(populatesIn)
+ ? populatesIn(dataKey, originalData)
+ : populatesIn)
+
+ const dataHasPopulateChilds = every(populatesForData, (populate) => (
+ has(originalData, populate.child)
+ ))
+
+ if (dataHasPopulateChilds) {
+ // Data is a single object, resolve populates directly
+ forEach(populatesForData, (p) => {
+ if (isString(get(originalData, p.child))) {
return promisesArray.push(
- getPopulateChild(firebase, p, originalData[p.child])
+ getPopulateChild(firebase, p, get(originalData, p.child))
.then((v) => {
// write child to result object under root name if it is found
if (v) {
- set(results, `${p.root}.${originalData[p.child]}`, v)
+ set(results, `${p.root}.${get(originalData, p.child)}`, v)
}
})
)
@@ -151,43 +160,51 @@ export const promisesForPopulate = (firebase, originalData, populatesIn) => {
// Single Parameter is list
return promisesArray.push(
- populateList(firebase, originalData[p.child], p, results)
+ populateList(firebase, get(originalData, p.child), p, results)
)
- }
-
- // Data is list, each item has parameter to be populated
+ })
+ } else {
+ // Data is a map of objects, each value has parameters to be populated
forEach(originalData, (d, key) => {
- // Get value of parameter to be populated (key or list of keys)
- const idOrList = get(d, p.child)
+ // generate populates for this data item if a fn was passed
+ const populatesForDataItem = getPopulateObj(isFunction(populatesIn)
+ ? populatesIn(key, d)
+ : populatesIn)
- // Parameter/child of list item does not exist
- if (!idOrList) {
- return
- }
+ // resolve each populate for this data item
+ forEach(populatesForDataItem, (p) => {
+ // get value of parameter to be populated (key or list of keys)
+ const idOrList = get(d, p.child)
- // Parameter of each list item is single ID
- if (isString(idOrList)) {
- return promisesArray.push(
- getPopulateChild(firebase, p, idOrList)
- .then((v) => {
- // write child to result object under root name if it is found
- if (v) {
- set(results, `${p.root}.${idOrList}`, v)
- }
- return results
- })
- )
- }
+ // Parameter/child of list item does not exist
+ if (!idOrList) {
+ return
+ }
- // Parameter of each list item is a list of ids
- if (isArray(idOrList) || isObject(idOrList)) {
- // Create single promise that includes a promise for each child
- return promisesArray.push(
- populateList(firebase, idOrList, p, results)
- )
- }
+ // Parameter of each list item is single ID
+ if (isString(idOrList)) {
+ return promisesArray.push(
+ getPopulateChild(firebase, p, idOrList)
+ .then((v) => {
+ // write child to result object under root name if it is found
+ if (v) {
+ set(results, `${p.root}.${idOrList}`, v)
+ }
+ return results
+ })
+ )
+ }
+
+ // Parameter of each list item is a list of ids
+ if (isArray(idOrList) || isObject(idOrList)) {
+ // Create single promise that includes a promise for each child
+ return promisesArray.push(
+ populateList(firebase, idOrList, p, results)
+ )
+ }
+ })
})
- })
+ }
// Return original data after population promises run
return Promise.all(promisesArray).then(() => results)
diff --git a/src/utils/query.js b/src/utils/query.js
index 9d2f44887..94c84422e 100644
--- a/src/utils/query.js
+++ b/src/utils/query.js
@@ -1,4 +1,5 @@
import { actionTypes } from '../constants'
+import { isFunction } from 'lodash'
const { UNSET_LISTENER } = actionTypes
@@ -95,16 +96,21 @@ export const getWatcherCount = (firebase, event, path, queryId = undefined) => {
export const unsetWatcher = (firebase, dispatch, event, path, queryId = undefined) => {
let id = queryId || getQueryIdFromPath(path, event) || getWatchPath(event, path)
path = path.split('#')[0]
- if (firebase._.watchers[id] <= 1) {
- delete firebase._.watchers[id]
+ const { watchers, config } = firebase._
+ if (watchers[id] <= 1) {
+ delete watchers[id]
if (event !== 'first_child' && event !== 'once') {
firebase.database().ref().child(path).off(event)
- if (firebase._.config.distpatchOnUnsetListener) {
+ // TODO: Remove config.distpatchOnUnsetListener
+ if (config.dispatchOnUnsetListener || config.distpatchOnUnsetListener) {
+ if (config.distpatchOnUnsetListener && isFunction(console.warn)) { // eslint-disable-line no-console
+ console.warn('config.distpatchOnUnsetListener is Depreceated and will be removed in future versions. Please use config.dispatchOnUnsetListener (dispatch spelled correctly).') // eslint-disable-line no-console
+ }
dispatch({ type: UNSET_LISTENER, path })
}
}
- } else if (firebase._.watchers[id]) {
- firebase._.watchers[id]--
+ } else if (watchers[id]) {
+ watchers[id]--
}
}
diff --git a/src/utils/storage.js b/src/utils/storage.js
index ca63a4e3d..4a8072a1b 100644
--- a/src/utils/storage.js
+++ b/src/utils/storage.js
@@ -4,7 +4,7 @@ export const deleteFile = (firebase, { path, dbPath }) =>
.delete()
.then(() =>
!dbPath
- ? ({ path, dbPath })
+ ? ({ path })
: firebase // Handle option for removing file info from database
.database()
.ref(dbPath)
diff --git a/tests/setup.js b/tests/setup.js
index 83e4e54e9..fe7097b64 100644
--- a/tests/setup.js
+++ b/tests/setup.js
@@ -6,6 +6,15 @@ var sinon = require('sinon')
var chaiAsPromised = require('chai-as-promised')
var sinonChai = require('sinon-chai')
var jsdom = require('jsdom').jsdom
+var FirebaseServer = require('firebase-server')
+
+new FirebaseServer(5000, 'localhost.firebaseio.test', { // eslint-disable-line no-new
+ users: {
+ Iq5b0qK2NtgggT6U3bU6iZRGyma2: {
+ displayName: 'Tester'
+ }
+ }
+})
// Chai Plugins
chai.use(chaiAsPromised)
@@ -21,15 +30,17 @@ global.navigator = global.window.navigator
// needed to fix "Error: The XMLHttpRequest compatibility library was not found." from Firebase auth
global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
+// needed to fix: "FIREBASE WARNING: wss:// URL used, but browser isn't known to support websockets. Trying anyway."
+global.WebSocket = require('ws')
-// Firebase
var Firebase = global.Firebase = require('firebase')
+
var fbConfig = global.fbConfig = {
- apiKey: 'AIzaSyA-4ZcRO93M3bl6SdUjTiqN7tNfNOjV6D4',
- authDomain: 'tester-2d4fa.firebaseapp.com',
- databaseURL: 'https://tester-2d4fa.firebaseio.com',
- storageBucket: 'tester-2d4fa.appspot.com',
- messagingSenderId: '553568276840'
+ apiKey: 'asdf', // placeholder
+ authDomain: 'asdf', // placeholder
+ databaseURL: 'ws://127.0.1:5000',
+ storageBucket: 'asdf', // placeholder
+ messagingSenderId: 'asdf' // placeholder
}
// Swallow firebase reinitialize error (useful when using watch)
diff --git a/tests/unit/actions/auth.spec.js b/tests/unit/actions/auth.spec.js
index 517872404..94da7676e 100644
--- a/tests/unit/actions/auth.spec.js
+++ b/tests/unit/actions/auth.spec.js
@@ -1,4 +1,3 @@
-/* global describe expect it beforeEach */
import {
dispatchLoginError,
dispatchUnauthorizedError,
@@ -167,7 +166,7 @@ describe('Actions: Auth', () => {
describe('createUserProfile', () => {
it('creates profile if config is enabled', () => {
- return createUserProfile(dispatch, firebase, { uid: '123', email: 'test@test.com', providerData: [{}] }, { some: 'asdf' })
+ return createUserProfile(dispatch, Firebase, { uid: '123', email: 'test@test.com', providerData: [{}] }, { some: 'asdf' })
.then((profile) => {
expect(profile).to.be.an.object
})
@@ -176,13 +175,13 @@ describe('Actions: Auth', () => {
describe('login', () => {
it('handles invalid email login', () =>
- login(dispatch, firebase, fakeLogin)
+ login(dispatch, fakeFirebase, fakeLogin)
.catch((err) => {
expect(err.code).to.equal('auth/user-not-found')
})
, 4000)
it('handles invalid token login', () =>
- login(dispatch, firebase, { token: 'test@tst.com' })
+ login(dispatch, fakeFirebase, { token: 'test@tst.com' })
.catch((err) => {
expect(err.code).to.equal('auth/invalid-custom-token')
})
@@ -327,7 +326,6 @@ describe('Actions: Auth', () => {
it('auth/weak-password', () => {
return confirmPasswordReset(dispatch, fakeFirebase, 'auth/weak-password', 'error')
.catch((err) => {
- console.log('error:', err)
expect(err.code).to.be.a.string
})
})
diff --git a/tests/unit/actions/query.spec.js b/tests/unit/actions/query.spec.js
index e31a60bf4..f8d11ac5b 100644
--- a/tests/unit/actions/query.spec.js
+++ b/tests/unit/actions/query.spec.js
@@ -1,4 +1,3 @@
-/* global describe expect it beforeEach */
import queryAction from '../../../src/actions/query'
import {
watchEvent,
@@ -49,7 +48,7 @@ describe('Actions: Query', () => {
expect(unWatchEvent).to.be.a.function
})
it('runs given basic params', () => {
- expect(unWatchEvent(firebase, dispatch, 'once', 'projects')).to.be.a.function
+ expect(unWatchEvent(firebase, dispatch, { type: 'once', path: 'projects' })).to.be.a.function
})
})
diff --git a/tests/unit/actions/storage.js b/tests/unit/actions/storage.spec.js
similarity index 81%
rename from tests/unit/actions/storage.js
rename to tests/unit/actions/storage.spec.js
index 541949f06..e365b9f34 100644
--- a/tests/unit/actions/storage.js
+++ b/tests/unit/actions/storage.spec.js
@@ -1,4 +1,4 @@
-/* global describe expect it beforeEach sinon */
+/* eslint-disable no-unused-expressions */
import {
uploadFileWithProgress,
uploadFile,
@@ -23,7 +23,8 @@ const fakeFirebase = {
on: () => Promise.resolve({ val: () => ({ some: 'obj' }) }),
off: () => Promise.resolve({ val: () => ({ some: 'obj' }) }),
once: () => Promise.resolve({ val: () => ({ some: 'obj' }) })
- })
+ }),
+ remove: () => Promise.resolve({ val: () => ({ some: 'obj' }) })
})
}),
storage: () => ({
@@ -94,11 +95,20 @@ describe('Actions: Storage', () => {
it('is exported', () => {
expect(deleteFile).to.be.a.function
})
- it('runs given basic params', () =>
- deleteFile(dispatch, fakeFirebase, { path: 'projects', file: { name: 'test.png' } })
- .then((snap) => {
- expect(snap).to.be.an.object
- })
+
+ it('runs given path', () =>
+ expect(deleteFile(dispatch, fakeFirebase, { path: 'projects' }))
+ .to
+ .eventually
+ .become({path: 'projects'})
)
+
+ it('runs given basic params', () => {
+ const metaObj = { path: 'projects/test.png', dbPath: 'test.png' }
+ return expect(deleteFile(dispatch, fakeFirebase, metaObj))
+ .to
+ .eventually
+ .become(metaObj)
+ })
})
})
diff --git a/tests/unit/compose.spec.js b/tests/unit/compose.spec.js
index ef4337144..8206a062e 100644
--- a/tests/unit/compose.spec.js
+++ b/tests/unit/compose.spec.js
@@ -1,9 +1,10 @@
-/* global describe expect it */
import { omit } from 'lodash'
import { createStore, combineReducers, compose } from 'redux'
-import composeFunc, { getFirebase } from '../../src/compose'
+import composeFunc from '../../src/compose'
+
const exampleData = { data: { some: 'data' } }
const reducer = sinon.spy()
+
const generateCreateStore = (params) =>
compose(composeFunc(
params ? omit(fbConfig, params) : fbConfig,
@@ -99,7 +100,7 @@ describe('Compose', () => {
)
it('has on err onComplete', () => {
const func = sinon.spy()
- helpers.uniqueSet('test', {some: 'asdf'}, func)
+ return helpers.uniqueSet('test', {some: 'asdf'}, func)
.catch((err) => {
expect(func).to.have.been.calledOnce
})
@@ -119,7 +120,6 @@ describe('Compose', () => {
describe.skip('unWatchEvent', () => {
it.skip('unWatchesEvent', () =>
helpers.unWatchEvent('value', 'test')
-
)
})
@@ -155,6 +155,44 @@ describe('Compose', () => {
}
})
+ describe('updateProfile', () => {
+ it('acccepts an object', () =>
+ expect(helpers.updateProfile({ displayName: 'test' })).to.eventually.become(undefined)
+ )
+ })
+
+ describe('updateAuth', () => {
+ it('rejects when not authenticated', () =>
+ expect(helpers.updateAuth()).to.be.rejectedWith('User must be logged in to update auth.')
+ )
+
+ // TODO: test that update auth when authenticated
+ it.skip('updates auth object if authenticated', () =>
+ expect(helpers.updateAuth()).to.eventually.become(undefined)
+ )
+
+ // TODO: test that updateProfile is called if updateInProfile is true
+ it.skip('calls update profile if updateInProfile is true', () =>
+ expect(helpers.updateAuth({}, true)).to.eventually.become(undefined)
+ )
+ })
+
+ describe('updateEmail', () => {
+ it('rejects when not authenticated', () =>
+ expect(helpers.updateEmail()).to.be.rejectedWith('User must be logged in to update email.')
+ )
+
+ // TODO: test that update auth when authenticated
+ it.skip('updates auth object if authenticated', () =>
+ expect(helpers.updateEmail()).to.eventually.become(undefined)
+ )
+
+ // TODO: test that updateProfile is called if updateInProfile is true
+ it.skip('calls update profile if updateInProfile is true', () =>
+ expect(helpers.updateEmail({}, true)).to.eventually.become(undefined)
+ )
+ })
+
describe('storage', () => {
try {
helpers.storage()
@@ -180,7 +218,7 @@ describe('Compose', () => {
})
})
- describe('getFirebase', () => {
+ describe.skip('getFirebase', () => {
it('exports firebase instance', () => {
expect(getFirebase()).to.be.an.object
})
diff --git a/tests/unit/connect.spec.js b/tests/unit/connect.spec.js
index 86d1234e7..1b6008108 100644
--- a/tests/unit/connect.spec.js
+++ b/tests/unit/connect.spec.js
@@ -1,9 +1,10 @@
-import React, { createClass, Children, Component, PropTypes } from 'react'
+import React, { createClass, Children, Component } from 'react'
+import PropTypes from 'prop-types'
import ReactDOM from 'react-dom'
-import connect from '../../src/connect'
-import reactReduxFirebase from '../../src/compose'
import TestUtils from 'react-addons-test-utils'
import { createStore, compose, combineReducers } from 'redux'
+import connect from '../../src/connect'
+import reactReduxFirebase from '../../src/compose'
describe('Connect', () => {
class Passthrough extends Component {
diff --git a/tests/unit/helpers.spec.js b/tests/unit/helpers.spec.js
index 71d5911d3..067472b97 100644
--- a/tests/unit/helpers.spec.js
+++ b/tests/unit/helpers.spec.js
@@ -1,6 +1,7 @@
-/* global describe expect it */
import { fromJS } from 'immutable'
-import helpers from '../../src/helpers'
+import * as helpers from '../../src/helpers'
+import { buildChildList } from '../../src/helpers'
+
const exampleData = {
data: {
some: 'data',
@@ -27,6 +28,19 @@ const exampleData = {
ABC: true,
abc: true
}
+ },
+ QRS: {
+ owner: 'ABC',
+ nested: {
+ owner: 'ABC'
+ },
+ notes: {
+ 123: true,
+ },
+ collaborators: {
+ ABC: true,
+ abc: true
+ }
}
},
users: {
@@ -40,104 +54,72 @@ const exampleData = {
}
}
},
+ ordered: {
+ projects: [
+ {
+ owner: 'ABC',
+ notes: {
+ 123: true,
+ },
+ collaborators: {
+ ABC: true,
+ abc: true
+ }
+ },
+ ]
+ },
timestamp: { 'some/path': { test: 'key' } },
snapshot: { some: 'snapshot' }
}
const exampleState = fromJS(exampleData)
describe('Helpers:', () => {
- describe('toJS', () => {
- it('exists', () => {
- expect(helpers).to.respondTo('toJS')
- })
-
- it('handles non-immutable data', () => {
- expect(helpers.toJS(exampleData)).to.equal(exampleData)
- })
-
- it('handles immutable data', () => {
- expect(helpers.toJS(exampleState)).to.be.an.object
- })
- })
-
- describe('pathToJS', () => {
- it('exists', () => {
- expect(helpers).to.respondTo('pathToJS')
- })
-
- it('passes notSetValue', () => {
- expect(helpers.pathToJS(null, '/some', exampleData))
- .to
- .equal(exampleData)
- })
-
- it('gets data', () => {
- expect(helpers.pathToJS(exampleState, '/some', exampleData))
- .to
- .equal(exampleData)
- })
-
- it('gets meta (string key)', () => {
- expect(helpers.pathToJS(exampleState, 'timestamp/some/path'))
- .to
- .have
- .keys('test')
- })
-
- it('returns state if its not an immutable Map', () => {
- const fakeState = {}
- expect(helpers.pathToJS(fakeState, 'asdf'))
- .to
- .equal(fakeState)
- })
- })
-
- describe('dataToJS', () => {
+ describe.skip('ordered', () => {
it('exists', () => {
- expect(helpers).to.respondTo('dataToJS')
+ expect(helpers).to.respondTo('ordered')
})
it('passes notSetValue', () => {
- expect(helpers.dataToJS(null, '/some', exampleData))
+ expect(helpers.ordered(null, '/some', exampleData))
.to
.equal(exampleData)
})
it('gets data from state', () => {
- const path = 'some'
- expect(helpers.dataToJS(exampleState, path, exampleData))
+ const path = 'projects'
+ expect(JSON.stringify(helpers.ordered(exampleState, path, exampleData)))
.to
- .equal(exampleData.data[path])
+ .equal(JSON.stringify(exampleData.ordered[path]))
})
it('returns state if its not an immutable Map', () => {
const fakeState = { }
- expect(helpers.dataToJS(fakeState, 'asdf'))
+ expect(helpers.ordered(fakeState, 'asdf'))
.to
.equal(fakeState)
})
})
- describe('populatedDataToJS', () => {
+ describe('populate', () => {
it('exists', () => {
- expect(helpers).to.respondTo('populatedDataToJS')
+ expect(helpers).to.respondTo('populate')
})
it('passes notSetValue', () => {
- expect(helpers.populatedDataToJS(null, '/some', [], exampleData))
+ expect(helpers.populate(null, '/some', [], exampleData))
.to
.equal(exampleData)
})
it('returns undefined for non existant path', () => {
- expect(helpers.populatedDataToJS(exampleState, '/asdfasdfadsf', []))
+ expect(helpers.populate(exampleState, '/asdfasdfadsf', []))
.to
.equal(undefined)
})
it('returns unpopulated data for no populates', () => {
const path = 'projects'
- expect(helpers.populatedDataToJS(exampleState, path, []).CDF.owner)
+ expect(helpers.populate(exampleState, path, []).CDF.owner)
.to
.equal(exampleData.data[path].CDF.owner)
})
@@ -147,16 +129,25 @@ describe('Helpers:', () => {
it('populates value', () => {
const path = 'projects/CDF'
const rootName = 'users'
- expect(helpers.populatedDataToJS(exampleState, path, [{ child: 'owner', root: rootName }]).owner)
+ expect(helpers.populate(exampleState, path, [{ child: 'owner', root: rootName }]).owner)
+ .to
+ .have
+ .property('displayName', 'scott')
+ })
+ it('handles child path', () => {
+ const path = 'projects/QRS'
+ const rootName = 'users'
+ const populates = [{ child: 'nested.owner', root: rootName }]
+ const populatedData = helpers.populate(exampleState, path, populates)
+ expect(populatedData.nested.owner)
.to
.have
.property('displayName', 'scott')
})
-
it('populates childParam', () => {
const path = 'projects/CDF'
const rootName = 'users'
- expect(helpers.populatedDataToJS(exampleState, path, [{ child: 'owner', root: rootName, childParam: 'displayName' }]).owner)
+ expect(helpers.populate(exampleState, path, [{ child: 'owner', root: rootName, childParam: 'displayName' }]).owner)
.to
.have
.equal('scott')
@@ -164,7 +155,7 @@ describe('Helpers:', () => {
it('keeps non-existant children', () => {
const path = 'projects/OKF'
const rootName = 'users'
- expect(helpers.populatedDataToJS(exampleState, path, [{ child: 'owner', root: rootName }]).owner)
+ expect(helpers.populate(exampleState, path, [{ child: 'owner', root: rootName }]).owner)
.to
.have
.equal('asdfasdf')
@@ -177,7 +168,7 @@ describe('Helpers:', () => {
const populates = [
{ child: 'collaborators', root: rootName },
]
- const populatedData = helpers.populatedDataToJS(exampleState, path, populates)
+ const populatedData = helpers.populate(exampleState, path, populates)
expect(populatedData)
.to
.have
@@ -186,6 +177,22 @@ describe('Helpers:', () => {
})
})
+ describe('config as function', () => {
+ it('populates values', () => {
+ const path = 'projects/CDF'
+ const rootName = 'users'
+ const populates = (projectKey, projectData) => ([
+ // configure populates with key / data tuple...
+ { child: 'owner', root: rootName }
+ ])
+ const populatedData = helpers.populate(exampleState, path, populates)
+ expect(populatedData.owner)
+ .to
+ .have
+ .property('displayName', 'scott')
+ })
+ })
+
})
describe('list', () => {
@@ -195,7 +202,7 @@ describe('Helpers:', () => {
const path = 'projects'
const rootName = 'users'
const valName = 'CDF'
- expect(helpers.populatedDataToJS(exampleState, path, [{ child: 'owner', root: rootName }])[valName].owner)
+ expect(helpers.populate(exampleState, path, [{ child: 'owner', root: rootName }])[valName].owner)
.to
.have
.property('displayName', 'scott')
@@ -205,7 +212,7 @@ describe('Helpers:', () => {
const path = 'projects'
const rootName = 'users'
const valName = 'CDF'
- expect(helpers.populatedDataToJS(exampleState, path, [{ child: 'owner', root: rootName, childParam: 'displayName' }])[valName].owner)
+ expect(helpers.populate(exampleState, path, [{ child: 'owner', root: rootName, childParam: 'displayName' }])[valName].owner)
.to
.have
.equal('scott')
@@ -220,7 +227,7 @@ describe('Helpers:', () => {
const populates = [
{ child: 'collaborators', root: rootName },
]
- const populatedData = helpers.populatedDataToJS(exampleState, path, populates)
+ const populatedData = helpers.populate(exampleState, path, populates)
expect(populatedData)
.to
.have
@@ -235,12 +242,12 @@ describe('Helpers:', () => {
const populates = [
{ child: 'collaborators', root: rootName },
]
- expect(helpers.populatedDataToJS(exampleState, path, populates))
+ expect(helpers.populate(exampleState, path, populates))
.to
.have
.deep
.property(`${valName}.collaborators.abc`, true)
- expect(helpers.populatedDataToJS(exampleState, path, populates))
+ expect(helpers.populate(exampleState, path, populates))
.to
.have
.deep
@@ -259,13 +266,13 @@ describe('Helpers:', () => {
{ child: 'notes', root: 'notes' },
]
// check that notes are populated
- expect(helpers.populatedDataToJS(exampleState, `/${path}`, populates))
+ expect(helpers.populate(exampleState, `/${path}`, populates))
.to
.have
.deep
.property(`${valName}.notes.123.text`, exampleData.data.notes['123'].text)
// check that owner is populated
- expect(helpers.populatedDataToJS(exampleState, `/${path}`, populates))
+ expect(helpers.populate(exampleState, `/${path}`, populates))
.to
.have
.deep
@@ -282,12 +289,12 @@ describe('Helpers:', () => {
{ child: 'collaborators', root: rootName },
]
// TODO: Test both children are populated
- expect(helpers.populatedDataToJS(exampleState, `/${path}`, populates))
+ expect(helpers.populate(exampleState, `/${path}`, populates))
.to
.have
.deep
.property(`${valName}.owner.displayName`, exampleData.data[rootName].ABC.displayName)
- expect(helpers.populatedDataToJS(exampleState, `/${path}`, populates))
+ expect(helpers.populate(exampleState, `/${path}`, populates))
.to
.have
.deep
@@ -298,30 +305,6 @@ describe('Helpers:', () => {
})
- describe('customToJS', () => {
- it('exists', () => {
- expect(helpers).to.respondTo('customToJS')
- })
-
- it('handles non-immutable state', () => {
- expect(helpers.customToJS(exampleData, '/some', 'some'))
- .to
- .equal(exampleData)
- })
-
- it('passes notSetValue', () => {
- expect(helpers.customToJS(null, '/some', 'some', exampleData))
- .to
- .equal(exampleData)
- })
-
- it('passes custom data', () => {
- expect(helpers.customToJS(exampleState, '/some', 'snapshot'))
- .to
- .exist
- })
- })
-
describe('isLoaded', () => {
it('exists', () => {
expect(helpers).to.respondTo('isLoaded')
diff --git a/tests/unit/reducer.spec.js b/tests/unit/reducer.spec.js
index c32868712..c0fd9f30a 100644
--- a/tests/unit/reducer.spec.js
+++ b/tests/unit/reducer.spec.js
@@ -1,5 +1,3 @@
-/* global describe expect it */
-import { fromJS } from 'immutable'
import { firebaseStateReducer } from '../../src'
import { actionTypes } from '../../src/constants'
const emptyState = {
@@ -24,8 +22,8 @@ const noError = { authError: null }
const noAuth = { auth: null, profile: null }
const exampleData = { some: 'data' }
const externalState = { data: { asdfasdf: {} } }
-const exampleState = fromJS({})
-const exampleEmptyState = fromJS(emptyState)
+const exampleState = {}
+const exampleEmptyState = emptyState
describe('reducer', () => {
it('is a function', () => {
@@ -33,46 +31,77 @@ describe('reducer', () => {
})
it('handles no initialState', () => {
- expect(
- JSON.stringify(firebaseStateReducer(undefined, {}).toJS()))
- .to.equal(JSON.stringify(initialState))
+ expect(firebaseStateReducer(undefined, {})).to.equal(initialState)
})
it('returns state by default', () => {
- expect(firebaseStateReducer(exampleData))
- .to.equal(exampleData)
+ expect(firebaseStateReducer(exampleData)).to.equal(exampleData)
})
describe('SET action', () => {
it('deletes data from state when data is null', () => {
expect(
- firebaseStateReducer(
- exampleState,
+ firebaseStateReducer(exampleState,
{ type: actionTypes.SET, path: 'test' }
)
).to.equal(exampleState)
})
+
it('sets state', () => {
const path = 'test'
const pathArray = path.split(/\//).filter(p => !!p)
- console.log('path:', { type: actionTypes.SET, path, data: {} })
expect(
- JSON.stringify(firebaseStateReducer(
+ firebaseStateReducer(
exampleState,
{ type: actionTypes.SET, path, data: {} }
- ))
- ).to.equal(JSON.stringify(exampleState.setIn(['data', ...pathArray], fromJS({}))))
+ )
+ ).to.equal(exampleState.setIn(['data', ...pathArray], {}))
+ })
+ it('handles already existing parent that is null', () => {
+ const childPath = 123
+ const path = `test/${childPath}`
+ const pathArray = path.split(/\//).filter(p => !!p)
+ const newData = { some: 'val' }
+ expect(
+ firebaseStateReducer(
+ {data: { test: null } },
+ { type: actionTypes.SET, path, data: newData }
+ )
+ ).to.equal(exampleState.data,newData)
+ })
+
+ it('handles already existing value of null', () => {
+ const path = 'test/123'
+ const pathArray = path.split(/\//).filter(p => !!p)
+ const newData = { some: 'val' }
+ expect(
+ firebaseStateReducer(
+ { data: { test: { '123': null } } },
+ { type: actionTypes.SET, path, data: newData }
+ )
+ ).to.equal(exampleState.data[pathArray],newData)
})
})
describe('NO_VALUE action', () => {
it('sets state', () => {
expect(
- JSON.stringify(firebaseStateReducer(
+ firebaseStateReducer(
exampleState,
{ type: actionTypes.NO_VALUE, path: 'asdfasdf' }
- ).toJS())
- ).to.equal(JSON.stringify(externalState))
+ )
+ ).to.equal(externalState)
+ })
+ })
+
+ describe('UNSET_LISTENER action', () => {
+ it('sets state', () => {
+ expect(
+ firebaseStateReducer(
+ exampleState,
+ { type: actionTypes.UNSET_LISTENER, path: 'asdfasdf' }
+ )
+ ).to.equal({})
})
})
@@ -80,91 +109,87 @@ describe('reducer', () => {
it('sets state', () => {
const profile = { email: 'test@test.com' }
expect(
- JSON.stringify(firebaseStateReducer(
+ firebaseStateReducer(
exampleState,
{ type: actionTypes.SET_PROFILE, profile }
- ).toJS())
- ).to.equal(JSON.stringify({ profile }))
+ )
+ ).to.equal({ profile })
})
it('removes for no profile', () => {
const profile = { email: 'test@test.com' }
expect(
- JSON.stringify(firebaseStateReducer(
+ firebaseStateReducer(
exampleState,
{ type: actionTypes.SET_PROFILE }
- ).toJS())
- ).to.equal(JSON.stringify(exampleState.deleteIn(['profile'])))
+ )
+ ).to.equal(exampleState.deleteIn(['profile']))
})
})
describe('LOGOUT action', () => {
it('sets state', () => {
expect(
- JSON.stringify(firebaseStateReducer(
+ firebaseStateReducer(
exampleState,
{ type: actionTypes.LOGOUT }
- ).toJS())
- ).to.equal(JSON.stringify({
+ )
+ ).to.equal({
auth: null,
authError: null,
profile: null,
isInitializing: false,
data: {}
- }))
+ })
})
})
describe('LOGIN action', () => {
it('sets state', () => {
expect(
- JSON.stringify(firebaseStateReducer(
+ firebaseStateReducer(
exampleState,
{ type: actionTypes.LOGIN }
- ).toJS())
- ).to.equal(JSON.stringify(noError))
+ )
+ ).to.equal(noError)
})
})
describe('LOGIN_ERROR action', () => {
it('sets state', () => {
expect(
- JSON.stringify(firebaseStateReducer(
+ firebaseStateReducer(
exampleState,
{ type: actionTypes.LOGIN_ERROR }
- ).toJS())
- ).to.equal(JSON.stringify(noAuth))
+ )
+ ).to.equal(noAuth)
})
})
describe('AUTHENTICATION_INIT_STARTED action', () => {
it('sets state', () => {
expect(
- JSON.stringify(firebaseStateReducer(
+ firebaseStateReducer(
exampleState,
{ type: actionTypes.AUTHENTICATION_INIT_STARTED }
- ).toJS())
- ).to.equal(JSON.stringify({
+ )
+ ).to.equal({
isInitializing: true,
data: {},
timestamp: {},
requesting: {},
requested: {}
- }))
+ })
})
})
describe('AUTHENTICATION_INIT_FINISHED action', () => {
it('sets state', () => {
expect(
- JSON.stringify(firebaseStateReducer(
+ firebaseStateReducer(
exampleState,
{ type: actionTypes.AUTHENTICATION_INIT_FINISHED }
- ).toJS())
- ).to.equal(
- JSON.stringify(
- exampleState.setIn(['isInitializing'], false).toJS()
)
- )
+ ).to.equal(exampleState, false)
})
})
@@ -172,15 +197,23 @@ describe('reducer', () => {
it('sets state', () => {
const authError = {}
expect(
- JSON.stringify(firebaseStateReducer(
+ firebaseStateReducer(
exampleState,
{ type: actionTypes.UNAUTHORIZED_ERROR, authError }
- ).toJS())
- ).to.equal(
- JSON.stringify(
- exampleState.setIn(['authError'], authError).toJS()
)
- )
+ ).to.equal(exampleState, authError)
+ })
+ })
+
+ describe('AUTH_UPDATE_SUCCESS action', () => {
+ it('sets state', () => {
+ const authUpdate = { email: 'newEmail' }
+ expect(
+ firebaseStateReducer(
+ exampleState,
+ { type: actionTypes.AUTH_UPDATE_SUCCESS, payload: authUpdate }
+ )
+ ).to.equal(exampleState, authUpdate)
})
})
})
diff --git a/tests/unit/utils/auth.spec.js b/tests/unit/utils/auth.spec.js
index 95a90471c..17d65d905 100644
--- a/tests/unit/utils/auth.spec.js
+++ b/tests/unit/utils/auth.spec.js
@@ -1,4 +1,3 @@
-/* global firebase describe expect it */
import {
createAuthProvider,
getLoginMethodAndParams
@@ -15,6 +14,11 @@ describe('Utils: Auth', () => {
expect(createAuthProvider(firebase, 'google', 'email'))
.to.be.a.function
})
+ it('handles customAuthParameters config option', () => {
+ firebase._.config.customAuthParameters = { google: [{prompt: 'select_account'}] }
+ expect(createAuthProvider(firebase, 'google', 'email'))
+ .to.be.a.function
+ })
it('throws for invalid provider', () => {
const provider = 'asdf'
expect(() => createAuthProvider(firebase, provider, ['email']))
diff --git a/tests/unit/utils/populate.spec.js b/tests/unit/utils/populate.spec.js
index 68fb376e1..76faa8dd2 100644
--- a/tests/unit/utils/populate.spec.js
+++ b/tests/unit/utils/populate.spec.js
@@ -1,9 +1,9 @@
-/* global describe expect it beforeEach */
import {
getPopulateObj,
getPopulates,
getPopulateChild,
getPopulateObjs,
+ getChildType,
promisesForPopulate
} from '../../../src/utils/populate'
@@ -18,7 +18,22 @@ describe('Utils: Populate', () => {
})
})
- describe('getPopulateObj', () => {
+ describe('getChildType', () => {
+ it('returns "string" for strings', () => {
+ expect(getChildType('some:value')).to.equal('string')
+ })
+ it('returns "object" for objects', () => {
+ expect(getChildType({ some: 'val' })).to.equal('object')
+ })
+ it('returns "array" for arrays', () => {
+ expect(getChildType([])).to.equal('array')
+ })
+ it('returns "other" for other types', () => {
+ expect(getChildType(1)).to.equal('other')
+ })
+ })
+
+ describe('getPopulateObjs', () => {
it('returns object with child and root', () => {
expect(getPopulateObjs(['some:value'])[0]).to.have.keys('child', 'root')
})
@@ -47,14 +62,14 @@ describe('Utils: Populate', () => {
describe('promisesForPopulate', () => {
it('handles non-existant single child', () =>
- promisesForPopulate(Firebase, { uid: '123123' }, [{child: 'random', root: 'users'}])
+ promisesForPopulate(Firebase, '', { uid: '123123' }, [{child: 'random', root: 'users'}])
.then((v) => {
expect(JSON.stringify(v)).to.equal(JSON.stringify({}))
})
)
it('populates single property containing a single item', () =>
- promisesForPopulate(Firebase, { uid: '123' }, [{child: 'uid', root: 'users'}])
+ promisesForPopulate(Firebase, '', { uid: '123' }, [{child: 'uid', root: 'users'}])
.then((v) => {
expect(v).to.exist
expect(v).to.have.keys('users')
@@ -63,7 +78,7 @@ describe('Utils: Populate', () => {
)
it('populates single property containing a list', () =>
- promisesForPopulate(Firebase, { collaborators: { 'Iq5b0qK2NtgggT6U3bU6iZRGyma2': true, '123': true } }, [{child: 'collaborators', root: 'users'}])
+ promisesForPopulate(Firebase, '', { collaborators: { 'Iq5b0qK2NtgggT6U3bU6iZRGyma2': true, '123': true } }, [{child: 'collaborators', root: 'users'}])
.then((v) => {
expect(v).to.exist
expect(v).to.have.keys('users')
@@ -72,7 +87,7 @@ describe('Utils: Populate', () => {
)
it('populates list with single property populate', () =>
- promisesForPopulate(Firebase, { 1: { owner: 'Iq5b0qK2NtgggT6U3bU6iZRGyma2' } }, [{child: 'owner', root: 'users'}])
+ promisesForPopulate(Firebase, '', { 1: { owner: 'Iq5b0qK2NtgggT6U3bU6iZRGyma2' } }, [{child: 'owner', root: 'users'}])
.then((v) => {
expect(v).to.have.keys('users')
expect(v.users['Iq5b0qK2NtgggT6U3bU6iZRGyma2']).to.be.an.object
@@ -80,7 +95,7 @@ describe('Utils: Populate', () => {
)
it('populates list with property containing array property', () =>
- promisesForPopulate(Firebase, { 1: { collaborators: ['Iq5b0qK2NtgggT6U3bU6iZRGyma2', '123'] } }, [{child: 'collaborators', root: 'users'}])
+ promisesForPopulate(Firebase, '', { 1: { collaborators: ['Iq5b0qK2NtgggT6U3bU6iZRGyma2', '123'] } }, [{child: 'collaborators', root: 'users'}])
.then((v) => {
expect(v).to.exist
expect(v).to.have.keys('users')
@@ -89,7 +104,7 @@ describe('Utils: Populate', () => {
)
it('populates list with property containing firebase list', () =>
- promisesForPopulate(Firebase, { 1: { collaborators: { 'Iq5b0qK2NtgggT6U3bU6iZRGyma2': true, '123': true } } }, [{child: 'collaborators', root: 'users'}])
+ promisesForPopulate(Firebase, '', { 1: { collaborators: { 'Iq5b0qK2NtgggT6U3bU6iZRGyma2': true, '123': true } } }, [{child: 'collaborators', root: 'users'}])
.then((v) => {
expect(v).to.exist
expect(v).to.have.keys('users')
@@ -98,7 +113,7 @@ describe('Utils: Populate', () => {
)
it('populates list with property containing invalid child id', () =>
- promisesForPopulate(Firebase, { 1: { collaborators: ['1111', '123'] } }, [{child: 'collaborators', root: 'users'}])
+ promisesForPopulate(Firebase, '', { 1: { collaborators: ['1111', '123'] } }, [{child: 'collaborators', root: 'users'}])
.then((v) => {
expect(v).to.exist
expect(v.users).to.have.keys('123') // sets valid child
diff --git a/tests/unit/utils/query.spec.js b/tests/unit/utils/query.spec.js
index caacfe848..4ea1c9714 100644
--- a/tests/unit/utils/query.spec.js
+++ b/tests/unit/utils/query.spec.js
@@ -1,4 +1,3 @@
-/* global describe expect it beforeEach */
import {
getWatchPath,
setWatcher,
@@ -89,11 +88,30 @@ describe('Utils: Query', () => {
'value:/todos': 1,
'value:/todo': 2
}
+ spy = sinon.spy(console, 'warn')
+
+ })
+ afterEach(() => {
+ console.warn.restore()
})
it('removes single watcher', () => {
unsetWatcher(Firebase, dispatch, 'value', '/todos')
expect(Firebase._.watchers['value:/todos']).to.be.undefined
})
+ it('handes dispatch on unset listener config', () => {
+ Firebase._.config.dispatchOnUnsetListener = true
+ unsetWatcher(Firebase, dispatch, 'value', '/todos')
+ expect(Firebase._.watchers['value:/todos']).to.be.undefined
+ })
+
+ it('warns for deprecated method name', () => {
+ Firebase._.config.distpatchOnUnsetListener = true
+ // TODO: confirm that console.warn is called with correct message
+ unsetWatcher(Firebase, dispatch, 'value', '/todos')
+ expect(Firebase._.watchers['value:/todos']).to.be.undefined
+ expect(spy).to.have.been.calledWith('config.distpatchOnUnsetListener is Depreceated and will be removed in future versions. Please use config.dispatchOnUnsetListener (dispatch spelled correctly).')
+ })
+
it('decrements existings watcher count', () => {
unsetWatcher(Firebase, dispatch, 'value', '/todo')
expect(Firebase._.watchers['value:/todos']).to.equal(1)
@@ -198,6 +216,17 @@ describe('Utils: Query', () => {
.to
.equal(equalTo)
})
+ it('does not parse if notParsed parameter passed', () => {
+ const child = 'emailAddress'
+ const equalTo = '123'
+ const queryParams = createQueryFromParams([`orderByChild=${child}`, 'notParsed', `equalTo=${equalTo}`])
+ expect(queryParams.child)
+ .to
+ .equal(child)
+ expect(queryParams.equalTo)
+ .to
+ .equal(equalTo)
+ })
})
})
diff --git a/tests/unit/utils/storage.spec.js b/tests/unit/utils/storage.spec.js
index 11c07f525..4d4fbf31c 100644
--- a/tests/unit/utils/storage.spec.js
+++ b/tests/unit/utils/storage.spec.js
@@ -1,4 +1,3 @@
-/* global describe expect it */
import { deleteFile } from '../../../src/utils/storage'
const fakeFirebase = {
_: {
@@ -26,8 +25,11 @@ const fakeFirebase = {
}
describe('Utils: Storage', () => {
describe('deleteFile', () => {
- it('returns dbPath', () => {
- expect(deleteFile(fakeFirebase, { path: 'some', dbPath: 'some' })).to.eventually.have.keys('dbPath')
- })
+ it('returns dbPath', () =>
+ expect(deleteFile(fakeFirebase, { path: 'some', dbPath: 'some' })).to.eventually.have.keys(['path', 'dbPath'])
+ )
+ it('returns dbPath', () =>
+ expect(deleteFile(fakeFirebase, { path: 'some' })).to.eventually.have.keys('path')
+ )
})
})
diff --git a/webpack.config.js b/webpack.config.js
index b768a844f..632339bcc 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -7,7 +7,7 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl
const config = {
module: {
- loaders: [
+ rules: [
{ test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/ }
]
},
@@ -23,10 +23,7 @@ const config = {
root: 'React'
}
},
- plugins: [
- new webpack.optimize.OccurrenceOrderPlugin(),
- new webpack.optimize.DedupePlugin()
- ]
+ plugins: []
}
if (env === 'production') {
@@ -55,8 +52,11 @@ config.plugins.push(
'process.env.NODE_ENV': JSON.stringify(env)
}),
new webpack.BannerPlugin(
- `${pkg.name}${env === 'production' ? '.min' : ''}.js v${pkg.version}`,
- { raw: false, entryOnly: true }
+ {
+ banner: `${pkg.name}${env === 'production' ? '.min' : ''}.js v${pkg.version}`,
+ raw: false,
+ entryOnly: true
+ }
)
)
diff --git a/yarn.lock b/yarn.lock
index 3674b9991..1d3588307 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -24,6 +24,12 @@ accepts@~1.3.3:
mime-types "~2.1.11"
negotiator "0.6.1"
+acorn-dynamic-import@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4"
+ dependencies:
+ acorn "^4.0.3"
+
acorn-globals@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
@@ -36,7 +42,7 @@ acorn-jsx@^3.0.0:
dependencies:
acorn "^3.0.4"
-acorn@^3.0.0, acorn@^3.0.4:
+acorn@^3.0.4:
version "3.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
@@ -44,11 +50,11 @@ acorn@^4.0.11, acorn@^4.0.3, acorn@^4.0.4:
version "4.0.11"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0"
-acorn@^5.0.1:
+acorn@^5.0.0, acorn@^5.0.1:
version "5.0.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d"
-ajv-keywords@^1.0.0:
+ajv-keywords@^1.0.0, ajv-keywords@^1.1.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
@@ -99,6 +105,10 @@ ansistyles@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/ansistyles/-/ansistyles-0.1.3.tgz#5de60415bda071bb37127854c864f41b23254539"
+any-promise@1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
+
anymatch@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507"
@@ -197,6 +207,14 @@ asap@^2.0.0, asap@~2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f"
+asn1.js@^4.0.0:
+ version "4.9.1"
+ resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40"
+ dependencies:
+ bn.js "^4.0.0"
+ inherits "^2.0.1"
+ minimalistic-assert "^1.0.0"
+
asn1@~0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
@@ -229,24 +247,16 @@ async-some@~1.0.2:
dependencies:
dezalgo "^1.0.2"
-async@1.x, async@^1.3.0, async@^1.4.0:
+async@1.x, async@^1.4.0:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
-async@^0.9.0:
- version "0.9.2"
- resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
-
-async@^2.0.1, async@^2.1.4:
+async@^2.0.1, async@^2.1.2, async@^2.1.4:
version "2.3.0"
resolved "https://registry.yarnpkg.com/async/-/async-2.3.0.tgz#1013d1051047dd320fe24e494d5c66ecaf6147d9"
dependencies:
lodash "^4.14.0"
-async@~0.2.6:
- version "0.2.10"
- resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
-
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -1081,6 +1091,10 @@ block-stream@*, block-stream@0.0.9:
dependencies:
inherits "~2.0.0"
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
+ version "4.11.6"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
+
body-parser@~1.14.0:
version "1.14.2"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.14.2.tgz#1015cb1fe2c443858259581db53332f8d0cf50f9"
@@ -1117,6 +1131,10 @@ braces@^1.8.2:
preserve "^0.2.0"
repeat-element "^1.1.2"
+brorand@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+
browser-resolve@^1.7.0:
version "1.11.2"
resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce"
@@ -1127,12 +1145,51 @@ browser-stdout@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
-browserify-aes@0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-0.4.0.tgz#067149b668df31c4b58533e02d01e806d8608e2c"
+browserify-aes@^1.0.0, browserify-aes@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a"
+ dependencies:
+ buffer-xor "^1.0.2"
+ cipher-base "^1.0.0"
+ create-hash "^1.1.0"
+ evp_bytestokey "^1.0.0"
+ inherits "^2.0.1"
+
+browserify-cipher@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a"
+ dependencies:
+ browserify-aes "^1.0.4"
+ browserify-des "^1.0.0"
+ evp_bytestokey "^1.0.0"
+
+browserify-des@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd"
dependencies:
+ cipher-base "^1.0.1"
+ des.js "^1.0.0"
inherits "^2.0.1"
+browserify-rsa@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
+ dependencies:
+ bn.js "^4.1.0"
+ randombytes "^2.0.1"
+
+browserify-sign@^4.0.0:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
+ dependencies:
+ bn.js "^4.1.1"
+ browserify-rsa "^4.0.0"
+ create-hash "^1.1.0"
+ create-hmac "^1.1.2"
+ elliptic "^6.0.0"
+ inherits "^2.0.1"
+ parse-asn1 "^5.0.0"
+
browserify-zlib@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d"
@@ -1147,7 +1204,11 @@ buffer-shims@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51"
-buffer@^4.9.0:
+buffer-xor@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
+
+buffer@^4.3.0:
version "4.9.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
dependencies:
@@ -1272,7 +1333,7 @@ chmodr@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/chmodr/-/chmodr-1.0.2.tgz#04662b932d0f02ec66deaa2b0ea42811968e3eb9"
-chokidar@^1.0.0, chokidar@^1.0.5, chokidar@^1.2.0, chokidar@^1.6.1:
+chokidar@^1.0.5, chokidar@^1.2.0, chokidar@^1.4.3, chokidar@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2"
dependencies:
@@ -1291,6 +1352,12 @@ chownr@^1.0.1, chownr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
+cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07"
+ dependencies:
+ inherits "^2.0.1"
+
circular-json@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d"
@@ -1301,10 +1368,23 @@ cli-cursor@^1.0.1, cli-cursor@^1.0.2:
dependencies:
restore-cursor "^1.0.1"
+cli-table@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
+ dependencies:
+ colors "1.0.3"
+
cli-width@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
+cli@0.11.3:
+ version "0.11.3"
+ resolved "https://registry.yarnpkg.com/cli/-/cli-0.11.3.tgz#7b0cd3de990e452925667c0dbaffdc9f7f2a9a15"
+ dependencies:
+ exit "0.1.2"
+ glob "^7.0.5"
+
cliui@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
@@ -1360,6 +1440,14 @@ collapse-white-space@^1.0.0, collapse-white-space@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.2.tgz#9c463fb9c6d190d2dcae21a356a01bcae9eeef6d"
+colors@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
+
+colors@^1.0.3:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
+
columnify@~1.5.4:
version "1.5.4"
resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb"
@@ -1464,6 +1552,33 @@ core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+create-ecdh@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d"
+ dependencies:
+ bn.js "^4.1.0"
+ elliptic "^6.0.0"
+
+create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd"
+ dependencies:
+ cipher-base "^1.0.1"
+ inherits "^2.0.1"
+ ripemd160 "^2.0.0"
+ sha.js "^2.4.0"
+
+create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06"
+ dependencies:
+ cipher-base "^1.0.3"
+ create-hash "^1.1.0"
+ inherits "^2.0.1"
+ ripemd160 "^2.0.0"
+ safe-buffer "^5.0.1"
+ sha.js "^2.4.8"
+
cross-env@^1.0.7:
version "1.0.8"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-1.0.8.tgz#2bde748efc780f56ddf07ea69fcad875357774ce"
@@ -1484,14 +1599,20 @@ cryptiles@2.x.x:
dependencies:
boom "2.x.x"
-crypto-browserify@3.3.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.3.0.tgz#b9fc75bb4a0ed61dcf1cd5dae96eb30c9c3e506c"
+crypto-browserify@^3.11.0:
+ version "3.11.0"
+ resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522"
dependencies:
- browserify-aes "0.4.0"
- pbkdf2-compat "2.0.1"
- ripemd160 "0.2.0"
- sha.js "2.2.6"
+ browserify-cipher "^1.0.0"
+ browserify-sign "^4.0.0"
+ create-ecdh "^4.0.0"
+ create-hash "^1.1.0"
+ create-hmac "^1.1.0"
+ diffie-hellman "^5.0.0"
+ inherits "^2.0.1"
+ pbkdf2 "^3.0.3"
+ public-encrypt "^4.0.0"
+ randombytes "^2.0.0"
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.2"
@@ -1616,6 +1737,13 @@ depd@1.1.0, depd@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3"
+des.js@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
+ dependencies:
+ inherits "^2.0.1"
+ minimalistic-assert "^1.0.0"
+
destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
@@ -1654,6 +1782,14 @@ diff@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
+diffie-hellman@^5.0.0:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e"
+ dependencies:
+ bn.js "^4.1.0"
+ miller-rabin "^4.0.0"
+ randombytes "^2.0.0"
+
disparity@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/disparity/-/disparity-2.0.0.tgz#57ddacb47324ae5f58d2cc0da886db4ce9eeb718"
@@ -1752,7 +1888,7 @@ documentation@^4.0.0-beta15:
vinyl-fs "^2.3.1"
yargs "^4.3.1"
-dom-storage@^2.0.2:
+dom-storage@2.0.2, dom-storage@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/dom-storage/-/dom-storage-2.0.2.tgz#ed17cbf68abd10e0aef8182713e297c5e4b500b0"
@@ -1808,6 +1944,18 @@ elegant-spinner@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
+elliptic@^6.0.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
+ dependencies:
+ bn.js "^4.4.0"
+ brorand "^1.0.1"
+ hash.js "^1.0.0"
+ hmac-drbg "^1.0.0"
+ inherits "^2.0.1"
+ minimalistic-assert "^1.0.0"
+ minimalistic-crypto-utils "^1.0.0"
+
emoji-regex@^6.0.0:
version "6.4.1"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.4.1.tgz#77486fe9cd45421d260a6238b88d721e2fad2050"
@@ -1832,13 +1980,14 @@ end-of-stream@1.0.0:
dependencies:
once "~1.3.0"
-enhanced-resolve@~0.9.0:
- version "0.9.1"
- resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e"
+enhanced-resolve@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.1.0.tgz#9f4b626f577245edcf4b2ad83d86e17f4f421dec"
dependencies:
graceful-fs "^4.1.2"
- memory-fs "^0.2.0"
- tapable "^0.1.8"
+ memory-fs "^0.4.0"
+ object-assign "^4.0.1"
+ tapable "^0.2.5"
errno@^0.1.3:
version "0.1.4"
@@ -2076,6 +2225,10 @@ espree@^3.4.0:
acorn "^5.0.1"
acorn-jsx "^3.0.0"
+esprima@^1.2.2:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.5.tgz#0993502feaf668138325756f30f9a51feeec11e9"
+
esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
@@ -2128,10 +2281,20 @@ events@^1.0.0, events@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
+evp_bytestokey@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53"
+ dependencies:
+ create-hash "^1.1.1"
+
exit-hook@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
+exit@0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
+
expand-brackets@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
@@ -2213,7 +2376,7 @@ faye-websocket@~0.10.0:
dependencies:
websocket-driver ">=0.5.1"
-fbjs@^0.8.1, fbjs@^0.8.4:
+fbjs@^0.8.1, fbjs@^0.8.4, fbjs@^0.8.9:
version "0.8.12"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.12.tgz#10b5d92f76d45575fd63a217d4ea02bea2f8ed04"
dependencies:
@@ -2291,6 +2454,29 @@ find-up@^1.0.0:
path-exists "^2.0.0"
pinkie-promise "^2.0.0"
+firebase-server@^0.10.1:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/firebase-server/-/firebase-server-0.10.1.tgz#60f1c89c60adc5acb140c13a06229d4ebfc20700"
+ dependencies:
+ any-promise "1.3.0"
+ cli "0.11.3"
+ debug "2.2.0"
+ firebase "3.6.1"
+ jwt-simple "0.3.1"
+ lodash "3.10.1"
+ targaryen "2.3.3"
+ ws "1.1.1"
+
+firebase@3.6.1:
+ version "3.6.1"
+ resolved "https://registry.yarnpkg.com/firebase/-/firebase-3.6.1.tgz#bcd7fe28f9eb75c8ecbefba9b4763b0800995229"
+ dependencies:
+ dom-storage "2.0.2"
+ faye-websocket "0.9.3"
+ jsonwebtoken "5.7.0"
+ rsvp "3.2.1"
+ xmlhttprequest "1.8.0"
+
firebase@^3.9.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/firebase/-/firebase-3.9.0.tgz#c4237f50f58eeb25081b1839d6cbf175f8f7ed9b"
@@ -2732,6 +2918,18 @@ has@^1.0.1:
dependencies:
function-bind "^1.0.2"
+hash-base@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1"
+ dependencies:
+ inherits "^2.0.1"
+
+hash.js@^1.0.0, hash.js@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573"
+ dependencies:
+ inherits "^2.0.1"
+
hawk@~3.1.0, hawk@~3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
@@ -2745,6 +2943,14 @@ highlight.js@^9.1.0, highlight.js@^9.6.0:
version "9.10.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.10.0.tgz#f9f0b14c0be00f0e4fb1e577b749fed9e6f52f55"
+hmac-drbg@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+ dependencies:
+ hash.js "^1.0.3"
+ minimalistic-assert "^1.0.0"
+ minimalistic-crypto-utils "^1.0.1"
+
hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
@@ -2876,10 +3082,6 @@ inquirer@^0.12.0:
strip-ansi "^3.0.0"
through "^2.3.6"
-interpret@^0.6.4:
- version "0.6.6"
- resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b"
-
interpret@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.2.tgz#f4f623f0bb7122f15f5717c8e254b8161b5c5b2d"
@@ -3289,6 +3491,10 @@ jsesc@~0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+json-loader@^0.5.4:
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de"
+
json-parse-helpfulerror@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz#13f14ce02eed4e981297b64eb9e3b932e2dd13dc"
@@ -3313,7 +3519,7 @@ json3@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
-json5@^0.5.0:
+json5@^0.5.0, json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
@@ -3335,6 +3541,14 @@ jsonpointer@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
+jsonwebtoken@5.7.0:
+ version "5.7.0"
+ resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-5.7.0.tgz#1c90f9a86ce5b748f5f979c12b70402b4afcddb4"
+ dependencies:
+ jws "^3.0.0"
+ ms "^0.7.1"
+ xtend "^4.0.1"
+
jsonwebtoken@^7.3.0:
version "7.4.0"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-7.4.0.tgz#515bf2bba070ec615bad97fd2e945027eb476946"
@@ -3369,7 +3583,7 @@ jwa@^1.1.4:
ecdsa-sig-formatter "1.0.9"
safe-buffer "^5.0.1"
-jws@^3.1.4:
+jws@^3.0.0, jws@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.4.tgz#f9e8b9338e8a847277d6444b1464f61880e050a2"
dependencies:
@@ -3381,6 +3595,10 @@ jwt-decode@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79"
+jwt-simple@0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/jwt-simple/-/jwt-simple-0.3.1.tgz#86e0b121d149534423dbd8044a727e3cf1eb939e"
+
kind-of@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47"
@@ -3437,7 +3655,11 @@ load-plugin@^2.0.0:
npm-prefix "^1.2.0"
resolve-from "^2.0.0"
-loader-utils@^0.2.11, loader-utils@^0.2.16:
+loader-runner@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
+
+loader-utils@^0.2.16:
version "0.2.17"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
dependencies:
@@ -3587,6 +3809,10 @@ lodash.keys@~4.0.3:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.0.8.tgz#c0cf45d2fcf576c83055404d674c7e637c83ae81"
+lodash.mergewith@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
+
lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
@@ -3632,6 +3858,10 @@ lodash.without@~4.1.0:
lodash._basedifference "~4.4.0"
lodash.rest "^4.0.0"
+lodash@3.10.1:
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
+
lodash@4.5.1:
version "4.5.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.5.1.tgz#80e8a074ca5f3893a6b1c10b2a636492d710c316"
@@ -3669,7 +3899,7 @@ longest@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
-loose-envify@^1.0.0, loose-envify@^1.1.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
dependencies:
@@ -3731,13 +3961,9 @@ media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
-memory-fs@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290"
-
-memory-fs@~0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20"
+memory-fs@^0.4.0, memory-fs@~0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
dependencies:
errno "^0.1.3"
readable-stream "^2.0.1"
@@ -3774,6 +4000,13 @@ micromatch@^2.1.5, micromatch@^2.1.6, micromatch@^2.3.7:
parse-glob "^3.0.4"
regex-cache "^0.4.2"
+miller-rabin@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d"
+ dependencies:
+ bn.js "^4.0.0"
+ brorand "^1.0.1"
+
mime-db@~1.27.0:
version "1.27.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1"
@@ -3788,6 +4021,14 @@ mime@1.3.4, mime@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
+minimalistic-assert@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
+
+minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+
minimatch@1:
version "1.0.0"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-1.0.0.tgz#e0dd2120b49e1b724ce8d714c520822a9438576d"
@@ -3932,16 +4173,16 @@ node-gyp@~3.6.0:
tar "^2.0.0"
which "1"
-node-libs-browser@^0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-0.7.0.tgz#3e272c0819e308935e26674408d7af0e1491b83b"
+node-libs-browser@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646"
dependencies:
assert "^1.1.1"
browserify-zlib "^0.1.4"
- buffer "^4.9.0"
+ buffer "^4.3.0"
console-browserify "^1.1.0"
constants-browserify "^1.0.0"
- crypto-browserify "3.3.0"
+ crypto-browserify "^3.11.0"
domain-browser "^1.1.1"
events "^1.0.0"
https-browserify "0.0.1"
@@ -4322,7 +4563,7 @@ opener@^1.4.2, opener@~1.4.1:
version "1.4.3"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
-optimist@0.6.1, optimist@^0.6.1, optimist@~0.6.0:
+optimist@0.6.1, optimist@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
dependencies:
@@ -4340,6 +4581,10 @@ optionator@^0.8.1, optionator@^0.8.2:
type-check "~0.3.2"
wordwrap "~1.0.0"
+options@>=0.0.5:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f"
+
ordered-read-streams@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz#7137e69b3298bb342247a1bbee3881c80e2fd78b"
@@ -4390,6 +4635,16 @@ parents@^1.0.0:
dependencies:
path-platform "~0.11.15"
+parse-asn1@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712"
+ dependencies:
+ asn1.js "^4.0.0"
+ browserify-aes "^1.0.0"
+ create-hash "^1.1.0"
+ evp_bytestokey "^1.0.0"
+ pbkdf2 "^3.0.3"
+
parse-entities@^1.0.0, parse-entities@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.1.0.tgz#4bc58f35fdc8e65dded35a12f2e40223ca24a3f7"
@@ -4499,9 +4754,15 @@ path-type@^1.0.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
-pbkdf2-compat@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz#b6e0c8fa99494d94e0511575802a59a5c142f288"
+pbkdf2@^3.0.3:
+ version "3.0.12"
+ resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.12.tgz#be36785c5067ea48d806ff923288c5f750b6b8a2"
+ dependencies:
+ create-hash "^1.1.2"
+ create-hmac "^1.1.4"
+ ripemd160 "^2.0.1"
+ safe-buffer "^5.0.1"
+ sha.js "^2.4.8"
performance-now@^0.2.0:
version "0.2.0"
@@ -4583,6 +4844,13 @@ promzard@^0.3.0:
dependencies:
read "1"
+prop-types@^15.5.8:
+ version "15.5.10"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
+ dependencies:
+ fbjs "^0.8.9"
+ loose-envify "^1.3.1"
+
proto-list@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
@@ -4606,6 +4874,16 @@ pseudomap@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+public-encrypt@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6"
+ dependencies:
+ bn.js "^4.1.0"
+ browserify-rsa "^4.0.0"
+ create-hash "^1.1.0"
+ parse-asn1 "^5.0.0"
+ randombytes "^2.0.1"
+
punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
@@ -4657,6 +4935,10 @@ randomatic@^1.1.3:
is-number "^2.0.2"
kind-of "^3.0.2"
+randombytes@^2.0.0, randombytes@^2.0.1:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec"
+
range-parser@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
@@ -5039,6 +5321,10 @@ replace-ext@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb"
+replaceall@^0.1.3:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/replaceall/-/replaceall-0.1.6.tgz#81d81ac7aeb72d7f5c4942adf2697a3220688d8e"
+
request@2, request@2.79.0, request@^2.47.0, request@^2.79.0:
version "2.79.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
@@ -5213,9 +5499,16 @@ rimraf@~2.5.2, rimraf@~2.5.4:
dependencies:
glob "^7.0.5"
-ripemd160@0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce"
+ripemd160@^2.0.0, ripemd160@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7"
+ dependencies:
+ hash-base "^2.0.0"
+ inherits "^2.0.1"
+
+rsvp@3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.2.1.tgz#07cb4a5df25add9e826ebc67dcc9fd89db27d84a"
run-async@^0.1.0:
version "0.1.0"
@@ -5227,7 +5520,7 @@ rx-lite@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
-safe-buffer@^5.0.1:
+safe-buffer@^5.0.1, safe-buffer@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
@@ -5294,9 +5587,11 @@ setprototypeof@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
-sha.js@2.2.6:
- version "2.2.6"
- resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.2.6.tgz#17ddeddc5f722fb66501658895461977867315ba"
+sha.js@^2.4.0, sha.js@^2.4.8:
+ version "2.4.8"
+ resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f"
+ dependencies:
+ inherits "^2.0.1"
sha@~2.0.1:
version "2.0.1"
@@ -5368,9 +5663,9 @@ sorted-object@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/sorted-object/-/sorted-object-2.0.1.tgz#7d631f4bd3a798a24af1dffcfbfe83337a5df5fc"
-source-list-map@~0.1.7:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
+source-list-map@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1"
source-map-support@^0.4.2:
version "0.4.14"
@@ -5378,13 +5673,13 @@ source-map-support@^0.4.2:
dependencies:
source-map "^0.5.6"
-source-map@^0.4.4, source-map@~0.4.1:
+source-map@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
dependencies:
amdefine ">=0.0.4"
-source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1:
+source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
@@ -5469,7 +5764,7 @@ stream-shift@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
-string-width@^1.0.0, string-width@^1.0.1:
+string-width@^1.0.0, string-width@^1.0.1, string-width@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
dependencies:
@@ -5525,7 +5820,7 @@ strip-bom@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
-strip-json-comments@^2.0.0, strip-json-comments@~2.0.1:
+strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -5564,9 +5859,9 @@ table@^3.7.8:
slice-ansi "0.0.4"
string-width "^2.0.0"
-tapable@^0.1.8, tapable@~0.1.8:
- version "0.1.10"
- resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4"
+tapable@^0.2.5, tapable@~0.2.5:
+ version "0.2.6"
+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d"
tar-pack@^3.4.0:
version "3.4.0"
@@ -5589,6 +5884,18 @@ tar@^2.0.0, tar@^2.2.1, tar@~2.2.1:
fstream "^1.0.2"
inherits "2"
+targaryen@2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/targaryen/-/targaryen-2.3.3.tgz#1014f2cc66f94c0f7a19233126b5f2168e25db2f"
+ dependencies:
+ cli-table "^0.3.1"
+ colors "^1.0.3"
+ esprima "^1.2.2"
+ lodash.mergewith "^4.6.0"
+ minimist "^1.1.0"
+ replaceall "^0.1.3"
+ strip-json-comments "^2.0.1"
+
text-encoding@0.6.4:
version "0.6.4"
resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
@@ -5760,14 +6067,14 @@ ua-parser-js@^0.7.9:
version "0.7.12"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"
-uglify-js@^2.6, uglify-js@~2.7.3:
- version "2.7.5"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8"
+uglify-js@^2.6, uglify-js@^2.8.27:
+ version "2.8.27"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.27.tgz#47787f912b0f242e5b984343be8e35e95f694c9c"
dependencies:
- async "~0.2.6"
source-map "~0.5.1"
- uglify-to-browserify "~1.0.0"
yargs "~3.10.0"
+ optionalDependencies:
+ uglify-to-browserify "~1.0.0"
uglify-to-browserify@~1.0.0:
version "1.0.2"
@@ -5777,6 +6084,14 @@ uid-number@0.0.6, uid-number@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
+ultron@1.0.x:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa"
+
+ultron@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864"
+
umask@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d"
@@ -6060,12 +6375,12 @@ ware@^1.3.0:
dependencies:
wrap-fn "^0.1.0"
-watchpack@^0.2.1:
- version "0.2.9"
- resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-0.2.9.tgz#62eaa4ab5e5ba35fdfc018275626e3c0f5e3fb0b"
+watchpack@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.3.1.tgz#7d8693907b28ce6013e7f3610aa2a1acf07dad87"
dependencies:
- async "^0.9.0"
- chokidar "^1.0.0"
+ async "^2.1.2"
+ chokidar "^1.4.3"
graceful-fs "^4.1.2"
wcwidth@^1.0.0:
@@ -6097,32 +6412,38 @@ webpack-bundle-analyzer@^2.3.1:
mkdirp "^0.5.1"
opener "^1.4.2"
-webpack-core@~0.6.9:
- version "0.6.9"
- resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2"
+webpack-sources@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.2.3.tgz#17c62bfaf13c707f9d02c479e0dcdde8380697fb"
dependencies:
- source-list-map "~0.1.7"
- source-map "~0.4.1"
+ source-list-map "^1.1.1"
+ source-map "~0.5.3"
-webpack@^1.14.0:
- version "1.14.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-1.14.0.tgz#54f1ffb92051a328a5b2057d6ae33c289462c823"
+webpack@^2.6.1:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.6.1.tgz#2e0457f0abb1ac5df3ab106c69c672f236785f07"
dependencies:
- acorn "^3.0.0"
- async "^1.3.0"
- clone "^1.0.2"
- enhanced-resolve "~0.9.0"
- interpret "^0.6.4"
- loader-utils "^0.2.11"
- memory-fs "~0.3.0"
+ acorn "^5.0.0"
+ acorn-dynamic-import "^2.0.0"
+ ajv "^4.7.0"
+ ajv-keywords "^1.1.1"
+ async "^2.1.2"
+ enhanced-resolve "^3.0.0"
+ interpret "^1.0.0"
+ json-loader "^0.5.4"
+ json5 "^0.5.1"
+ loader-runner "^2.3.0"
+ loader-utils "^0.2.16"
+ memory-fs "~0.4.1"
mkdirp "~0.5.0"
- node-libs-browser "^0.7.0"
- optimist "~0.6.0"
+ node-libs-browser "^2.0.0"
+ source-map "^0.5.3"
supports-color "^3.1.0"
- tapable "~0.1.8"
- uglify-js "~2.7.3"
- watchpack "^0.2.1"
- webpack-core "~0.6.9"
+ tapable "~0.2.5"
+ uglify-js "^2.8.27"
+ watchpack "^1.3.1"
+ webpack-sources "^0.2.3"
+ yargs "^6.0.0"
websocket-driver@>=0.5.1:
version "0.6.5"
@@ -6218,6 +6539,20 @@ write@^0.2.1:
dependencies:
mkdirp "^0.5.1"
+ws@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.1.tgz#082ddb6c641e85d4bb451f03d52f06eabdb1f018"
+ dependencies:
+ options ">=0.0.5"
+ ultron "1.0.x"
+
+ws@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-3.0.0.tgz#98ddb00056c8390cb751e7788788497f99103b6c"
+ dependencies:
+ safe-buffer "~5.0.1"
+ ultron "~1.1.0"
+
x-is-string@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82"
@@ -6226,7 +6561,7 @@ xml-name-validator@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
-xmlhttprequest@^1.8.0:
+xmlhttprequest@1.8.0, xmlhttprequest@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
@@ -6249,6 +6584,12 @@ yargs-parser@^2.4.1:
camelcase "^3.0.0"
lodash.assign "^4.0.6"
+yargs-parser@^4.2.0:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c"
+ dependencies:
+ camelcase "^3.0.0"
+
yargs@^4.3.1:
version "4.8.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0"
@@ -6268,6 +6609,24 @@ yargs@^4.3.1:
y18n "^3.2.1"
yargs-parser "^2.4.1"
+yargs@^6.0.0:
+ version "6.6.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
+ dependencies:
+ camelcase "^3.0.0"
+ cliui "^3.2.0"
+ decamelize "^1.1.1"
+ get-caller-file "^1.0.1"
+ os-locale "^1.4.0"
+ read-pkg-up "^1.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^1.0.1"
+ set-blocking "^2.0.0"
+ string-width "^1.0.2"
+ which-module "^1.0.0"
+ y18n "^3.2.1"
+ yargs-parser "^4.2.0"
+
yargs@~3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"