diff --git a/.eslintignore b/.eslintignore index d1a2d76c8..ef1b0687f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ examples/** coverage/** node_modules/** -*.spec.js diff --git a/.eslintrc b/.eslintrc index a88694083..33193bbfa 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,13 +15,6 @@ ecmaFeatures: jsx: true modules: true -globals: - MockFirebase: true - sinon: true - Raven: true - __COVERAGE__: true - __DEV__: true - rules: semi: [2, 'never'] no-console: 'error' diff --git a/README.md b/README.md index 2a19daa13..1fac0c7ac 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ npm install --save react-redux-firebase ## Use -Include reduxFirebase in your store compose function: +Include reactReduxFirebase in your store compose function: ```javascript diff --git a/docs/api/compose.md b/docs/api/compose.md index 2239d0bbf..e8d79552f 100644 --- a/docs/api/compose.md +++ b/docs/api/compose.md @@ -54,6 +54,7 @@ _Setup_ ```javascript import { createStore, compose } from 'redux' import { reactReduxFirebase } from 'react-redux-firebase' +import * as firebase from 'firebase' // React Redux Firebase Config const config = { @@ -61,30 +62,20 @@ const config = { // here is where you place other config options } +// initialize script from Firebase page +const fbConfg = {} // firebase config object +firebase.initializeApp(fbConfig) + // Add react-redux-firebase to compose // Note: In full projects this will often be within createStore.js or store.js const createStoreWithFirebase = compose( - reactReduxFirebase(fbConfig, config), + reactReduxFirebase(firebase, config), )(createStore) // Use Function later to create store const store = createStoreWithFirebase(rootReducer, initialState) ``` -_Custom Auth Parameters_ - -```javascript -// Follow Setup example with the following config: -const config = { - customAuthParameters: { - google: { - // prompts user to select account on every google login - prompt: 'select_account' - } - } -} -``` - Returns **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** That accepts a component a returns a wrapped version of component # getFirebase diff --git a/docs/api/constants.md b/docs/api/constants.md index b21924463..f76a777b6 100644 --- a/docs/api/constants.md +++ b/docs/api/constants.md @@ -17,16 +17,18 @@ Object containing all action types - `START` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/START` - `SET` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/SET` -- `SET_ORDERED` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/SET_ORDERED` - `SET_PROFILE` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/SET_PROFILE` - `LOGIN` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/LOGIN` - `LOGOUT` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/LOGOUT` - `LOGIN_ERROR` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/LOGIN_ERROR` - `NO_VALUE` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/NO_VALUE` - `UNAUTHORIZED_ERROR` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/UNAUTHORIZED_ERROR` +- `SET_LISTENER` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/SET_LISTENER` - `UNSET_LISTENER` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/UNSET_LISTENER` - `AUTHENTICATION_INIT_STARTED` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/AUTHENTICATION_INIT_STARTED` - `AUTHENTICATION_INIT_FINISHED` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/AUTHENTICATION_INIT_FINISHED` +- `SESSION_START` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/SESSION_START` +- `SESSION_END` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/SESSION_END` - `FILE_UPLOAD_START` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/FILE_UPLOAD_START` - `FILE_UPLOAD_ERROR` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/FILE_UPLOAD_ERROR` - `FILE_UPLOAD_PROGRESS` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `@@reactReduxFirebase/FILE_UPLOAD_PROGRESS` @@ -59,6 +61,11 @@ Default configuration options - `userProfile` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `null` Location on Firebase where user profiles are stored. Often set to `'users'`. +- `presence` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `null` Location on Firebase where of currently + online users is stored. Often set to `'presence'` or `'onlineUsers'`. +- `sessions` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** `sessions` Location on Firebase where user + sessions are stored (only if presense is set). Often set to `'presence'` or + `'onlineUsers'`. - `enableLogging` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** `false` Whether or not firebase database logging is enabled. - `updateProfileOnLogin` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** `true` Whether or not to update @@ -71,7 +78,7 @@ Default configuration options 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. -- `autoPopulateProfile` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** `true` Whether or not to +- `autoPopulateProfile` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** `false` REMOVED FROM v2.0.0. Whether or not to automatically populate profile with data loaded through profileParamsToPopulate config. - `setProfilePopulateResults` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** `true` Whether or not to diff --git a/docs/api/helpers.md b/docs/api/helpers.md index 8c6f4d1cb..14593050d 100644 --- a/docs/api/helpers.md +++ b/docs/api/helpers.md @@ -5,6 +5,7 @@ Detect whether items are loaded yet or not **Parameters** - `item` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Item to check loaded status of. A comma seperated list is also acceptable. +- `args` **...Any** **Examples** @@ -24,7 +25,7 @@ Detect whether items are empty or not **Parameters** - `item` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Item to check loaded status of. A comma seperated list is also acceptable. -- `data` +- `args` **...Any** **Examples** diff --git a/docs/api/reducer.md b/docs/api/reducer.md index 6cd075930..895d9165e 100644 --- a/docs/api/reducer.md +++ b/docs/api/reducer.md @@ -1,35 +1,73 @@ -# requestingReducer +# isInitializingReducer -Reducer for requesting state. Changed by `START` and `SET` actions. +Reducer for isInitializing state. Changed by `AUTHENTICATION_INIT_STARTED` +and `AUTHENTICATION_INIT_FINISHED` actions. **Parameters** -- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)](default {})** Current requesting redux state +- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Current isInitializing redux state (optional, default `false`) - `action` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched + - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of action that was dispatched Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Profile state after reduction -# dataReducer +# requestingReducer -Reducer for data state. Changed by `LOGIN`, `LOGOUT`, and `LOGIN_ERROR` -actions. +Reducer for requesting state.Changed by `START`, `NO_VALUE`, and `SET` actions. **Parameters** -- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)](default {})** Current data redux state -- `action` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched +- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Current requesting redux state (optional, default `{}`) +- `action` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched + - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of action that was dispatched + - `action.path` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Path of action that was dispatched +- `$1` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** + - `$1.type` + - `$1.path` + +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Profile state after reduction + +# requestedReducer + +Reducer for requested state. Changed by `START`, `NO_VALUE`, and `SET` actions. + +**Parameters** + +- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Current requested redux state (optional, default `{}`) +- `action` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched + - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of action that was dispatched + - `action.path` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Path of action that was dispatched +- `$1` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** + - `$1.type` + - `$1.path` + +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Profile state after reduction + +# timestampsReducer + +Reducer for timestamps state. Changed by `START`, `NO_VALUE`, and `SET` actions. + +**Parameters** + +- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Current timestamps redux state (optional, default `{}`) +- `action` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched + - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of action that was dispatched + - `action.path` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Path of action that was dispatched +- `$1` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** + - `$1.type` + - `$1.path` Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Profile state after reduction # authReducer -Reducer for auth state. Changed by `LOGIN`, `LOGOUT`, and `LOGIN_ERROR` -actions. +Reducer for auth state. Changed by `LOGIN`, `LOGOUT`, and `LOGIN_ERROR` actions. **Parameters** -- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)](default {})** Current auth redux state -- `action` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched +- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Current auth redux state (optional, default `{isLoaded:false}`) +- `action` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched + - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of action that was dispatched Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Profile state after reduction @@ -40,35 +78,66 @@ Reducer for profile state. Changed by `SET_PROFILE`, `LOGOUT`, and **Parameters** -- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)](default null)** Current profile redux state +- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Current profile redux state (optional, default `{isLoaded:false}`) - `action` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched + - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of action that was dispatched Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Profile state after reduction -# isInitializingReducer +# errorsReducer -Reducer for isInitializing state. Changed by `AUTHENTICATION_INIT_STARTED` -and `AUTHENTICATION_INIT_FINISHED` actions. +Reducer for errors state. Changed by `UNAUTHORIZED_ERROR` +and `LOGOUT` actions. **Parameters** -- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)](default false)** Current isInitializing redux state -- `action` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched +- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Current authError redux state (optional, default `[]`) +- `action` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched + - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of action that was dispatched Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Profile state after reduction -# errorsReducer +# listenersReducer -Reducer for errors state. Changed by `UNAUTHORIZED_ERROR` +Reducer for listeners state. Changed by `UNAUTHORIZED_ERROR` and `LOGOUT` actions. **Parameters** -- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)](default \[])** Current authError redux state -- `action` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched +- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Current authError redux state (optional, default `[]`) +- `action` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched + - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of action that was dispatched Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Profile state after reduction +# dataReducer + +Reducer for data state. Changed by `SET`, `SET_ORDERED`,`NO_VALUE`, and +`LOGOUT` actions. + +**Parameters** + +- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Current data redux state (optional, default `{}`) +- `action` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched + - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of action that was dispatched + - `action.path` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Path of action that was dispatched + +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Data state after reduction + +# orderedReducer + +Reducer for ordered state. Changed by `SET`, `SET_ORDERED`,`NO_VALUE`, and +`LOGOUT` actions. + +**Parameters** + +- `state` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Current data redux state (optional, default `{}`) +- `action` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Object containing the action that was dispatched + - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of action that was dispatched + - `action.path` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Path of action that was dispatched + +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Data state after reduction + # firebaseStateReducer Reducer for react redux firebase. This function is called @@ -81,6 +150,7 @@ changes. - `state` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Current Redux State - `action` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Action which will modify state - `action.type` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of Action being called - - `action.data` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Type of Action which will modify state + - `action.path` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Path of action that was dispatched + - `action.data` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Data associated with action -Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** State +Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Firebase redux state diff --git a/docs/roadmap.md b/docs/roadmap.md index 1f5767148..1e6cfa615 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -41,6 +41,7 @@ * Use `storeAs` with populates - [#130](https://github.com/prescottprue/react-redux-firebase/issues/130) * `updateUser` method for updating currently authenticated user's user object (`/users/${uid}`) * `updateAuth` method for updating currently authenticated user's auth object [as seen in the Firebase docs](https://firebase.google.com/docs/auth/web/manage-users#get_a_users_provider-specific_profile_information) - [#129](https://github.com/prescottprue/react-redux-firebase/issues/129) +* Ability to use different stores - [#148](https://github.com/prescottprue/react-redux-firebase/pull/148) * Expose Firebase messaging (`firebase.messaging()`) * Typescript typings - [#142](https://github.com/prescottprue/react-redux-firebase/issues/142) * `enableEmptyAuthChanges` config option added - [#137](https://github.com/prescottprue/react-redux-firebase/issues/137) @@ -87,21 +88,24 @@ #### Breaking Changes * Remove usage of `Immutable.js` and Immutable Maps (no more need for `pathToJS()` & `dataToJS()` to load data from redux) +* Firebase is now initialized outside of `react-redux-firebase` - [#173](https://github.com/prescottprue/react-redux-firebase/issues), [#131](https://github.com/prescottprue/react-redux-firebase/issues), [#107](https://github.com/prescottprue/react-redux-firebase/issues) * reducer split into multiple nested reducers for a few reasons: * follows [standard for nesting of reducers using combine reducers](http://redux.js.org/docs/recipes/reducers/UpdatingNormalizedData.html)). * allows for separately importable reducers (for placing in other parts of redux other than `state.firebase`) * Improved rendering/update performance for `react` - [#84](https://github.com/prescottprue/react-redux-firebase/issues/84) #### Features +* Support for keeping data on logout - [#125](https://github.com/prescottprue/react-redux-firebase/issues/125) * `react-native` index file referenced in `package.json` that makes it no longer necessary to pass `ReactNative` in config * `AuthRequired` decorator (or decorator factory) that forces auth to exist before rendering component -* Possibility of delayed initialization - [#70](https://github.com/prescottprue/react-redux-firebase/issues/70) (more research needed) +* Support [`react-native-firebase`](https://github.com/invertase/react-native-firebase) [#131](https://github.com/prescottprue/react-redux-firebase/issues/131) #### Enhancements/Fixes * Implement [`firebase-server`](https://github.com/urish/firebase-server) for tests instead of using demo firebase instance -#### Enhancements -* Implement [`firebase-server`](https://github.com/urish/firebase-server) for tests instead of using demo firebase instance +#### Under Consideration +* Possibility of delayed initialization - [#70](https://github.com/prescottprue/react-redux-firebase/issues/70) (more research needed) + ### Long Term Goals * Optional Built in Role Management diff --git a/docs/v2-migration-guide.md b/docs/v2-migration-guide.md index e21cbc859..73498d0d2 100644 --- a/docs/v2-migration-guide.md +++ b/docs/v2-migration-guide.md @@ -6,10 +6,52 @@ * simplified population (can easily be done manually following common redux patterns) * [`redux-persist`](https://github.com/rt2zz/redux-persist) is supported out of the box * Simplified population through `populate` (instead of `populatedDataToJS`) -* Firebase instance can be passed as first argument instead of config vars: +* Firebase instance must be passed as first argument instead of config vars: * removes platform specific code while improving platform support * allows any version of Firebase to be used * allows [`react-native-firebase`](https://github.com/invertase/react-native-firebase) to be passed (for using native modules instead of JS within `react-native`) + * firebase is no longer a dependency (shrinks umd bundle size) + +### Pass In Firebase instance + +If you would like to instantiate a Firebase instance outside of `react-redux-firebase`, you can pass it in as the first argument like so: + +#### `v1.*.*` + +```js +import { reactReduxFirebase } from 'react-redux-firebase' +const fbConfig = {} // object containing Firebase config +const rrfConfig = { userProfile: 'users' } // react-redux-firebase config + +const store = createStore( + reducer, + initialState, + compose( + reactReduxFirebase(fbConfig, rrfConfig), // pass in firebase instance instead of config + applyMiddleware(...middleware) + ) +) +``` + +#### `v2.*.*` + +```js +import { reactReduxFirebase } from 'react-redux-firebase' +import * as firebase from 'firebase' + +const fbConfig = {} // object containing Firebase config +firebase.initializeApp(fbConfig) // initialize firebase instance +const rrfConfig = { userProfile: 'users' } // react-redux-firebase config + +const store = createStore( + reducer, + initialState, + compose( + reactReduxFirebase(firebase, rrfConfig), // pass in firebase instance instead of config + applyMiddleware(...middleware) + ) +) +``` ### Loading Data and Paths @@ -29,6 +71,7 @@ import { firebaseConnect, dataToJS, pathToJS } from 'react-redux-firebase'; ``` #### `v2.*.*` + ```js import { connect } from 'react-redux' import { firebaseConnect } from 'react-redux-firebase'; @@ -85,28 +128,6 @@ const populates = [{ child: 'owner', root: 'users' }] ) ``` -### Pass In Firebase instance - -If you would like to instantiate a Firebase instance outside of `react-redux-firebase`, you can pass it in as the first argument like so: - -```js -import * as firebase from 'firebase/app' -import 'firebase/auth' -import 'firebase/database' -import 'firebase/storage' - -const fbConfig = {} // object containing Firebase config -firebase.initializeApp(fbConfig) // initialize firebase instance - -const store = createStore( - reducer, - initialState, - compose( - reactReduxFirebase(firebase, reduxConfig), // pass in firebase instance instead of config - applyMiddleware(...middleware) - ) -) -``` #### [react-native-firebase](https://github.com/invertase/react-native-firebase) @@ -115,17 +136,15 @@ Passing in an instance also allows for libraries with similar APIs (such as [`re ```js import RNFirebase from 'react-native-firebase'; -const configurationOptions = { - debug: true -}; - -const firebase = RNFirebase.initializeApp(configurationOptions); +const rnfConfig = { debug: true } // react-native-firebase config +const firebase = RNFirebase.initializeApp(rnfConfig); const store = createStore( reducer, - undefined, + initialState, compose( - reactReduxFirebase(RNFirebase, reduxConfig), // pass in react-native-firebase instance instead of config + // pass in react-native-firebase instance instead of firebase instance + reactReduxFirebase(RNFirebase, reduxConfig), applyMiddleware(...middleware) ) ) @@ -139,13 +158,18 @@ View the [redux-persist](/docs/recipes/redux-persist) section for the full examp import { compose, createStore } from 'redux' import { reactReduxFirebase } from 'react-redux-firebase' import { persistStore, autoRehydrate } from 'redux-persist' -import { firebase as fbConfig, reduxFirebase as reduxConfig } from '../config' +import * as firebase from 'firebase' + +const fbConfig = {} // firebase config object +firebase.initializeApp(fbConfig) + +const rrfConfig = {} const store = createStore( reducer, initialState, compose( - reactReduxFirebase(fbConfig, reduxConfig), + reactReduxFirebase(fbConfig, rrfConfig), autoRehydrate() ) ) diff --git a/examples/complete/material/README.md b/examples/complete/material/README.md index 29a83a000..1c05eec0a 100644 --- a/examples/complete/material/README.md +++ b/examples/complete/material/README.md @@ -21,6 +21,14 @@ While developing, you will probably rely mostly on `npm start`; however, there a |`lint`|Lint all `.js` files.| |`lint:fix`|Lint and fix all `.js` files. [Read more on this](http://eslint.org/docs/user-guide/command-line-interface.html#fix).| +## Config + +Visit the config file ([`src/config.js`](/src/config.js)) to change config within the project + +This usually gets skipped form git tracking because either: +* it is built within a continuous integration environment based on environment/branch settings then deployed +* your local version is deployed if you are using `firebase deploy` + ## What is Shown * Route protection using `redux-auth-wrapper` * Data input/validation using `redux-form` diff --git a/examples/complete/material/package.json b/examples/complete/material/package.json index b00c5a65c..7cdfd2116 100644 --- a/examples/complete/material/package.json +++ b/examples/complete/material/package.json @@ -59,15 +59,16 @@ "author": "prescottprue (https://github.com/prescottprue)", "license": "MIT", "dependencies": { + "firebase": "^4.1.3", "lodash": "^4.17.2", "material-ui": "0.16.0", "normalize.css": "^4.1.1", - "prop-types": "^15.5.8", + "prop-types": "^15.5.10", "react": "15.3.2", "react-dom": "15.3.2", "react-google-button": "^0.1.0", "react-redux": "^4.4.5", - "react-redux-firebase": "*", + "react-redux-firebase": "2.0.0-alpha.7", "react-router": "^2.8.0", "react-tap-event-plugin": "1.0.0", "redux": "^3.6.0", @@ -94,7 +95,6 @@ "css-loader": "^0.25.0", "cssnano": "^3.7.4", "debug": "^2.2.0", - "enzyme": "^2.5.1", "eslint": "^3.9.1", "eslint-config-standard": "^6.2.1", "eslint-config-standard-react": "^4.2.0", @@ -110,12 +110,10 @@ "imports-loader": "^0.6.5", "ip": "^1.1.2", "json-loader": "^0.5.4", - "mocha": "^3.0.1", "node-sass": "^3.7.0", "nodemon": "^1.10.2", "postcss-loader": "^0.13.0", "redbox-react": "^1.2.10", - "redux-logger": "^3.0.6", "rimraf": "^2.5.4", "sass-loader": "^4.0.0", "style-loader": "^0.13.1", diff --git a/examples/complete/material/src/config.js b/examples/complete/material/src/config.js index 3dc660662..bb861c2e7 100644 --- a/examples/complete/material/src/config.js +++ b/examples/complete/material/src/config.js @@ -12,9 +12,11 @@ export const firebase = { // For more details, visit https://prescottprue.gitbooks.io/react-redux-firebase/content/config.html export const reduxFirebase = { userProfile: 'users', // root that user profiles are written to - enableLogging: false, // enable/disable Firebase Database Logging - updateProfileOnLogin: false // enable/disable updating of profile on login - // profileDecorator: (userData) => ({ email: userData.email }) // customize format of user profile + // enableLogging: true, // enable/disable Firebase Database Logging + updateProfileOnLogin: false, // enable/disable updating of profile on login + presence: 'presence', + // profileParamsToPopulate: [{ child: 'cars', root: 'cars' }] + // profileFactory: (userData) => ({ email: userData.email }) // customize format of user profile } export const env = 'development' diff --git a/examples/complete/material/src/containers/App/App.js b/examples/complete/material/src/containers/App/App.js index f2e3d83af..7e231e218 100644 --- a/examples/complete/material/src/containers/App/App.js +++ b/examples/complete/material/src/containers/App/App.js @@ -2,9 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { browserHistory, Router } from 'react-router' import { Provider } from 'react-redux' - -// Themeing/Styling -import Theme from '../../theme' +import Theme from 'theme' import getMuiTheme from 'material-ui/styles/getMuiTheme' // Tap Plugin @@ -16,11 +14,9 @@ export default class AppContainer extends Component { muiTheme: PropTypes.object } - getChildContext = () => ( - { - muiTheme: getMuiTheme(Theme) - } - ) + getChildContext = () => ({ + muiTheme: getMuiTheme(Theme) + }) static propTypes = { routes: PropTypes.object.isRequired, diff --git a/examples/complete/material/src/routes/Account/containers/AccountContainer.js b/examples/complete/material/src/routes/Account/containers/AccountContainer.js index dd08a8732..274b38deb 100644 --- a/examples/complete/material/src/routes/Account/containers/AccountContainer.js +++ b/examples/complete/material/src/routes/Account/containers/AccountContainer.js @@ -10,7 +10,7 @@ import LoadingSpinner from 'components/LoadingSpinner' import AccountForm from '../components/AccountForm/AccountForm' import classes from './AccountContainer.scss' -@UserIsAuthenticated // redirect to /login if user is not authenticated +// @UserIsAuthenticated // redirect to /login if user is not authenticated @firebaseConnect() // add this.props.firebase @connect( // Map redux state to props ({ firebase: { auth, profile } }) => ({ diff --git a/examples/complete/material/src/routes/Home/containers/HomeContainer.js b/examples/complete/material/src/routes/Home/containers/HomeContainer.js index 5ede8fabb..9218f04cf 100755 --- a/examples/complete/material/src/routes/Home/containers/HomeContainer.js +++ b/examples/complete/material/src/routes/Home/containers/HomeContainer.js @@ -6,6 +6,7 @@ import Theme from 'theme' import { firebaseConnect, isLoaded, + isEmpty, populate // for populated list } from 'react-redux-firebase' import CircularProgress from 'material-ui/CircularProgress' @@ -21,17 +22,17 @@ const populates = [{ child: 'owner', root: 'users' }] @firebaseConnect([ // 'todos' // sync full list of todos + // { path: 'todos', populates }, // gather data to populate owners (uid => object) // { 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 ]) @connect( // get auth, profile, and data from ({ firebase, firebase: { auth, profile, data: { todos } } }) => ({ auth, profile, - // todos, - todos: populate(firebase, 'todos', populates) // if populating + todos, + // todos: populate(firebase, 'todos', populates) // populate todos with users data from redux // todos: firebase.ordered.todos // if using ordering such as orderByChild }) ) @@ -87,7 +88,7 @@ export default class Home extends Component { handleAdd = (newTodo) => { // Attach user if logged in - if (this.props.auth) { + if (!isEmpty(this.props.auth)) { newTodo.owner = this.props.auth.uid } else { newTodo.owner = 'Anonymous' @@ -99,9 +100,9 @@ export default class Home extends Component { } render () { - const { todos } = this.props + const { todos, profile, pProfile } = this.props const { error } = this.state - console.log('todos: ', todos) + console.log('profile:', profile) return (
{ diff --git a/examples/complete/material/src/routes/Login/containers/LoginContainer.js b/examples/complete/material/src/routes/Login/containers/LoginContainer.js index 3d29df527..865398a41 100644 --- a/examples/complete/material/src/routes/Login/containers/LoginContainer.js +++ b/examples/complete/material/src/routes/Login/containers/LoginContainer.js @@ -6,24 +6,28 @@ import { connect } from 'react-redux' import { firebaseConnect, isLoaded, - isEmpty, - pathToJS + isEmpty } from 'react-redux-firebase' import Paper from 'material-ui/Paper' import Snackbar from 'material-ui/Snackbar' import { UserIsNotAuthenticated } from 'utils/router' -import { SIGNUP_PATH } from 'constants' +import { SIGNUP_PATH, LIST_PATH } from 'constants' import LoginForm from '../components/LoginForm' import classes from './LoginContainer.scss' -@UserIsNotAuthenticated // redirect to list page if logged in +// TODO: Uncomment redirect decorator v2.0.0 router util still requires update +// @UserIsNotAuthenticated // redirect to list page if logged in @firebaseConnect() // add this.props.firebase @connect( // map redux state to props - ({ firebase }) => ({ - authError: pathToJS(firebase, 'authError') + ({ firebase: { authError } }) => ({ + authError }) ) export default class Login extends Component { + static contextTypes = { + router: PropTypes.object.isRequired + } + static propTypes = { firebase: PropTypes.shape({ login: PropTypes.func.isRequired @@ -41,6 +45,9 @@ export default class Login extends Component { handleLogin = loginData => { this.setState({ snackCanOpen: true }) return this.props.firebase.login(loginData) + .then(() => { + return this.context.router.push(LIST_PATH) // v2.0.0 router util still requires update + }) } providerLogin = (provider) => 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 42ae6d33d..42e0f34a0 100644 --- a/examples/complete/material/src/routes/Projects/components/NewProjectDialog/NewProjectDialog.js +++ b/examples/complete/material/src/routes/Projects/components/NewProjectDialog/NewProjectDialog.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import { reduxForm } from 'redux-form' +import { reduxForm, Field } 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/containers/ProjectsContainer.js b/examples/complete/material/src/routes/Projects/containers/ProjectsContainer.js index 44ed8fe62..63c242bd1 100644 --- a/examples/complete/material/src/routes/Projects/containers/ProjectsContainer.js +++ b/examples/complete/material/src/routes/Projects/containers/ProjectsContainer.js @@ -21,15 +21,15 @@ const populates = [ { child: 'createdBy', root: 'users', keyProp: 'uid' } ] -@UserIsAuthenticated +// @UserIsAuthenticated @firebaseConnect([ { path: 'projects', populates } // 'projects#populate=owner:users' // string equivalent ]) @connect( - ({ firebase }, { params }) => ({ - auth: pathToJS(firebase, 'auth'), - projects: firebase.data.projects + ({ firebase: { auth, data: { projects } } }, { params }) => ({ + auth, + projects }) ) export default class Projects extends Component { @@ -51,7 +51,8 @@ export default class Projects extends Component { newSubmit = (newProject) => { const { firebase: { pushWithMeta } } = this.props - return pushWithMeta('projects', newProject) + console.log('this.props', this.props.firebase) + return this.props.firebase.push('projects', newProject) .then(() => this.setState({ newProjectModal: false })) .catch(err => { // TODO: Show Snackbar diff --git a/examples/complete/material/src/routes/Signup/containers/SignupContainer.js b/examples/complete/material/src/routes/Signup/containers/SignupContainer.js index b543faae1..17c5bc4f6 100644 --- a/examples/complete/material/src/routes/Signup/containers/SignupContainer.js +++ b/examples/complete/material/src/routes/Signup/containers/SignupContainer.js @@ -11,12 +11,13 @@ import { } from 'react-redux-firebase' import Paper from 'material-ui/Paper' import Snackbar from 'material-ui/Snackbar' -import { LOGIN_PATH } from 'constants' +import { LOGIN_PATH, LIST_PATH } from 'constants' import { UserIsNotAuthenticated } from 'utils/router' import SignupForm from '../components/SignupForm' import classes from './SignupContainer.scss' -@UserIsNotAuthenticated // redirect to list page if logged in +// TODO: Uncomment redirect decorator v2.0.0 router util still requires update +// @UserIsNotAuthenticated // redirect to list page if logged in @firebaseConnect() // add this.props.firebase @connect( // map redux state to props ({firebase}) => ({ @@ -24,6 +25,10 @@ import classes from './SignupContainer.scss' }) ) export default class Signup extends Component { + static contextTypes = { + router: PropTypes.object.isRequired + } + static propTypes = { firebase: PropTypes.object, authError: PropTypes.object @@ -40,6 +45,9 @@ export default class Signup extends Component { // create new user then login (redirect handled by decorator) return createUser(creds, { email, username }) .then(() => login(creds)) + .then(() => { + return this.context.router.push(LIST_PATH) // v2.0.0 router util still requires update + }) } providerLogin = (provider) => { diff --git a/examples/complete/material/src/store/createStore.js b/examples/complete/material/src/store/createStore.js index b68bae79d..bdc770751 100755 --- a/examples/complete/material/src/store/createStore.js +++ b/examples/complete/material/src/store/createStore.js @@ -3,10 +3,7 @@ import thunk from 'redux-thunk' import { browserHistory } from 'react-router' import { reactReduxFirebase, getFirebase } 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 from 'firebase' import { firebase as fbConfig, reduxFirebase as reduxConfig } from '../config' import makeRootReducer from './reducers' import { updateLocation } from './location' @@ -17,7 +14,7 @@ export default (initialState = {}, history) => { // ====================================================== const middleware = [ thunk.withExtraArgument(getFirebase), - logger, // Uncomment to see actions in console + // logger, // Uncomment to see actions in console // This is where you add other middleware like redux-observable ] diff --git a/examples/complete/material/src/utils/router.js b/examples/complete/material/src/utils/router.js index f64e78be6..ebcb3c797 100644 --- a/examples/complete/material/src/utils/router.js +++ b/examples/complete/material/src/utils/router.js @@ -18,8 +18,8 @@ export const UserIsAuthenticated = UserAuthWrapper({ // eslint-disable-line new- LoadingComponent: LoadingSpinner, authSelector: ({ firebase: { auth } }) => auth, authenticatingSelector: ({ firebase: { auth, isInitializing } }) => - auth === undefined || isInitializing === true, - predicate: auth => auth !== null, + !auth.isLoaded || isInitializing, + predicate: auth => !auth.isEmpty, redirectAction: newLoc => (dispatch) => { browserHistory.replace(newLoc) dispatch({ @@ -46,8 +46,8 @@ export const UserIsNotAuthenticated = UserAuthWrapper({ // eslint-disable-line n props.location.query.redirect || LIST_PATH, authSelector: ({ firebase: { auth } }) => auth, authenticatingSelector: ({ firebase: { auth, isInitializing } }) => - auth === undefined || isInitializing === true, - predicate: auth => auth === null, + !auth.isLoaded || isInitializing, + predicate: auth => auth.isEmpty, redirectAction: newLoc => (dispatch) => { browserHistory.replace(newLoc) dispatch({ type: AUTHED_REDIRECT }) diff --git a/examples/complete/material/yarn.lock b/examples/complete/material/yarn.lock index 2ded603fe..8829f2f3c 100644 --- a/examples/complete/material/yarn.lock +++ b/examples/complete/material/yarn.lock @@ -66,10 +66,20 @@ ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" +ansi-styles@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.1.0.tgz#09c202d5c917ec23188caa5c9cb9179cd9547750" + dependencies: + color-convert "^1.0.0" + anymatch@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" @@ -78,8 +88,8 @@ anymatch@^1.3.0: micromatch "^2.1.5" aproba@^1.0.3: - version "1.1.1" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab" + version "1.1.2" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1" are-we-there-yet@~1.1.2: version "1.1.4" @@ -176,8 +186,8 @@ async@^1.3.0, async@^1.5.0: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" async@^2.0.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/async/-/async-2.4.0.tgz#4990200f18ea5b837c2cc4f8c031a6985c385611" + version "2.5.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d" dependencies: lodash "^4.14.0" @@ -217,19 +227,19 @@ babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: js-tokens "^3.0.0" babel-core@^6.22.1, babel-core@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83" + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.25.0.tgz#7dd42b0463c742e9d5296deb3ec67a9322dad729" dependencies: babel-code-frame "^6.22.0" - babel-generator "^6.24.1" + babel-generator "^6.25.0" babel-helpers "^6.24.1" babel-messages "^6.23.0" babel-register "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - babylon "^6.11.0" + babel-template "^6.25.0" + babel-traverse "^6.25.0" + babel-types "^6.25.0" + babylon "^6.17.2" convert-source-map "^1.1.0" debug "^2.1.1" json5 "^0.5.0" @@ -250,13 +260,13 @@ babel-eslint@^6.0.4: lodash.assign "^4.0.0" lodash.pickby "^4.0.0" -babel-generator@^6.18.0, babel-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497" +babel-generator@^6.18.0, babel-generator@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.25.0.tgz#33a1af70d5f2890aeb465a4a7793c1df6a9ea9fc" dependencies: babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.24.1" + babel-types "^6.25.0" detect-indent "^4.0.0" jsesc "^1.3.0" lodash "^4.2.0" @@ -745,8 +755,8 @@ babel-plugin-transform-object-rest-spread@^6.22.0: babel-runtime "^6.22.0" babel-plugin-transform-react-display-name@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.23.0.tgz#4398910c358441dc4cef18787264d0412ed36b37" + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz#67e2bf1f1e9c93ab08db96792e05392bf2cc28d1" dependencies: babel-runtime "^6.22.0" @@ -899,50 +909,54 @@ babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtim core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.3.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333" +babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0, babel-template@^6.3.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071" dependencies: babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - babylon "^6.11.0" + babel-traverse "^6.25.0" + babel-types "^6.25.0" + babylon "^6.17.2" lodash "^4.2.0" -babel-traverse@^6.0.20, babel-traverse@^6.18.0, babel-traverse@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695" +babel-traverse@^6.0.20, babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" dependencies: babel-code-frame "^6.22.0" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.24.1" - babylon "^6.15.0" + babel-types "^6.25.0" + babylon "^6.17.2" debug "^2.2.0" globals "^9.0.0" invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.0.19, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975" +babel-types@^6.0.19, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" dependencies: babel-runtime "^6.22.0" esutils "^2.0.2" lodash "^4.2.0" to-fast-properties "^1.0.1" -babylon@^6.0.18, babylon@^6.11.0, babylon@^6.13.0, babylon@^6.15.0: - version "6.17.0" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.0.tgz#37da948878488b9c4e3c4038893fa3314b3fc932" +babylon@^6.0.18, babylon@^6.17.2, babylon@^6.17.4: + version "6.17.4" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a" -balanced-match@^0.4.1, balanced-match@^0.4.2: +balanced-match@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + base64-js@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" + version "1.2.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886" base64url@2.0.0, base64url@^2.0.0: version "2.0.0" @@ -990,14 +1004,14 @@ boom@2.x.x: hoek "2.x.x" bowser@^1.0.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.6.1.tgz#9157e9498f456e937173a2918f3b2161e5353eb3" + version "1.7.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.7.0.tgz#169de4018711f994242bff9a8009e77a1f35e003" -brace-expansion@^1.0.0: - version "1.1.7" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" dependencies: - balanced-match "^0.4.1" + balanced-match "^1.0.0" concat-map "0.0.1" braces@^1.8.2: @@ -1008,10 +1022,6 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" -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" @@ -1035,10 +1045,6 @@ buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" -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: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" @@ -1101,8 +1107,8 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000665" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000665.tgz#e84f4277935f295f546f8533cb0b410a8415b972" + version "1.0.30000696" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000696.tgz#e71f5c61e1f96c7a3af4e791ac5db55e11737604" caseless@~0.12.0: version "0.12.0" @@ -1115,6 +1121,10 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" +chain-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc" + chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -1125,34 +1135,21 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.0.1.tgz#dbec49436d2ae15f536114e76d14656cdbc0f44d" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + change-emitter@^0.1.2: version "0.1.6" resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515" -cheerio@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.0" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash.assignin "^4.0.9" - lodash.bind "^4.1.4" - lodash.defaults "^4.0.1" - lodash.filter "^4.4.0" - lodash.flatten "^4.2.0" - lodash.foreach "^4.3.0" - lodash.map "^4.4.0" - lodash.merge "^4.4.0" - lodash.pick "^4.2.1" - lodash.reduce "^4.4.0" - lodash.reject "^4.4.0" - lodash.some "^4.4.0" - chokidar@^1.0.0, chokidar@^1.4.3: - version "1.6.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" dependencies: anymatch "^1.3.0" async-each "^1.0.0" @@ -1170,14 +1167,14 @@ circular-json@^0.3.1: resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" clap@^1.0.9: - version "1.1.3" - resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.3.tgz#b3bd36e93dd4cbfb395a3c26896352445265c05b" + version "1.2.0" + resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.0.tgz#59c90fe3e137104746ff19469a27a634ff68c857" dependencies: chalk "^1.1.3" -clean-css@4.0.x: - version "4.0.12" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.0.12.tgz#a02e61707f1840bd3338f54dbc9acbda4e772fa3" +clean-css@4.1.x: + version "4.1.5" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.5.tgz#d09a87a02a5375117589796ae76a063cacdb541a" dependencies: source-map "0.5.x" @@ -1216,8 +1213,8 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" coa@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.1.tgz#7f959346cfc8719e3f7233cd6852854a7c67d8a3" + version "1.0.3" + resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.3.tgz#1b54a5e1dcf77c990455d4deea98c564416dc893" dependencies: q "^1.1.2" @@ -1225,7 +1222,7 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" -color-convert@^1.3.0: +color-convert@^1.0.0, color-convert@^1.3.0: version "1.9.0" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" dependencies: @@ -1267,7 +1264,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.9.0, commander@2.9.x: +commander@2.9.x, commander@~2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: @@ -1353,10 +1350,11 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" create-react-class@^15.5.1: - version "15.5.2" - resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.5.2.tgz#6a8758348df660b88326a0e764d569f274aad681" + version "15.6.0" + resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.0.tgz#ab448497c26566e1e29413e883207d57cfe7bed4" dependencies: fbjs "^0.8.9" + loose-envify "^1.3.1" object-assign "^4.1.1" cross-spawn@^3.0.0: @@ -1402,7 +1400,7 @@ css-loader@^0.25.0: postcss-modules-values "^1.1.0" source-list-map "^0.1.4" -css-select@^1.1.0, css-select@~1.2.0: +css-select@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" dependencies: @@ -1419,6 +1417,14 @@ css-selector-tokenizer@^0.6.0: fastparse "^1.1.1" regexpu-core "^1.0.0" +css-selector-tokenizer@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86" + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + css-what@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" @@ -1493,29 +1499,17 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" +debug@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e" dependencies: - ms "0.7.2" - -debug@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" - dependencies: - ms "0.7.2" - -debug@2.6.4: - version "2.6.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.4.tgz#7586a9b3c39741c0282ae33445c4e8ac74734fe0" - dependencies: - ms "0.7.3" + ms "2.0.0" debug@^2.1.1, debug@^2.2.0: - version "2.6.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.6.tgz#a9fa6fbe9ca43cf1e79f73b75c0189cbb7d6db5a" + version "2.6.8" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: - ms "0.7.3" + ms "2.0.0" decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" @@ -1530,8 +1524,8 @@ deep-equal@^1.0.0, deep-equal@^1.0.1: resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" deep-extend@~0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" + version "0.4.2" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" deep-is@~0.1.3: version "0.1.3" @@ -1582,10 +1576,6 @@ detect-indent@^4.0.0: dependencies: repeating "^2.0.0" -diff@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" - doctrine@^1.2.2: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -1606,7 +1596,11 @@ dom-converter@~0.1: dependencies: utila "~0.3" -dom-serializer@0, dom-serializer@~0.1.0: +dom-helpers@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a" + +dom-serializer@0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" dependencies: @@ -1621,7 +1615,7 @@ domain-browser@^1.1.1: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" -domelementtype@1, domelementtype@^1.3.0: +domelementtype@1: version "1.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" @@ -1635,19 +1629,13 @@ domhandler@2.1: dependencies: domelementtype "1" -domhandler@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" - dependencies: - domelementtype "1" - domutils@1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.1.6.tgz#bddc3de099b9a2efacc51c623f28f416ecc57485" dependencies: domelementtype "1" -domutils@1.5.1, domutils@^1.5.1: +domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" dependencies: @@ -1689,8 +1677,8 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" electron-to-chromium@^1.2.7: - version "1.3.9" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.9.tgz#db1cba2a26aebcca2f7f5b8b034554468609157d" + version "1.3.15" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.15.tgz#08397934891cbcfaebbd18b82a95b5a481138369" emojis-list@^2.0.0: version "2.1.0" @@ -1720,25 +1708,10 @@ enhanced-resolve@~0.9.0: memory-fs "^0.2.0" tapable "^0.1.8" -entities@^1.1.1, entities@~1.1.1: +entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" -enzyme@^2.5.1: - version "2.8.2" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-2.8.2.tgz#6c8bcb05012abc4aa4bc3213fb23780b9b5b1714" - dependencies: - cheerio "^0.22.0" - function.prototype.name "^1.0.0" - is-subset "^0.1.1" - lodash "^4.17.2" - object-is "^1.0.1" - object.assign "^4.0.4" - object.entries "^1.0.3" - object.values "^1.0.3" - prop-types "^15.5.4" - uuid "^2.0.3" - errno@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" @@ -1757,7 +1730,7 @@ error-stack-parser@^1.3.6: dependencies: stackframe "^0.3.1" -es-abstract@^1.6.1, es-abstract@^1.7.0: +es-abstract@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c" dependencies: @@ -1775,8 +1748,8 @@ es-to-primitive@^1.1.1: is-symbol "^1.0.1" es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.15" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.15.tgz#c330a5934c1ee21284a7c081a86e5fd937c91ea6" + version "0.10.23" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.23.tgz#7578b51be974207a5487821b56538c224e4e7b38" dependencies: es6-iterator "2" es6-symbol "~3.1" @@ -1808,10 +1781,6 @@ es6-promise@^3.0.2: version "3.3.1" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" -es6-promise@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.1.0.tgz#dda03ca8f9f89bc597e689842929de7ba8cebdf0" - es6-set@~0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" @@ -1842,7 +1811,7 @@ escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -1953,20 +1922,16 @@ esquery@^1.0.0: estraverse "^4.0.0" esrecurse@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" + version "4.2.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" dependencies: - estraverse "~4.1.0" + estraverse "^4.1.0" object-assign "^4.0.1" -estraverse@^4.0.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" -estraverse@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" - esutils@^2.0.0, esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -2015,8 +1980,8 @@ expand-range@^1.8.1: fill-range "^2.1.0" express@^4.14.0: - version "4.15.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.15.2.tgz#af107fc148504457f2dca9a6f2571d7129b97b35" + version "4.15.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.15.3.tgz#bab65d0f03aa80c358408972fc700f916944b662" dependencies: accepts "~1.3.3" array-flatten "1.1.1" @@ -2024,28 +1989,28 @@ express@^4.14.0: content-type "~1.0.2" cookie "0.3.1" cookie-signature "1.0.6" - debug "2.6.1" + debug "2.6.7" depd "~1.1.0" encodeurl "~1.0.1" escape-html "~1.0.3" etag "~1.8.0" - finalhandler "~1.0.0" + finalhandler "~1.0.3" fresh "0.5.0" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" parseurl "~1.3.1" path-to-regexp "0.1.7" - proxy-addr "~1.1.3" + proxy-addr "~1.1.4" qs "6.4.0" range-parser "~1.2.0" - send "0.15.1" - serve-static "1.12.1" + send "0.15.3" + serve-static "1.12.3" setprototypeof "1.0.3" statuses "~1.3.1" - type-is "~1.6.14" + type-is "~1.6.15" utils-merge "1.0.0" - vary "~1.1.0" + vary "~1.1.1" extend@~3.0.0: version "3.0.1" @@ -2137,11 +2102,11 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" -finalhandler@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.2.tgz#d0e36f9dbc557f2de14423df6261889e9d60c93a" +finalhandler@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.3.tgz#ef47e77950e999780e86022a560e3217e0d0cc89" dependencies: - debug "2.6.4" + debug "2.6.7" encodeurl "~1.0.1" escape-html "~1.0.3" on-finished "~2.3.0" @@ -2164,9 +2129,9 @@ find-up@^1.0.0, find-up@^1.1.2: path-exists "^2.0.0" pinkie-promise "^2.0.0" -firebase@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/firebase/-/firebase-3.9.0.tgz#c4237f50f58eeb25081b1839d6cbf175f8f7ed9b" +firebase@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/firebase/-/firebase-4.1.3.tgz#e5d7327366c854dc12461633ba8bfeea2f5c7358" dependencies: dom-storage "^2.0.2" faye-websocket "0.9.3" @@ -2238,11 +2203,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" fsevents@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff" + version "1.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4" dependencies: nan "^2.3.0" - node-pre-gyp "^0.6.29" + node-pre-gyp "^0.6.36" fstream-ignore@^1.0.5: version "1.0.5" @@ -2265,14 +2230,6 @@ function-bind@^1.0.2, function-bind@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" -function.prototype.name@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.0.0.tgz#5f523ca64e491a5f95aba80cc1e391080a14482e" - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.0" - is-callable "^1.1.2" - gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -2329,20 +2286,20 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@~7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@~7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.2" + minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" globals@^9.0.0, globals@^9.14.0: - version "9.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.17.0.tgz#0c0ca696d9b9bb694d2e5470bd37777caad50286" + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" globby@^5.0.0: version "5.0.0" @@ -2356,11 +2313,11 @@ globby@^5.0.0: pinkie-promise "^2.0.0" globule@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.1.0.tgz#c49352e4dc183d85893ee825385eb994bb6df45f" + version "1.2.0" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.0.tgz#1dc49c6822dd9e8a2fa00ba2a295006e8664bd09" dependencies: glob "~7.1.1" - lodash "~4.16.4" + lodash "~4.17.4" minimatch "~3.0.2" got@^3.2.0: @@ -2386,10 +2343,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" -growl@1.9.2: - version "1.9.2" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" - har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" @@ -2411,6 +2364,10 @@ has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -2459,8 +2416,8 @@ home-or-tmp@^2.0.0: os-tmpdir "^1.0.1" hosted-git-info@^2.1.4: - version "2.4.2" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67" + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" html-comment-regex@^1.1.0: version "1.1.1" @@ -2471,21 +2428,21 @@ html-entities@^1.2.0: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" html-minifier@^3.2.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.4.3.tgz#eb3a7297c804611f470454eeebe0aacc427e424a" + version "3.5.2" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.2.tgz#d73bc3ff448942408818ce609bf3fb0ea7ef4eb7" dependencies: camel-case "3.0.x" - clean-css "4.0.x" + clean-css "4.1.x" commander "2.9.x" he "1.1.x" ncname "1.0.x" param-case "2.1.x" relateurl "0.2.x" - uglify-js "~2.8.22" + uglify-js "3.0.x" html-webpack-plugin@^2.22.0: - version "2.28.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-2.28.0.tgz#2e7863b57e5fd48fe263303e2ffc934c3064d009" + version "2.29.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-2.29.0.tgz#e987f421853d3b6938c8c4c8171842e5fd17af23" dependencies: bluebird "^3.4.7" html-minifier "^3.2.3" @@ -2494,17 +2451,6 @@ html-webpack-plugin@^2.22.0: pretty-error "^2.0.2" toposort "^1.0.0" -htmlparser2@^3.9.1: - version "3.9.2" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" - dependencies: - domelementtype "^1.3.0" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^2.0.2" - htmlparser2@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe" @@ -2540,12 +2486,12 @@ hyphenate-style-name@^1.0.1: resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz#31160a36930adaf1fc04c6074f7eb41465d4ec4b" iconv-lite@~0.4.13: - version "0.4.17" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.17.tgz#4fdaa3b38acbc2c031b045d0edcdfe1ecab18c8d" + version "0.4.18" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2" -icss-replace-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5" +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" ieee754@^1.1.4: version "1.1.8" @@ -2556,12 +2502,8 @@ ignore-by-default@^1.0.0: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" ignore@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.0.tgz#3812d22cbe9125f2c2b4915755a1b8abd745a001" - -immutable@^3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2" + version "3.3.3" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d" imports-loader@^0.6.5: version "0.6.5" @@ -2603,7 +2545,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -2690,7 +2632,7 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" -is-callable@^1.1.1, is-callable@^1.1.2, is-callable@^1.1.3: +is-callable@^1.1.1, is-callable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" @@ -2699,8 +2641,8 @@ is-date-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" is-dotfile@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" is-equal-shallow@^0.1.3: version "0.1.3" @@ -2751,12 +2693,18 @@ is-npm@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" -is-number@^2.0.2, is-number@^2.1.0: +is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" dependencies: kind-of "^3.0.2" +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -2813,10 +2761,6 @@ is-stream@^1.0.0, is-stream@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" -is-subset@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" - is-svg@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" @@ -2868,28 +2812,22 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -istanbul-lib-coverage@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz#caca19decaef3525b5d6331d701f3f3b7ad48528" +istanbul-lib-coverage@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da" istanbul-lib-instrument@^1.1.4: - version "1.7.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.1.tgz#169e31bc62c778851a99439dd99c3cc12184d360" + version "1.7.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.3.tgz#925b239163eabdd68cc4048f52c2fa4f899ecfa7" dependencies: babel-generator "^6.18.0" babel-template "^6.16.0" babel-traverse "^6.18.0" babel-types "^6.18.0" - babylon "^6.13.0" - istanbul-lib-coverage "^1.1.0" + babylon "^6.17.4" + istanbul-lib-coverage "^1.1.1" semver "^5.3.0" -jodid25519@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" - dependencies: - jsbn "~0.1.0" - joi@^6.10.1: version "6.10.1" resolved "https://registry.yarnpkg.com/joi/-/joi-6.10.1.tgz#4d50c318079122000fe5f16af1ff8e1917b77e06" @@ -2904,12 +2842,12 @@ js-base64@^2.1.8, js-base64@^2.1.9: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce" js-tokens@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" js-yaml@^3.5.1: - version "3.8.3" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.3.tgz#33a05ec481c850c8875929166fe1beb61c728766" + version "3.8.4" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6" dependencies: argparse "^1.0.7" esprima "^3.1.1" @@ -2947,14 +2885,10 @@ 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" -json3@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" - json5@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -2974,13 +2908,13 @@ jsonpointer@^4.0.0: resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" jsonwebtoken@^7.3.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-7.4.0.tgz#515bf2bba070ec615bad97fd2e945027eb476946" + version "7.4.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-7.4.1.tgz#7ca324f5215f8be039cd35a6c45bb8cb74a448fb" dependencies: joi "^6.10.1" jws "^3.1.4" lodash.once "^4.0.0" - ms "^0.7.1" + ms "^2.0.0" xtend "^4.0.1" jsprim@^1.2.2: @@ -3022,8 +2956,14 @@ keycode@^2.1.1: resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.1.9.tgz#964a23c54e4889405b4861a5c9f0480d45141dfa" kind-of@^3.0.2: - version "3.2.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.0.tgz#b58abe4d5c044ad33726a8c1525b48cf891bff07" + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" dependencies: is-buffer "^1.1.5" @@ -3083,7 +3023,7 @@ loader-utils@^1.0.2: emojis-list "^2.0.0" json5 "^0.5.0" -lodash-es@^4.17.3, lodash-es@^4.17.4, lodash-es@^4.2.1: +lodash-es@^4.17.3, lodash-es@^4.2.1: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7" @@ -3098,10 +3038,6 @@ lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" -lodash._basecreate@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" - lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" @@ -3145,14 +3081,6 @@ lodash.assign@^4.0.0, lodash.assign@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" -lodash.assignin@^4.0.9: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" - -lodash.bind@^4.1.4: - version "4.2.1" - resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" - lodash.camelcase@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-3.0.1.tgz#932c8b87f8a4377897c67197533282f97aeac298" @@ -3163,14 +3091,6 @@ lodash.clonedeep@^4.3.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" -lodash.create@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" - dependencies: - lodash._baseassign "^3.0.0" - lodash._basecreate "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash.deburr@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-3.2.0.tgz#6da8f54334a366a7cf4c4c76ef8d80aa1b365ed5" @@ -3184,22 +3104,6 @@ lodash.defaults@^3.1.2: lodash.assign "^3.0.0" lodash.restparam "^3.0.0" -lodash.defaults@^4.0.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - -lodash.filter@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" - -lodash.flatten@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - -lodash.foreach@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" - lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" @@ -3220,15 +3124,11 @@ lodash.keys@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash.map@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" - lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" -lodash.merge@^4.4.0, lodash.merge@^4.6.0: +lodash.merge@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" @@ -3236,30 +3136,14 @@ lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" -lodash.pick@^4.2.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" - lodash.pickby@^4.0.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" -lodash.reduce@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" - -lodash.reject@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" - lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" -lodash.some@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" - lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" @@ -3274,19 +3158,15 @@ lodash.words@^3.0.0: dependencies: lodash._root "^3.0.0" -lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: +lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" -lodash@~4.16.4: - version "4.16.6" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" - 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.2.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.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: @@ -3308,11 +3188,11 @@ lowercase-keys@^1.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" lru-cache@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" dependencies: - pseudomap "^1.0.1" - yallist "^2.0.0" + pseudomap "^1.0.2" + yallist "^2.1.2" macaddress@^0.2.8: version "0.2.8" @@ -3419,15 +3299,19 @@ mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: dependencies: mime-db "~1.27.0" -mime@1.3.4, mime@1.3.x, mime@^1.3.4: +mime@1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" +mime@1.3.x, mime@^1.3.4: + version "1.3.6" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" + +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: - brace-expansion "^1.0.0" + brace-expansion "^1.1.7" minimist@0.0.8, minimist@~0.0.1: version "0.0.8" @@ -3437,39 +3321,19 @@ minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" -mocha@^3.0.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.3.0.tgz#d29b7428d3f52c82e2e65df1ecb7064e1aabbfb5" - dependencies: - browser-stdout "1.3.0" - commander "2.9.0" - debug "2.6.0" - diff "3.2.0" - escape-string-regexp "1.0.5" - glob "7.1.1" - growl "1.9.2" - json3 "3.3.2" - lodash.create "3.1.1" - mkdirp "0.5.1" - supports-color "3.1.2" - moment@2.x.x: version "2.18.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" -ms@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" - -ms@0.7.3, ms@^0.7.1: - version "0.7.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.3.tgz#708155a5e44e33f5fd0fc53e81d0d40a91be1fff" +ms@2.0.0, ms@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" mute-stream@0.0.5: version "0.0.5" @@ -3506,15 +3370,15 @@ no-case@^2.2.0: lower-case "^1.1.1" node-fetch@^1.0.1: - version "1.6.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" + version "1.7.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.1.tgz#899cb3d0a3c92f952c47f1b876f4c8aeabd400d5" dependencies: encoding "^0.1.11" is-stream "^1.0.1" node-gyp@^3.3.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.1.tgz#19561067ff185464aded478212681f47fd578cbc" + version "3.6.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60" dependencies: fstream "^1.0.0" glob "^7.0.3" @@ -3558,9 +3422,9 @@ node-libs-browser@^0.7.0: util "^0.10.3" vm-browserify "0.0.4" -node-pre-gyp@^0.6.29: - version "0.6.34" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7" +node-pre-gyp@^0.6.36: + version "0.6.36" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" dependencies: mkdirp "^0.5.1" nopt "^4.0.1" @@ -3628,8 +3492,8 @@ nopt@~1.0.10: abbrev "1" normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.3.8" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb" + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" dependencies: hosted-git-info "^2.1.4" is-builtin-module "^1.0.0" @@ -3660,8 +3524,8 @@ normalize.css@^4.1.1: resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-4.2.0.tgz#21d66cc557154d4379fd1e079ec7de58a379b099" "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: - version "4.1.0" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5" + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" @@ -3694,10 +3558,6 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" -object-is@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6" - object-keys@^1.0.10, object-keys@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" @@ -3710,15 +3570,6 @@ object.assign@^4.0.4: function-bind "^1.1.0" object-keys "^1.0.10" -object.entries@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f" - dependencies: - define-properties "^1.1.2" - es-abstract "^1.6.1" - function-bind "^1.1.0" - has "^1.0.1" - object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -3726,15 +3577,6 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" -object.values@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac99f13069a" - dependencies: - define-properties "^1.1.2" - es-abstract "^1.6.1" - function-bind "^1.1.0" - has "^1.0.1" - on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -4038,31 +3880,31 @@ postcss-minify-selectors@^2.0.4: postcss-selector-parser "^2.0.0" postcss-modules-extract-imports@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341" + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85" dependencies: - postcss "^5.0.4" + postcss "^6.0.1" postcss-modules-local-by-default@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz#29a10673fa37d19251265ca2ba3150d9040eb4ce" + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" dependencies: - css-selector-tokenizer "^0.6.0" - postcss "^5.0.4" + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" postcss-modules-scope@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz#ff977395e5e06202d7362290b88b1e8cd049de29" + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" dependencies: - css-selector-tokenizer "^0.6.0" - postcss "^5.0.4" + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" postcss-modules-values@^1.1.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz#f0e7d476fe1ed88c5e4c7f97533a3e772ad94ca1" + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" dependencies: - icss-replace-symbols "^1.0.2" - postcss "^5.0.14" + icss-replace-symbols "^1.1.0" + postcss "^6.0.1" postcss-normalize-charset@^1.1.0: version "1.1.1" @@ -4153,6 +3995,14 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 source-map "^0.5.6" supports-color "^3.2.3" +postcss@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.4.tgz#573acddf73f42ecb24aa618d40ee3d5a7c04a654" + dependencies: + chalk "^2.0.1" + source-map "^0.5.6" + supports-color "^4.0.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -4166,8 +4016,8 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" pretty-error@^2.0.2: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.0.tgz#87f4e9d706a24c87d6cbee9fabec001fcf8c75d8" + version "2.1.1" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" dependencies: renderkid "^2.0.1" utila "~0.4" @@ -4193,18 +4043,25 @@ promise-polyfill@^6.0.2: resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-6.0.2.tgz#d9c86d3dc4dc2df9016e88946defd69b49b41162" promise@^7.0.3, promise@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" 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: version "15.5.8" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394" dependencies: fbjs "^0.8.9" -proxy-addr@~1.1.3: +prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.5.9: + 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" + +proxy-addr@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3" dependencies: @@ -4221,7 +4078,7 @@ ps-tree@^1.0.1: dependencies: event-stream "~3.3.0" -pseudomap@^1.0.1: +pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -4263,11 +4120,11 @@ querystring@0.2.0, querystring@^0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" randomatic@^1.1.3: - version "1.1.6" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + version "1.1.7" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" dependencies: - is-number "^2.0.2" - kind-of "^3.0.2" + is-number "^3.0.0" + kind-of "^4.0.0" range-parser@^1.0.3, range-parser@~1.2.0: version "1.2.0" @@ -4283,25 +4140,25 @@ rc@^1.0.1, rc@^1.1.7: strip-json-comments "~2.0.1" react-addons-create-fragment@^15.0.0: - version "15.5.3" - resolved "https://registry.yarnpkg.com/react-addons-create-fragment/-/react-addons-create-fragment-15.5.3.tgz#d0025675a9e98b2591240382c73b491251066109" + version "15.6.0" + resolved "https://registry.yarnpkg.com/react-addons-create-fragment/-/react-addons-create-fragment-15.6.0.tgz#af91a22b1fb095dd01f1afba43bfd0ef589d8b20" dependencies: fbjs "^0.8.4" + loose-envify "^1.3.1" object-assign "^4.1.0" "react-addons-shallow-compare@^0.14.0 || ^15.0.0": - version "15.5.2" - resolved "https://registry.yarnpkg.com/react-addons-shallow-compare/-/react-addons-shallow-compare-15.5.2.tgz#7cb0ee7acc8d7c93fcc202df0bf47ba916a7bdad" + version "15.6.0" + resolved "https://registry.yarnpkg.com/react-addons-shallow-compare/-/react-addons-shallow-compare-15.6.0.tgz#b7a4e5ff9f2704c20cf686dd8a05dd08b26de252" dependencies: fbjs "^0.8.4" object-assign "^4.1.0" react-addons-transition-group@^15.0.0: - version "15.5.2" - resolved "https://registry.yarnpkg.com/react-addons-transition-group/-/react-addons-transition-group-15.5.2.tgz#da6989d9cffe27dffacaedc862000ffb09ae2206" + version "15.6.0" + resolved "https://registry.yarnpkg.com/react-addons-transition-group/-/react-addons-transition-group-15.6.0.tgz#0f220b9f9597db3a80a88dbd6fe805fc644ce21c" dependencies: - fbjs "^0.8.4" - object-assign "^4.1.0" + react-transition-group "^1.2.0" react-dom@15.3.2: version "15.3.2" @@ -4317,16 +4174,15 @@ 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@*: - version "1.4.0" - resolved "https://registry.yarnpkg.com/react-redux-firebase/-/react-redux-firebase-1.4.0.tgz#e3225622bdb4ecfbddfca5ac5df17d2366ab0384" +react-redux-firebase@2.0.0-alpha.7: + version "2.0.0-alpha.7" + resolved "https://registry.yarnpkg.com/react-redux-firebase/-/react-redux-firebase-2.0.0-alpha.7.tgz#f7978690574c16b27c219d700643c64ce3ad253a" dependencies: - es6-promise "^4.1.0" - firebase "^3.9.0" + firebase "^4.1.3" hoist-non-react-statics "^1.2.0" - immutable "^3.8.1" jwt-decode "^2.2.0" lodash "^4.17.4" + prop-types "^15.5.8" react-redux@^4.4.5: version "4.4.8" @@ -4355,6 +4211,16 @@ react-tap-event-plugin@1.0.0: dependencies: fbjs "^0.2.1" +react-transition-group@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-1.2.0.tgz#b51fc921b0c3835a7ef7c571c79fc82c73e9204f" + dependencies: + chain-function "^1.0.0" + dom-helpers "^3.2.0" + loose-envify "^1.3.1" + prop-types "^15.5.6" + warning "^3.0.0" + react@15.3.2: version "15.3.2" resolved "https://registry.yarnpkg.com/react/-/react-15.3.2.tgz#a7bccd2fee8af126b0317e222c28d1d54528d09e" @@ -4395,15 +4261,15 @@ readable-stream@1.0: string_decoder "~0.10.x" readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.6: - version "2.2.9" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" dependencies: - buffer-shims "~1.0.0" core-util-is "~1.0.0" - inherits "~2.0.1" + inherits "~2.0.3" isarray "~1.0.0" process-nextick-args "~1.0.6" - string_decoder "~1.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" util-deprecate "~1.0.1" readdirp@^2.0.0: @@ -4439,12 +4305,13 @@ recompose@^0.20.2: symbol-observable "^0.2.4" redbox-react@^1.2.10: - version "1.3.6" - resolved "https://registry.yarnpkg.com/redbox-react/-/redbox-react-1.3.6.tgz#70314c57c066257eb70b0a24dc794b5cef4f1c4e" + version "1.4.2" + resolved "https://registry.yarnpkg.com/redbox-react/-/redbox-react-1.4.2.tgz#7fe35d3c567301e97938cc7fd6a10918f424c6b4" dependencies: error-stack-parser "^1.3.6" object-assign "^4.0.1" prop-types "^15.5.4" + sourcemapped-stacktrace "^1.1.6" redent@^1.0.0: version "1.0.0" @@ -4476,8 +4343,8 @@ redux-auth-wrapper@^1.0.0: prop-types "15.5.8" redux-form@^6.6.1: - version "6.6.3" - resolved "https://registry.yarnpkg.com/redux-form/-/redux-form-6.6.3.tgz#62362654f2214c83a8f9fcb8313702bb46f92205" + version "6.8.0" + resolved "https://registry.yarnpkg.com/redux-form/-/redux-form-6.8.0.tgz#ff1b590b59f987d7e3ff080d752f7120bfe42af3" dependencies: deep-equal "^1.0.1" es6-error "^4.0.0" @@ -4486,7 +4353,7 @@ redux-form@^6.6.1: is-promise "^2.1.0" lodash "^4.17.3" lodash-es "^4.17.3" - prop-types "^15.5.6" + prop-types "^15.5.9" redux-logger@^3.0.6: version "3.0.6" @@ -4494,26 +4361,18 @@ redux-logger@^3.0.6: 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" redux@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/redux/-/redux-3.6.0.tgz#887c2b3d0b9bd86eca2be70571c27654c19e188d" + version "3.7.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.1.tgz#bfc535c757d3849562ead0af18ac52122cd7268e" dependencies: lodash "^4.2.1" lodash-es "^4.2.1" loose-envify "^1.1.0" - symbol-observable "^1.0.2" + symbol-observable "^1.0.3" regenerate@^1.2.1: version "1.3.2" @@ -4575,8 +4434,8 @@ relateurl@0.2.x: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" remove-trailing-separator@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4" + version "1.0.2" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511" renderkid@^2.0.1: version "2.0.1" @@ -4693,18 +4552,18 @@ 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: - version "5.0.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" +safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" sass-graph@^2.1.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.2.tgz#f4d6c95b546ea2a09d14176d0fc1a07ee2b48354" + version "2.2.4" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" dependencies: glob "^7.0.0" lodash "^4.0.0" - scss-tokenizer "^0.2.1" - yargs "^6.6.0" + scss-tokenizer "^0.2.3" + yargs "^7.0.0" sass-loader@^4.0.0: version "4.1.1" @@ -4715,12 +4574,12 @@ sass-loader@^4.0.0: object-assign "^4.1.0" sax@~1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828" + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" -scss-tokenizer@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.1.tgz#07c0cc577bb7ab4d08fd900185adbf4bc844141d" +scss-tokenizer@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" dependencies: js-base64 "^2.1.8" source-map "^0.4.2" @@ -4735,11 +4594,11 @@ semver-diff@^2.0.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" -send@0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.15.1.tgz#8a02354c26e6f5cca700065f5f0cdeba90ec7b5f" +send@0.15.3: + version "0.15.3" + resolved "https://registry.yarnpkg.com/send/-/send-0.15.3.tgz#5013f9f99023df50d1bd9892c19e3defd1d53309" dependencies: - debug "2.6.1" + debug "2.6.7" depd "~1.1.0" destroy "~1.0.4" encodeurl "~1.0.1" @@ -4748,19 +4607,19 @@ send@0.15.1: fresh "0.5.0" http-errors "~1.6.1" mime "1.3.4" - ms "0.7.2" + ms "2.0.0" on-finished "~2.3.0" range-parser "~1.2.0" statuses "~1.3.1" -serve-static@1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.1.tgz#7443a965e3ced647aceb5639fa06bf4d1bbe0039" +serve-static@1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.3.tgz#9f4ba19e2f3030c547f8af99107838ec38d5b1e2" dependencies: encodeurl "~1.0.1" escape-html "~1.0.3" parseurl "~1.3.1" - send "0.15.1" + send "0.15.3" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -4783,8 +4642,8 @@ sha.js@2.2.6: resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.2.6.tgz#17ddeddc5f722fb66501658895461977867315ba" shelljs@^0.7.5: - version "0.7.7" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.7.tgz#b2f5c77ef97148f4b4f6e22682e10bba8667cff1" + version "0.7.8" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" dependencies: glob "^7.0.0" interpret "^1.0.0" @@ -4838,7 +4697,7 @@ source-map@0.1.x: dependencies: amdefine ">=0.0.4" -source-map@0.5.x, 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: +source-map@0.5.6, source-map@0.5.x, 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" @@ -4848,6 +4707,12 @@ source-map@^0.4.2, source-map@~0.4.1: dependencies: amdefine ">=0.0.4" +sourcemapped-stacktrace@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/sourcemapped-stacktrace/-/sourcemapped-stacktrace-1.1.6.tgz#112d8749c942c3cd3b630dfac9514577b86a3a51" + dependencies: + source-map "0.5.6" + spdx-correct@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" @@ -4873,8 +4738,8 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" sshpk@^1.7.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c" + version "1.13.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -4883,7 +4748,6 @@ sshpk@^1.7.0: optionalDependencies: bcrypt-pbkdf "^1.0.0" ecc-jsbn "~0.1.1" - jodid25519 "^1.0.0" jsbn "~0.1.0" tweetnacl "~0.14.0" @@ -4909,8 +4773,8 @@ stream-combiner@~0.0.4: duplexer "~0.1.1" stream-http@^2.3.1: - version "2.7.0" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.0.tgz#cec1f4e3b494bc4a81b451808970f8b20b4ed5f6" + version "2.7.2" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" @@ -4941,21 +4805,21 @@ string-width@^1.0.1, string-width@^1.0.2: strip-ansi "^3.0.0" string-width@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" + version "2.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.0.tgz#030664561fc146c9423ec7d978fe2457437fe6d0" dependencies: is-fullwidth-code-point "^2.0.0" - strip-ansi "^3.0.0" + strip-ansi "^4.0.0" string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" -string_decoder@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667" +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" dependencies: - buffer-shims "~1.0.0" + safe-buffer "~5.1.0" stringstream@~0.0.4: version "0.0.5" @@ -4967,6 +4831,12 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -4993,22 +4863,22 @@ style-loader@^0.13.1: dependencies: loader-utils "^1.0.2" -supports-color@3.1.2, supports-color@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" - dependencies: - has-flag "^1.0.0" - supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.2.3: +supports-color@^3.1.0, supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: has-flag "^1.0.0" +supports-color@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.1.0.tgz#92cc14bb3dad8928ca5656c33e19a19f20af5c7a" + dependencies: + has-flag "^2.0.0" + svgo@^0.7.0: version "0.7.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" @@ -5025,7 +4895,7 @@ symbol-observable@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-0.2.4.tgz#95a83db26186d6af7e7a18dbd9760a2f86d08f40" -symbol-observable@^1.0.2: +symbol-observable@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" @@ -5155,7 +5025,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-is@~1.6.14: +type-is@~1.6.15: version "1.6.15" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" dependencies: @@ -5167,8 +5037,15 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 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" + version "0.7.13" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.13.tgz#cd9dd2f86493b3f44dbeeef3780fda74c5ee14be" + +uglify-js@3.0.x: + version "3.0.23" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.0.23.tgz#a58c6b97e6d6763d94dbc265fe8e8c1725e64666" + dependencies: + commander "~2.9.0" + source-map "~0.5.1" uglify-js@~2.7.3: version "2.7.5" @@ -5179,15 +5056,6 @@ uglify-js@~2.7.3: uglify-to-browserify "~1.0.0" yargs "~3.10.0" -uglify-js@~2.8.22: - version "2.8.22" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.22.tgz#d54934778a8da14903fa29a326fb24c0ab51a1a0" - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" - uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" @@ -5235,8 +5103,8 @@ upper-case@^1.1.1: resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" url-loader@^0.5.6: - version "0.5.8" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.8.tgz#b9183b1801e0f847718673673040bc9dc1c715c5" + version "0.5.9" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.9.tgz#cc8fea82c7b906e7777019250869e569e995c295" dependencies: loader-utils "^1.0.2" mime "1.3.x" @@ -5276,13 +5144,13 @@ utils-merge@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" -uuid@^2.0.1, uuid@^2.0.3: +uuid@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" uuid@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" validate-npm-package-license@^3.0.1: version "3.0.1" @@ -5291,7 +5159,7 @@ validate-npm-package-license@^3.0.1: spdx-correct "~1.0.0" spdx-expression-parse "~1.0.0" -vary@~1.1.0: +vary@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" @@ -5416,10 +5284,10 @@ which@1, which@^1.2.9: isexe "^2.0.0" wide-align@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" + version "1.1.2" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" dependencies: - string-width "^1.0.1" + string-width "^1.0.2" window-size@0.1.0: version "0.1.0" @@ -5484,7 +5352,7 @@ y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" -yallist@^2.0.0: +yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -5494,7 +5362,13 @@ yargs-parser@^4.2.0: dependencies: camelcase "^3.0.0" -yargs@^6.3.0, yargs@^6.6.0: +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + dependencies: + camelcase "^3.0.0" + +yargs@^6.3.0: version "6.6.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" dependencies: @@ -5512,6 +5386,24 @@ yargs@^6.3.0, yargs@^6.6.0: y18n "^3.2.1" yargs-parser "^4.2.0" +yargs@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + 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 "^5.0.0" + yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" diff --git a/package.json b/package.json index a16cd3908..1c48684ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-redux-firebase", - "version": "2.0.0-alpha.7", + "version": "2.0.0-beta", "description": "Redux integration for Firebase. Comes with a Higher Order Component for use with React.", "main": "lib/index.js", "module": "es/index.js", @@ -56,7 +56,6 @@ "redux-react-firebase" ], "dependencies": { - "firebase": "^4.1.3", "hoist-non-react-statics": "^1.2.0", "jwt-decode": "^2.2.0", "lodash": "^4.17.4", @@ -96,6 +95,7 @@ "eslint-plugin-promise": "^3.5.0", "eslint-plugin-react": "^6.10.3", "eslint-plugin-standard": "^2.2.0", + "firebase": "^4.1.3", "firebase-server": "^0.10.1", "gitbook-cli": "^2.3.0", "istanbul": "^1.1.0-alpha.1", diff --git a/src/actions/auth.js b/src/actions/auth.js index 995b06dd6..836970eb8 100644 --- a/src/actions/auth.js +++ b/src/actions/auth.js @@ -1,45 +1,14 @@ +import jwtDecode from 'jwt-decode' import { omit, isArray, isString, isFunction, - forEach, - set, - get, - map, - mapValues + forEach } from 'lodash' -import jwtDecode from 'jwt-decode' import { actionTypes, defaultJWTProps } from '../constants' import { getLoginMethodAndParams } from '../utils/auth' -import { - promisesForPopulate, - getPopulateObjs, - getChildType -} from '../utils/populate' - -const { - SET, - SET_PROFILE, - LOGIN, - LOGOUT, - LOGIN_ERROR, - UNAUTHORIZED_ERROR, - AUTHENTICATION_INIT_STARTED, - AUTHENTICATION_INIT_FINISHED -} = actionTypes - -/** - * @description Dispatch login error action - * @param {Function} dispatch - Action dispatch function - * @param {Object} authError - Error object - * @private - */ -export const dispatchLoginError = (dispatch, authError) => - dispatch({ - type: LOGIN_ERROR, - authError - }) +import { promisesForPopulate } from '../utils/populate' /** * @description Dispatch login error action @@ -47,25 +16,12 @@ export const dispatchLoginError = (dispatch, authError) => * @param {Object} authError - Error object * @private */ -export const dispatchUnauthorizedError = (dispatch, authError) => +const dispatchLoginError = (dispatch, authError) => dispatch({ - type: UNAUTHORIZED_ERROR, + type: actionTypes.LOGIN_ERROR, authError }) -/** - * @description Dispatch login action - * @param {Function} dispatch - Action dispatch function - * @param {Object} auth - Auth data object - * @private - */ -export const dispatchLogin = (dispatch, auth) => - dispatch({ - type: LOGIN, - auth, - authError: null - }) - /** * @description Remove listener from user profile * @param {Object} firebase - Internal firebase object @@ -84,7 +40,7 @@ export const unWatchUserProfile = (firebase) => { } /** - * @description Watch user profile + * @description Watch user profile. Internally dispatches SET_PROFILE actions * @param {Function} dispatch - Action dispatch function * @param {Object} firebase - Internal firebase object * @private @@ -101,81 +57,31 @@ export const watchUserProfile = (dispatch, firebase) => { .on('value', snap => { const { profileParamsToPopulate, - autoPopulateProfile, - setProfilePopulateResults + autoPopulateProfile } = firebase._.config if (!profileParamsToPopulate || (!isArray(profileParamsToPopulate) && !isString(profileParamsToPopulate))) { - dispatch({ - type: SET_PROFILE, - profile: snap.val() - }) + dispatch({ type: actionTypes.SET_PROFILE, profile: snap.val() }) } else { + // TODO: Share population logic with query action // Convert each populate string in array into an array of once query promises promisesForPopulate(firebase, snap.val(), profileParamsToPopulate) .then(data => { + // Fire actions for placement of data gathered in populate into redux + forEach(data, (result, path) => { + dispatch({ + type: actionTypes.SET, + path, + data: result, + timestamp: Date.now(), + requesting: false, + requested: true + }) + }) + dispatch({ type: actionTypes.SET_PROFILE, profile: snap.val() }) // Dispatch action with profile combined with populated parameters // Auto Populate profile if (autoPopulateProfile) { - const populates = getPopulateObjs(profileParamsToPopulate) - const profile = snap.val() - forEach(populates, (p) => { - const child = get(profile, p.child) - const childType = getChildType(child) - let populatedChild - - switch (childType) { - case 'object': - populatedChild = mapValues( - child, - (value, key) => { - if (value) { // Only populate keys with truthy values - return get(data, `${p.root}.${key}`) - } - return value - }) - break - - case 'string': - populatedChild = get(data, `${p.root}.${child}`) - break - - case 'array': - populatedChild = map( - child, - (key) => get(data, `${p.root}.${key}`) - ) - break - - default: - populatedChild = child - } - // Overwrite the child value with the populated child - set(profile, p.child, populatedChild) - }) - dispatch({ - type: SET_PROFILE, - profile - }) - } else { - // dispatch with unpopulated profile data - dispatch({ - type: SET_PROFILE, - profile: snap.val() - }) - } - - // Fire actions for placement of data gathered in populate into redux - if (setProfilePopulateResults) { - forEach(data, (result, path) => { - dispatch({ - type: SET, - path, - data: result, - timestamp: Date.now(), - requesting: false, - requested: true - }) - }) + console.warn('Auto populate is no longer supported. We are working on backwards compatibility for v2.0.0') // eslint-disable-line no-console } }) } @@ -194,19 +100,13 @@ export const watchUserProfile = (dispatch, firebase) => { * @private */ export const createUserProfile = (dispatch, firebase, userData, profile) => { - if (!firebase._.config.userProfile) { + const { database, _: { config } } = firebase + if (!config.userProfile) { return Promise.resolve(userData) } - const { database, _: { config } } = firebase if (isFunction(config.profileFactory)) { profile = config.profileFactory(userData, profile) } - if (isFunction(config.profileDecorator)) { - if (isFunction(console.warn)) { // eslint-disable-line no-console - console.warn('profileDecorator is Depreceated and will be removed in future versions. Please use profileFactory.') // eslint-disable-line no-console - } - profile = config.profileDecorator(userData, profile) - } // Check for user's profile at userProfile path if provided return database() .ref() @@ -216,29 +116,64 @@ export const createUserProfile = (dispatch, firebase, userData, profile) => { // update profile only if doesn't exist or if set by config !config.updateProfileOnLogin && profileSnap.val() !== null ? profileSnap.val() - : profileSnap.ref.update(profile) // Update the profile - .then(() => profile) - .catch(err => { - // Error setting profile - dispatchUnauthorizedError(dispatch, err) - return Promise.reject(err) - }) + : profileSnap.ref.update(profile).then(() => profile) // Update the profile ) .catch(err => { // Error reading user profile - dispatchUnauthorizedError(dispatch, err) + dispatch({ type: actionTypes.UNAUTHORIZED_ERROR, authError: err }) return Promise.reject(err) }) } +/** + * @description Start presence management for a specificed user uid. + * Presence collection contains a list of users that are online currently. + * Sessions collection contains a record of all user sessions. + * This function is called within login functions if enablePresence: true. + * @param {Function} dispatch - Action dispatch function + * @param {Object} firebase - Internal firebase object + * @return {Promise} + * @private + */ +const setupPresence = (dispatch, firebase) => { + // TODO: Enable more settings here (enable/disable sessions, store past sessions) + const ref = firebase.database().ref() + const { config: { presence, sessions }, authUid } = firebase._ + let amOnline = ref.child('.info/connected') + let onlineRef = ref.child(presence).child(authUid) + let sessionsRef = ref.child(sessions) + amOnline.on('value', snapShot => { + if (!snapShot.val()) return + // user is online + // add session and set disconnect + dispatch({ type: actionTypes.SESSION_START, payload: authUid }) + // add new session to sessions collection + const session = sessionsRef.push({ + startedAt: firebase.database.ServerValue.TIMESTAMP, + user: authUid + }) + // set authUid as priority for easy sorting + session.setPriority(authUid) + const endedRef = session.child('endedAt') + endedRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP, () => { + dispatch({ type: actionTypes.SESSION_END }) + }) + // add correct session id to user + // remove from presence list + onlineRef.set(true) + onlineRef.onDisconnect().remove() + }) +} + /** * @description Initialize authentication state change listener that * watches user profile and dispatches login action * @param {Function} dispatch - Action dispatch function + * @param {Object} firebase - Internal firebase object * @private */ export const init = (dispatch, firebase) => { - dispatch({ type: AUTHENTICATION_INIT_STARTED }) + dispatch({ type: actionTypes.AUTHENTICATION_INIT_STARTED }) firebase.auth().onAuthStateChanged(authData => { if (!authData) { @@ -246,13 +181,16 @@ export const init = (dispatch, firebase) => { if (isFunction(firebase._.config.onAuthStateChanged) && firebase._.config.enableEmptyAuthChanges) { firebase._.config.onAuthStateChanged(authData, firebase, dispatch) } - return dispatch({ type: LOGOUT }) + return dispatch({ type: actionTypes.LOGOUT }) } firebase._.authUid = authData.uid + if (firebase._.config.presence) { + setupPresence(dispatch, firebase) + } watchUserProfile(dispatch, firebase) - dispatchLogin(dispatch, authData) + dispatch({ type: actionTypes.LOGIN, auth: authData }) // Run onAuthStateChanged if it exists in config if (isFunction(firebase._.config.onAuthStateChanged)) { @@ -274,7 +212,7 @@ export const init = (dispatch, firebase) => { firebase._.authUid = user.uid watchUserProfile(dispatch, firebase) - dispatchLogin(dispatch, user) + dispatch({ type: actionTypes.LOGIN, auth: user }) createUserProfile( dispatch, @@ -296,7 +234,7 @@ export const init = (dispatch, firebase) => { firebase.auth().currentUser // eslint-disable-line no-unused-expressions - dispatch({ type: AUTHENTICATION_INIT_FINISHED }) + dispatch({ type: actionTypes.AUTHENTICATION_INIT_FINISHED }) } /** @@ -370,7 +308,10 @@ export const logout = (dispatch, firebase) => firebase.auth() .signOut() .then(() => { - dispatch({ type: LOGOUT }) + dispatch({ + type: actionTypes.LOGOUT, + preserve: firebase._.config.preserveOnLogout + }) firebase._.authUid = null unWatchUserProfile(firebase) return firebase @@ -516,11 +457,10 @@ export const verifyPasswordResetCode = (dispatch, firebase, code) => { * @private */ export const updateProfile = (dispatch, firebase, profileUpdate) => { + dispatch({ type: actionTypes.PROFILE_UPDATE_START, payload: profileUpdate }) + const { database, _: { config, authUid } } = firebase - dispatch({ - type: actionTypes.PROFILE_UPDATE_START, - payload: profileUpdate - }) + return database() .ref(`${config.userProfile}/${authUid}`) .update(profileUpdate) @@ -529,12 +469,11 @@ export const updateProfile = (dispatch, firebase, profileUpdate) => { type: actionTypes.PROFILE_UPDATE_SUCCESS, payload: snap.val() }) + return snap.val() }) .catch((payload) => { - dispatch({ - type: actionTypes.PROFILE_UPDATE_ERROR, - payload - }) + dispatch({ type: actionTypes.PROFILE_UPDATE_ERROR, payload }) + return Promise.reject(payload) }) } @@ -548,18 +487,14 @@ export const updateProfile = (dispatch, firebase, profileUpdate) => { * @private */ export const updateAuth = (dispatch, firebase, authUpdate, updateInProfile) => { - dispatch({ - type: actionTypes.AUTH_UPDATE_START, - payload: authUpdate - }) + 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 - }) + dispatch({ type: actionTypes.AUTH_UPDATE_ERROR, payload: msg }) return Promise.reject(msg) } + return firebase.auth().currentUser .updateProfile(authUpdate) .then((payload) => { @@ -573,10 +508,8 @@ export const updateAuth = (dispatch, firebase, authUpdate, updateInProfile) => { return payload }) .catch((payload) => { - dispatch({ - type: actionTypes.AUTH_UPDATE_ERROR, - payload - }) + dispatch({ type: actionTypes.AUTH_UPDATE_ERROR, payload }) + return Promise.reject(payload) }) } @@ -590,53 +523,25 @@ export const updateAuth = (dispatch, firebase, authUpdate, updateInProfile) => { * @private */ export const updateEmail = (dispatch, firebase, newEmail, updateInProfile) => { - dispatch({ - type: actionTypes.EMAIL_UPDATE_START, - payload: newEmail - }) + 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 - }) + 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 - }) + 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 - }) + dispatch({ type: actionTypes.EMAIL_UPDATE_ERROR, payload }) + return Promise.reject(payload) }) } - -export default { - dispatchLoginError, - dispatchUnauthorizedError, - dispatchLogin, - unWatchUserProfile, - watchUserProfile, - init, - createUserProfile, - login, - logout, - createUser, - resetPassword, - confirmPasswordReset, - verifyPasswordResetCode, - updateAuth, - updateProfile, - updateEmail -} diff --git a/src/actions/query.js b/src/actions/query.js index 746b1adf6..ce2284027 100644 --- a/src/actions/query.js +++ b/src/actions/query.js @@ -9,7 +9,13 @@ import { getQueryIdFromPath } from '../utils/query' -const { START, SET, NO_VALUE, UNAUTHORIZED_ERROR, ERROR } = actionTypes +const { + START, + SET, + NO_VALUE, + UNAUTHORIZED_ERROR, + ERROR +} = actionTypes /** * @description Watch a specific event type @@ -33,7 +39,7 @@ export const watchEvent = (firebase, dispatch, { type, path, populates, queryPar } } - setWatcher(firebase, type, watchPath, queryId) + setWatcher(firebase, dispatch, type, watchPath, queryId) if (type === 'first_child') { return firebase.database() @@ -45,9 +51,6 @@ export const watchEvent = (firebase, dispatch, { type, path, populates, queryPar if (snapshot.val() === null) { dispatch({ type: NO_VALUE, - timestamp: Date.now(), - requesting: false, - requested: true, path: storeAs || path }) } @@ -60,6 +63,7 @@ export const watchEvent = (firebase, dispatch, { type, path, populates, queryPar // }) dispatch({ type: ERROR, + path: storeAs || path, payload: err }) }) @@ -72,22 +76,21 @@ export const watchEvent = (firebase, dispatch, { type, path, populates, queryPar } const runQuery = (q, e, p, params) => { - dispatch({ - type: START, - timestamp: Date.now(), - requesting: true, - requested: false, - path: storeAs || path - }) + dispatch({ type: START, path: storeAs || path }) // Handle once queries if (e === 'once') { return q.once('value') .then(snapshot => { if (snapshot.val() !== null) { + const ordered = [] + snapshot.forEach((child) => { + ordered.push({ key: child.key, ...child.val() }) + }) dispatch({ type: SET, path: storeAs || path, + ordered: size(ordered) ? ordered : undefined, data: snapshot.val() }) } @@ -106,61 +109,51 @@ export const watchEvent = (firebase, dispatch, { type, path, populates, queryPar let data = (e === 'child_removed') ? undefined : snapshot.val() const resultPath = storeAs || (e === 'value') ? p : `${p}/${snapshot.key}` + // create an array for preserving order of children under ordered + const ordered = [] + if (e === 'child_added') { + ordered.push({ key: snapshot.key, ...snapshot.val() }) + } else { + snapshot.forEach((child) => { + ordered.push({ key: child.key, ...child.val() }) + }) + } + // Dispatch standard event if no populates exists if (!populates) { - const ordered = [] - // preserve order of children under ordered - if (e === 'child_added') { - ordered.push({ key: snapshot.key, ...snapshot.val() }) - } else { - snapshot.forEach((child) => { - ordered.push({ key: child.key, ...child.val() }) - }) - } - return dispatch({ type: SET, path: storeAs || resultPath, - ordered: size(ordered) ? ordered : undefined, data, - timestamp: Date.now(), + ordered: size(ordered) ? ordered : undefined, requesting: false, requested: true }) } // 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 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) + // populate after all data is in redux (Issue #121) forEach(results, (result, path) => { dispatch({ type: SET, path, - data: result, - timestamp: Date.now(), - requesting: false, - requested: true + data: result }) }) dispatch({ type: SET, path: storeAs || resultPath, data, - timestamp: Date.now(), - requesting: false, - requested: true + ordered: size(ordered) ? ordered : undefined }) }) }, (err) => { - dispatch({ - type: UNAUTHORIZED_ERROR, - payload: err - }) + dispatch({ type: UNAUTHORIZED_ERROR, payload: err }) }) } @@ -173,7 +166,7 @@ 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, { type, path, storeAs, queryId = undefined }) => { +export const unWatchEvent = (firebase, dispatch, { type, path, storeAs, queryId }) => { const watchPath = !storeAs ? path : `${path}@${storeAs}` unsetWatcher(firebase, dispatch, type, watchPath, queryId) } diff --git a/src/actions/storage.js b/src/actions/storage.js index bdd84a357..8f9a72e23 100644 --- a/src/actions/storage.js +++ b/src/actions/storage.js @@ -23,21 +23,20 @@ const { * @private */ export const uploadFileWithProgress = (dispatch, firebase, { path, file }) => { - dispatch({ - type: FILE_UPLOAD_START, - payload: { path, file } - }) + dispatch({ type: FILE_UPLOAD_START, payload: { path, file } }) const uploadEvent = firebase.storage().ref(`${path}/${file.name}`).put(file) // TODO: Allow config to control whether progress it set to state or not const unListen = uploadEvent.on( firebase.storage.TaskEvent.STATE_CHANGED, { next: (snapshot) => { - const percent = Math.floor(snapshot.bytesTransferred / snapshot.totalBytes * 100) dispatch({ type: FILE_UPLOAD_PROGRESS, path, - payload: { snapshot, percent } + payload: { + snapshot, + percent: Math.floor(snapshot.bytesTransferred / snapshot.totalBytes * 100) + } }) }, error: (err) => { @@ -62,6 +61,8 @@ export const uploadFileWithProgress = (dispatch, firebase, { path, file }) => { * @param {String} opts.path - Location within Firebase Stroage at which to upload files. * @param {Blob} opts.file - File Blob to be uploaded * @param {String} opts.dbPath - Datbase path to write file meta data to + * @return {Promise} Resolves with uploadFileWithProgress response. If dbPath + * is included, object with snapshot, key and File is returned. * @private */ export const uploadFile = (dispatch, firebase, { path, file, dbPath }) => diff --git a/src/compose.js b/src/compose.js index 7e72c9e70..ce43ada9b 100644 --- a/src/compose.js +++ b/src/compose.js @@ -1,7 +1,5 @@ -import * as firebase from 'firebase' import { createFirebaseInstance } from './createFirebaseInstance' import { defaultConfig } from './constants' -import { validateConfig } from './utils' import { authActions } from './actions' let firebaseInstance @@ -56,55 +54,41 @@ let firebaseInstance * @example Setup * import { createStore, compose } from 'redux' * import { reactReduxFirebase } from 'react-redux-firebase' + * import * as firebase from 'firebase' * // React Redux Firebase Config * const config = { * userProfile: 'users', // saves user profiles to '/users' on Firebase * // here is where you place other config options * } + + * // initialize script from Firebase page + * const fbConfg = {} // firebase config object + * firebase.initializeApp(fbConfig) * * // Add react-redux-firebase to compose * // Note: In full projects this will often be within createStore.js or store.js * const createStoreWithFirebase = compose( - * reactReduxFirebase(fbConfig, config), + * reactReduxFirebase(firebase, config), * )(createStore) * * // Use Function later to create store * const store = createStoreWithFirebase(rootReducer, initialState) - * @example Custom Auth Parameters - * // Follow Setup example with the following config: - * const config = { - * customAuthParameters: { - * google: { - * // prompts user to select account on every google login - * prompt: 'select_account' - * } - * } - * } */ export default (fbConfig, otherConfig) => next => (reducer, initialState, middleware) => { const store = next(reducer, initialState, middleware) - const { dispatch } = store - - // handle firebase instance being passed in as first argument - if (typeof fbConfig.database === 'function') { - firebaseInstance = createFirebaseInstance(fbConfig, otherConfig, dispatch) - } else { - // Combine all configs - const configs = Object.assign({}, defaultConfig, fbConfig, otherConfig) - validateConfig(configs) - - // Initialize Firebase - try { - firebase.initializeApp(fbConfig) - } catch (err) {} // silence reinitialize warning (hot-reloading) - - firebaseInstance = createFirebaseInstance(firebase, configs, dispatch) + // firebase instance not being passed in as first argument + if (typeof fbConfig.database !== 'function') { + throw new Error('v2.0.0-beta and higher require passing a firebase instance. View the migration guide for details.') } - authActions.init(dispatch, firebaseInstance) + const configs = { ...defaultConfig, ...otherConfig } + // validateConfig(configs) + firebaseInstance = createFirebaseInstance(fbConfig, configs, store.dispatch) + + authActions.init(store.dispatch, firebaseInstance) store.firebase = firebaseInstance return store diff --git a/src/constants.js b/src/constants.js index 8b2b9ded0..7dd151892 100644 --- a/src/constants.js +++ b/src/constants.js @@ -13,16 +13,18 @@ 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` * @property {String} LOGIN_ERROR - `@@reactReduxFirebase/LOGIN_ERROR` * @property {String} NO_VALUE - `@@reactReduxFirebase/NO_VALUE` * @property {String} UNAUTHORIZED_ERROR - `@@reactReduxFirebase/UNAUTHORIZED_ERROR` + * @property {String} SET_LISTENER - `@@reactReduxFirebase/SET_LISTENER` * @property {String} UNSET_LISTENER - `@@reactReduxFirebase/UNSET_LISTENER` * @property {String} AUTHENTICATION_INIT_STARTED - `@@reactReduxFirebase/AUTHENTICATION_INIT_STARTED` * @property {String} AUTHENTICATION_INIT_FINISHED - `@@reactReduxFirebase/AUTHENTICATION_INIT_FINISHED` + * @property {String} SESSION_START - `@@reactReduxFirebase/SESSION_START` + * @property {String} SESSION_END - `@@reactReduxFirebase/SESSION_END` * @property {String} FILE_UPLOAD_START - `@@reactReduxFirebase/FILE_UPLOAD_START` * @property {String} FILE_UPLOAD_ERROR - `@@reactReduxFirebase/FILE_UPLOAD_ERROR` * @property {String} FILE_UPLOAD_PROGRESS - `@@reactReduxFirebase/FILE_UPLOAD_PROGRESS` @@ -53,9 +55,12 @@ export const actionTypes = { NO_VALUE: `${actionsPrefix}/NO_VALUE`, UNAUTHORIZED_ERROR: `${actionsPrefix}/UNAUTHORIZED_ERROR`, ERROR: `${actionsPrefix}/ERROR`, + SET_LISTENER: `${actionsPrefix}/SET_LISTENER`, UNSET_LISTENER: `${actionsPrefix}/UNSET_LISTENER`, AUTHENTICATION_INIT_STARTED: `${actionsPrefix}/AUTHENTICATION_INIT_STARTED`, AUTHENTICATION_INIT_FINISHED: `${actionsPrefix}/AUTHENTICATION_INIT_FINISHED`, + SESSION_START: `${actionsPrefix}/SESSION_START`, + SESSION_END: `${actionsPrefix}/SESSION_END`, FILE_UPLOAD_START: `${actionsPrefix}/FILE_UPLOAD_START`, FILE_UPLOAD_ERROR: `${actionsPrefix}/FILE_UPLOAD_ERROR`, FILE_UPLOAD_PROGRESS: `${actionsPrefix}/FILE_UPLOAD_PROGRESS`, @@ -78,6 +83,11 @@ export const actionTypes = { * @description Default configuration options * @property {String} userProfile - `null` Location on Firebase where user * profiles are stored. Often set to `'users'`. + * @property {String} presence - `null` Location on Firebase where of currently + * online users is stored. Often set to `'presence'` or `'onlineUsers'`. + * @property {String} sessions - `sessions` Location on Firebase where user + * sessions are stored (only if presense is set). Often set to `'presence'` or + * `'onlineUsers'`. * @property {Boolean} enableLogging - `false` Whether or not firebase * database logging is enabled. * @property {Boolean} updateProfileOnLogin - `true` Whether or not to update @@ -90,7 +100,7 @@ export const actionTypes = { * 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 + * @property {Boolean} autoPopulateProfile - `false` REMOVED FROM v2.0.0. Whether or not to * automatically populate profile with data loaded through * profileParamsToPopulate config. * @property {Boolean} setProfilePopulateResults - `true` Whether or not to @@ -106,12 +116,14 @@ export const actionTypes = { */ export const defaultConfig = { userProfile: null, + presence: null, + sessions: 'sessions', enableLogging: false, updateProfileOnLogin: true, enableRedirectHandling: true, - autoPopulateProfile: true, + autoPopulateProfile: false, setProfilePopulateResults: false, - dispatchOnUnsetListener: false, + dispatchOnUnsetListener: true, enableEmptyAuthChanges: false } diff --git a/src/createFirebaseInstance.js b/src/createFirebaseInstance.js index 5dcf4f011..c2b605ba5 100644 --- a/src/createFirebaseInstance.js +++ b/src/createFirebaseInstance.js @@ -1,6 +1,13 @@ import { isObject } from 'lodash' import { authActions, queryActions, storageActions } from './actions' +/** + * Create a firebase instance that has helpers attached for dispatching actions + * @param {Object} firebase - Firebase instance which to extend + * @param {Object} configs - Configuration object + * @param {Function} dispatch - Action dispatch function + * @return {Object} Extended Firebase instance + */ export const createFirebaseInstance = (firebase, configs, dispatch) => { // Enable Logging based on config if (configs.enableLogging) { @@ -335,25 +342,8 @@ export const createFirebaseInstance = (firebase, configs, dispatch) => { * @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} - */ - const helpers = { ref: path => firebase.database().ref(path), - storage: () => firebase.storage(), set, setWithMeta, uniqueSet, diff --git a/src/helpers.js b/src/helpers.js index 0d2acf17f..d5afb8645 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -12,6 +12,7 @@ import { defaultsDeep, isString, compact, + some, isFunction } from 'lodash' import { getPopulateObjs } from './utils/populate' @@ -49,13 +50,10 @@ import { getPopulateObjs } from './utils/populate' * } * } */ -export const isLoaded = function () { - if (!arguments || !arguments.length) { - return true - } - - return map(arguments, a => a !== undefined).reduce((a, b) => a && b) -} +export const isLoaded = (...args) => + !args || !args.length + ? true + : every(args, arg => arg !== undefined && (get(arg, 'isLoaded') !== false)) /** * @description Detect whether items are empty or not @@ -90,7 +88,8 @@ export const isLoaded = function () { * } * } */ -export const isEmpty = data => !(data && size(data)) +export const isEmpty = (...args) => + some(args, (arg) => !(arg && size(arg)) || arg.isEmpty === true) /** * @private @@ -147,50 +146,62 @@ export const buildChildList = (state, list, p) => */ export const populate = (state, path, populates, notSetValue) => { // TODO: Handle slash and lodash notation + // TODO: Handle populating profile const pathArr = compact(path.split('/')) const dotPath = pathArr.join('.') + const data = pathArr.indexOf('profile') === -1 ? get(state, 'data') : state // Handle undefined child - if (!state || !get(state.data, dotPath)) { + if (!state || !get(data, dotPath)) { return notSetValue } // 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, '/')), get(state.data, dotPath)) - : populates) + const populatesForData = getPopulateObjs( + isFunction(populates) + ? populates(last(split(path, '/')), get(data, dotPath)) + : populates + ) const dataHasPopluateChilds = every(populatesForData, (populate) => ( - has(get(state.data, dotPath), populate.child) + has(get(data, dotPath), populate.child) )) if (dataHasPopluateChilds) { // Data is a single object, resolve populates directly return reduce( map(populatesForData, (p, obj) => { // populate child is key - if (isString(get(get(state.data, dotPath), p.child))) { - const key = get(get(state.data, dotPath), p.child) + if (isString(get(get(data, dotPath), p.child))) { + const key = get(get(data, dotPath), p.child) const pathString = p.childParam ? `${p.root}.${key}.${p.childParam}` : `${p.root}.${key}` - if (get(state.data, pathString)) { + if (get(data, pathString)) { return set({}, p.child, p.keyProp - ? { [p.keyProp]: key, ...get(state.data, pathString) } - : get(state.data, pathString) + ? { [p.keyProp]: key, ...get(data, pathString) } + : get(data, pathString) ) } // matching child does not exist - return get(state.data, dotPath) + return get(data, dotPath) } - return set({}, p.child, buildChildList(state, get(get(state.data, dotPath), p.child), p)) + return set({}, p.child, buildChildList(state, get(get(data, dotPath), p.child), p)) }), // combine data from all populates to one object starting with original data - (obj, v) => defaultsDeep(v, obj), get(state.data, dotPath)) + (obj, v) => defaultsDeep(v, obj), get(data, dotPath) + ) } else { + // TODO: Improve this logic + // Handle non-existant profile populate child + if (pathArr.indexOf('profile') !== -1) { + return get(data, dotPath) + } // Data is a map of objects, each value has parameters to be populated - return mapValues(get(state.data, dotPath), (child, childKey) => { - const populatesForDataItem = getPopulateObjs(isFunction(populates) - ? populates(childKey, child) - : populates) + return mapValues(get(data, dotPath), (child, childKey) => { + const populatesForDataItem = getPopulateObjs( + isFunction(populates) + ? populates(childKey, child) + : populates + ) const resolvedPopulates = map(populatesForDataItem, (p, obj) => { // no matching child parameter if (!child || !get(child, p.child)) { @@ -203,9 +214,9 @@ export const populate = (state, path, populates, notSetValue) => { const pathString = p.childParam ? `${p.root}.${key}.${p.childParam}` : `${p.root}.${key}` - if (get(state.data, pathString)) { + if (get(data, pathString)) { return set({}, p.child, (p.keyProp - ? { [p.keyProp]: key, ...get(state.data, pathString) } + ? { [p.keyProp]: key, ...get(data, pathString) } : get(state.data, pathString) )) } @@ -215,7 +226,6 @@ export const populate = (state, path, populates, notSetValue) => { // populate child list return set({}, p.child, buildChildList(state, get(child, p.child), p)) }) - // combine data from all populates to one object starting with original data return reduce( resolvedPopulates, diff --git a/src/reducer.js b/src/reducer.js index 0b7bf712a..a782ea18d 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -1,94 +1,198 @@ import { combineReducers } from 'redux' -import { set } from 'lodash' +import { set, pick, omit } from 'lodash' import { actionTypes } from './constants' const { START, SET, - SET_ORDERED, SET_PROFILE, LOGIN, LOGOUT, LOGIN_ERROR, NO_VALUE, - // UNSET_LISTENER, + SET_LISTENER, + UNSET_LISTENER, AUTHENTICATION_INIT_STARTED, AUTHENTICATION_INIT_FINISHED, UNAUTHORIZED_ERROR, AUTH_UPDATE_SUCCESS } = actionTypes +/** + * Create a path array from path string + * @param {String} path - Path seperated with slashes + * @return {Array} Path as Array + * @private + */ const pathToArr = path => path ? path.split(/\//).filter(p => !!p) : [] /** - * Reducer for requesting state. Changed by `START` and `SET` actions. - * @param {Object} state - Current requesting redux state + * Trim leading slash from path for use with state + * @param {String} path - Path seperated with slashes + * @return {String} Path seperated with dots + * @private + */ +const getSlashStrPath = path => pathToArr(path).join('/') + +/** + * Convert path with slashes to dot seperated path (for use with lodash get/set) + * @param {String} path - Path seperated with slashes + * @return {String} Path seperated with dots + * @private + */ +const getDotStrPath = path => pathToArr(path).join('.') + +/** + * Reducer for isInitializing state. Changed by `AUTHENTICATION_INIT_STARTED` + * and `AUTHENTICATION_INIT_FINISHED` actions. + * @param {Object} [state=false] - Current isInitializing redux state * @param {object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched * @return {Object} Profile state after reduction */ -const requestingReducer = (state = {}, action) => { - const { path, requesting } = action +export const isInitializingReducer = (state = false, action) => { switch (action.type) { + case AUTHENTICATION_INIT_STARTED: + return true + case AUTHENTICATION_INIT_FINISHED: + return false + default: + return state + } +} + +/** + * Reducer for requesting state.Changed by `START`, `NO_VALUE`, and `SET` actions. + * @param {Object} [state={}] - Current requesting redux state + * @param {Object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched + * @param {String} action.path - Path of action that was dispatched + * @return {Object} Profile state after reduction + */ +export const requestingReducer = (state = {}, { type, path }) => { + switch (type) { case START: + return { + ...state, + [getSlashStrPath(path)]: true + } + case NO_VALUE: case SET: return { ...state, - [pathToArr(path).join('/')]: requesting + [getSlashStrPath(path)]: false } - // TODO: Handle NO_VALUE case - // case NO_VALUE: default: return state } } -const getPathStr = (path) => path ? path.replace('/', '.') : '' +/** + * Reducer for requested state. Changed by `START`, `NO_VALUE`, and `SET` actions. + * @param {Object} [state={}] - Current requested redux state + * @param {Object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched + * @param {String} action.path - Path of action that was dispatched + * @return {Object} Profile state after reduction + */ +export const requestedReducer = (state = {}, { type, path }) => { + switch (type) { + case START: + return { + ...state, + [getSlashStrPath(path)]: false + } + case NO_VALUE: + case SET: + return { + ...state, + [getSlashStrPath(path)]: true + } + default: + return state + } +} /** - * 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 + * Reducer for timestamps state. Changed by `START`, `NO_VALUE`, and `SET` actions. + * @param {Object} [state={}] - Current timestamps redux state + * @param {Object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched + * @param {String} action.path - Path of action that was dispatched * @return {Object} Profile state after reduction */ -const dataReducer = (state = {}, action) => { - const { path, data, ordered } = action - switch (action.type) { +export const timestampsReducer = (state = {}, { type, path }) => { + switch (type) { + case START: + case NO_VALUE: case SET: return { ...state, - ...set({}, getPathStr(path), data) + [getSlashStrPath(path)]: Date.now() } - case SET_ORDERED: + default: + return state + } +} + +/** + * Creates reducer for data state. Used to create data and ordered reducers. + * Changed by `SET` or `SET_ORDERED` (if actionKey === 'ordered'),`NO_VALUE`, + * and `LOGOUT` actions. + * @param {Object} [state={}] - Current data redux state + * @param {Object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched + * @param {String} action.path - Path of action that was dispatched + * @return {Object} Data state after reduction + * @private + */ +const createDataReducer = (actionKey = 'data') => (state = {}, action) => { + switch (action.type) { + case SET: return { ...state, - ...set({}, getPathStr(path), ordered) + ...set({}, getDotStrPath(action.path), action[actionKey]) } case NO_VALUE: return { ...state, - ...set({}, getPathStr(path), {}) + ...set({}, getDotStrPath(action.path), null) + } + case LOGOUT: + // support keeping data when logging out - #125 + if (action.preserve) { + return pick(state, action.preserve) // pick returns a new object } + return {} default: return state } } /** - * 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 + * Reducer for auth state. Changed by `LOGIN`, `LOGOUT`, and `LOGIN_ERROR` actions. + * @param {Object} [state={isLoaded: false}] - Current auth redux state + * @param {Object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched * @return {Object} Profile state after reduction */ -const authReducer = (state = {}, action) => { +export const authReducer = (state = { isLoaded: false, isEmpty: true }, action) => { switch (action.type) { case LOGIN: case AUTH_UPDATE_SUCCESS: - return action.auth || undefined - case LOGOUT: + if (!action.auth) { + return { + isEmpty: true, + isLoaded: true + } + } + const auth = action.auth.toJSON ? action.auth.toJSON() : action.auth + return { ...auth, isEmpty: false, isLoaded: true } case LOGIN_ERROR: - return null + // TODO: Support keeping data when logging out + return { isLoaded: true, isEmpty: true } + case LOGOUT: + return { isLoaded: true, isEmpty: true } default: return state } @@ -97,61 +201,127 @@ const authReducer = (state = {}, action) => { /** * Reducer for profile state. Changed by `SET_PROFILE`, `LOGOUT`, and * `LOGIN_ERROR` actions. - * @param {Object} state - Current profile redux state + * @param {Object} [state={isLoaded: false}] - Current profile redux state * @param {object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched * @return {Object} Profile state after reduction */ -const profileReducer = (state = null, action) => { +export const profileReducer = (state = { isLoaded: false, isEmpty: true }, action) => { switch (action.type) { case SET_PROFILE: + if (!action.profile) { + return { + ...state, + isEmpty: true, + isLoaded: true + } + } return { ...state, - ...action.profile + ...action.profile, + isEmpty: false, + isLoaded: true } case LOGOUT: case LOGIN_ERROR: - return null + return { isLoaded: true, isEmpty: true } default: return state } } /** - * 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 + * 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 + * @param {String} action.type - Type of action that was dispatched * @return {Object} Profile state after reduction */ -const isInitializingReducer = (state = false, action) => { +export const errorsReducer = (state = [], action) => { switch (action.type) { - case AUTHENTICATION_INIT_STARTED: - return true - case AUTHENTICATION_INIT_FINISHED: - return false - default: - return state + case UNAUTHORIZED_ERROR: return [...state, action.authError] + case LOGOUT: return [] + default: return state } } /** - * Reducer for errors state. Changed by `UNAUTHORIZED_ERROR` + * Reducer for listeners ids. Changed by `SET_LISTENER` and `UNSET_LISTENER` + * actions. + * @param {Object} [state={}] - Current listenersById redux state + * @param {Object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched + * @return {Object} listenersById state after reduction (used in listeners) + * @private + */ +const listenersById = (state = {}, { type, path, payload }) => { + switch (type) { + case SET_LISTENER: + return { + ...state, + [payload.id]: { + id: payload.id, + path + } + } + case UNSET_LISTENER: return omit(state, [payload.id]) + default: return state + } +} + +/** + * Reducer for listeners 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 + * @param {Object} [state=[]] - Current authError redux state + * @param {Object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched + * @return {Object} allListeners state after reduction (used in listeners) + * @private */ -const errorsReducer = (state = [], action) => { - switch (action.type) { - case UNAUTHORIZED_ERROR: - return [...state, action.payload] - case LOGOUT: - return null - default: - return state +const allListeners = (state = [], { type, path, payload }) => { + switch (type) { + case SET_LISTENER: return [...state, payload.id] + case UNSET_LISTENER: return state.filter(lId => lId !== payload.id) + default: return state } } +/** + * Reducer for listeners 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 + * @param {String} action.type - Type of action that was dispatched + * @return {Object} Profile state after reduction + */ +export const listenersReducer = combineReducers({ + byId: listenersById, + allIds: allListeners +}) + +/** + * Reducer for data state. Changed by `SET`, `SET_ORDERED`,`NO_VALUE`, and + * `LOGOUT` actions. + * @param {Object} [state={}] - Current data redux state + * @param {Object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched + * @param {String} action.path - Path of action that was dispatched + * @return {Object} Data state after reduction + */ +export const dataReducer = createDataReducer() + +/** + * Reducer for ordered state. Changed by `SET`, `SET_ORDERED`,`NO_VALUE`, and + * `LOGOUT` actions. + * @param {Object} [state={}] - Current data redux state + * @param {Object} action - Object containing the action that was dispatched + * @param {String} action.type - Type of action that was dispatched + * @param {String} action.path - Path of action that was dispatched + * @return {Object} Data state after reduction + */ +export const orderedReducer = createDataReducer('ordered') + /** * @name firebaseStateReducer * @description Reducer for react redux firebase. This function is called @@ -161,14 +331,19 @@ const errorsReducer = (state = [], action) => { * @param {Object} 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 {Object} State + * @param {String} action.path - Path of action that was dispatched + * @param {String} action.data - Data associated with action + * @return {Object} Firebase redux state */ export default combineReducers({ requesting: requestingReducer, + requested: requestedReducer, + timestamps: timestampsReducer, data: dataReducer, + ordered: orderedReducer, auth: authReducer, profile: profileReducer, + listeners: listenersReducer, isInitializing: isInitializingReducer, errors: errorsReducer }) diff --git a/src/utils/populate.js b/src/utils/populate.js index 7a61c9010..f11b1a757 100644 --- a/src/utils/populate.js +++ b/src/utils/populate.js @@ -106,18 +106,14 @@ export const populateList = (firebase, list, p, results) => { map(list, (id, childKey) => { // handle list of keys const populateKey = id === true ? childKey : id - return getPopulateChild( - firebase, - p, - populateKey - ) - .then(pc => { - if (pc) { - // write child to result object under root name if it is found - return set(results, `${p.root}.${populateKey}`, pc) - } - return results - }) + return getPopulateChild(firebase, p, populateKey) + .then(pc => { + if (pc) { + // write child to result object under root name if it is found + return set(results, `${p.root}.${populateKey}`, pc) + } + return results + }) }) ) } diff --git a/src/utils/query.js b/src/utils/query.js index 0292ea413..60960cf33 100644 --- a/src/utils/query.js +++ b/src/utils/query.js @@ -1,7 +1,5 @@ import { actionTypes } from '../constants' -import { isNaN, isFunction } from 'lodash' - -const { UNSET_LISTENER } = actionTypes +import { isNaN } from 'lodash' const tryParseToNumber = (value) => { const result = Number(value) @@ -59,7 +57,7 @@ export const getQueryIdFromPath = (path, event = undefined) => { * @param {String} queryId - Id of query * @return {Integer} watcherCount - count */ -export const setWatcher = (firebase, event, path, queryId = undefined) => { +export const setWatcher = (firebase, dispatch, event, path, queryId = undefined) => { const id = queryId || getQueryIdFromPath(path, event) || getWatchPath(event, path) if (firebase._.watchers[id]) { @@ -68,6 +66,8 @@ export const setWatcher = (firebase, event, path, queryId = undefined) => { firebase._.watchers[id] = 1 } + dispatch({ type: actionTypes.SET_LISTENER, path, payload: { id } }) + return firebase._.watchers[id] } @@ -96,22 +96,17 @@ 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] - const { watchers, config } = firebase._ - if (watchers[id] <= 1) { - delete watchers[id] + if (firebase._.watchers[id] <= 1) { + delete firebase._.watchers[id] if (event !== 'first_child' && event !== 'once') { firebase.database().ref().child(path).off(event) // 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 (watchers[id]) { + } else if (firebase._.watchers[id]) { firebase._.watchers[id]-- } + + dispatch({ type: actionTypes.UNSET_LISTENER, path, payload: { id } }) } /** diff --git a/src/utils/storage.js b/src/utils/storage.js index 4a8072a1b..0f49f9676 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 }) + ? ({ path }) // return path if dbPath does not exist : firebase // Handle option for removing file info from database .database() .ref(dbPath) diff --git a/tests/.eslintrc b/tests/.eslintrc new file mode 100644 index 000000000..ead6411e7 --- /dev/null +++ b/tests/.eslintrc @@ -0,0 +1,14 @@ +extends: ../.eslintrc + +globals: + sinon: true + expect: true + after: true + afterEach: true + before: true + beforeEach: true + Firebase: true + firebase: true + +rules: + no-unused-expressions: [0] diff --git a/tests/unit/actions/auth.spec.js b/tests/unit/actions/auth.spec.js index 94da7676e..4f0799f91 100644 --- a/tests/unit/actions/auth.spec.js +++ b/tests/unit/actions/auth.spec.js @@ -1,7 +1,4 @@ import { - dispatchLoginError, - dispatchUnauthorizedError, - dispatchLogin, init, unWatchUserProfile, watchUserProfile, @@ -11,7 +8,7 @@ import { createUser, resetPassword, confirmPasswordReset, - verifyPasswordResetCode, + verifyPasswordResetCode } from '../../../src/actions/auth' import { promisesForPopulate } from '../../../src/utils/populate' @@ -24,16 +21,20 @@ const fakeFirebase = { authUid: '123', config: { userProfile: 'users', - disableRedirectHandling: true, - }, + disableRedirectHandling: true + } }, database: () => ({ ref: () => ({ + val: () => ({ some: 'obj' }), child: () => ({ - on: () => ({ val: () => { some: 'obj' } }), - off: () => Promise.resolve({ val: () => { some: 'obj' }}), - once: () => Promise.resolve({ val: () => { some: 'obj' }}) + on: () => ({ val: () => ({ some: 'obj' }) }), + off: () => Promise.resolve({ val: () => ({ some: 'obj' }) }), + once: () => Promise.resolve({ val: () => ({ some: 'obj' }) }) }) + }), + update: () => Promise.resolve({ + val: () => ({ some: 'obj' }) }) }), auth: () => ({ @@ -47,7 +48,7 @@ const fakeFirebase = { Promise.resolve({}), createUserWithEmailAndPassword: (email, password) => email === 'error' - ? Promise.reject({ code: 'asdfasdf' }) + ? Promise.reject(new Error('asdfasdf')) : Promise.resolve({ uid: '123', email: 'test@test.com', providerData: [{}] }), signInWithCustomToken: () => { return Promise.resolve({ @@ -61,48 +62,33 @@ const fakeFirebase = { }, signInWithEmailAndPassword: (email, password) => email.indexOf('error2') !== -1 - ? Promise.reject({ code: 'asdfasdf' }) + ? Promise.reject(new Error('asdfasdf')) : email === 'error3' - ? Promise.reject({ code: 'auth/user-not-found' }) + ? Promise.reject(new Error('auth/user-not-found')) : Promise.resolve({ uid: '123', email: 'test@test.com', providerData: [{}] }), sendPasswordResetEmail: (email) => email === 'error' - ? Promise.reject({code: 'auth/user-not-found'}) + ? Promise.reject({ code: 'auth/user-not-found' }) // eslint-disable-line prefer-promise-reject-errors : email === 'error2' - ? Promise.reject({code: 'asdfasdf'}) - : Promise.resolve({some: 'val'}), + ? Promise.reject(new Error('asdfasdf')) + : Promise.resolve({ some: 'val' }), confirmPasswordReset: (code, password) => password === 'error' - ? Promise.reject({code: code}) + ? Promise.reject({ code: code }) // eslint-disable-line prefer-promise-reject-errors : Promise.resolve(), verifyPasswordResetCode: (code) => code === 'error' - ? Promise.reject({ code: 'some' }) + ? Promise.reject(new Error('some')) : Promise.resolve('success') }) } describe('Actions: Auth', () => { - describe('dispatchLoginError', () => { - it('calls dispatch with error', () => { - expect(dispatchLoginError(dispatch, { some: 'error' })) - }) - }) - - describe('dispatchUnauthorizedError', () => { - it('calls dispatch with error', () => { - expect(dispatchUnauthorizedError(dispatch, { some: 'error' })) - }) - }) - - describe('dispatchLogin', () => { - it('calls dispatch', () => { - expect(dispatchLogin(dispatch, { some: 'error' })) - }) - }) - describe('init', () => { - it('calls firebases onAuthStateChanged', () => { - init(dispatch, fakeFirebase) + it("calls firebase's onAuthStateChanged", () => { + init(dispatch, Firebase) + }) + it('Errors if Firebase instance is not passed', () => { + // expect(init(dispatch, {})).to.Throw }) }) @@ -161,7 +147,6 @@ describe('Actions: Auth', () => { promisesForPopulate.restore() expect(functionSpy).to.be.calledOnce }) - }) describe('createUserProfile', () => { @@ -336,7 +321,6 @@ describe('Actions: Auth', () => { }) }) }) - }) describe('verifyPasswordResetCode', () => { diff --git a/tests/unit/actions/query.spec.js b/tests/unit/actions/query.spec.js index f8d11ac5b..23f4269dd 100644 --- a/tests/unit/actions/query.spec.js +++ b/tests/unit/actions/query.spec.js @@ -1,43 +1,45 @@ -import queryAction from '../../../src/actions/query' import { watchEvent, unWatchEvent, watchEvents, unWatchEvents } from '../../../src/actions/query' -import { - unsetWatcher -} from '../../../src/utils/query' -let spy, unWatch -const dispatch = () => { -} +let spy +const dispatch = () => {} + describe('Actions: Query', () => { beforeEach(() => { spy = sinon.spy(dispatch) }) + describe('watchEvent', () => { it('is exported', () => { expect(watchEvent).to.be.a.function }) + it('runs given basic params', () => { return watchEvent(firebase, dispatch, { type: 'once', path: 'projects' }, 'projects') .then((snap) => { expect(snap).to.be.an.object }) }, 4000) + it('runs given first_child', () => { return watchEvent(firebase, dispatch, { type: 'first_child', path: 'projects' }, 'projects') .then((snap) => { expect(snap).to.be.an.object }) }) + it('runs value query', () => { expect(watchEvent(firebase, dispatch, { type: 'value', path: 'projects' }, 'projects')) }) + it('handles populates', () => { expect(watchEvent(firebase, dispatch, { type: 'value', path: 'projects', populates: [{ child: 'uid', root: 'users' }] }, 'projects')) }) + it('throws for null type', () => { expect(() => watchEvent(firebase, dispatch, { path: 'projects' }, 'projects')).to.Throw }) @@ -47,6 +49,7 @@ describe('Actions: Query', () => { it('is exported', () => { expect(unWatchEvent).to.be.a.function }) + it('runs given basic params', () => { expect(unWatchEvent(firebase, dispatch, { type: 'once', path: 'projects' })).to.be.a.function }) @@ -56,6 +59,7 @@ describe('Actions: Query', () => { it('is exported', () => { expect(watchEvents).to.be.a.function }) + it('runs given basic params', () => { const events = [{type: 'once', path: 'test'}] spy = sinon.spy(events, 'forEach') @@ -68,12 +72,14 @@ describe('Actions: Query', () => { it('is exported', () => { expect(unWatchEvents).to.be.a.function }) + it('runs given basic params', () => { const events = [{type: 'value', path: 'test'}] spy = sinon.spy(events, 'forEach') unWatchEvents(firebase, dispatch, events) expect(spy).to.be.calledOnce }) + it('throws for bad type', () => { const events = [{path: 'test'}] spy = sinon.spy(events, 'forEach') diff --git a/tests/unit/actions/storage.spec.js b/tests/unit/actions/storage.spec.js index e365b9f34..d23346ffd 100644 --- a/tests/unit/actions/storage.spec.js +++ b/tests/unit/actions/storage.spec.js @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-expressions */ import { uploadFileWithProgress, uploadFile, @@ -51,9 +50,11 @@ describe('Actions: Storage', () => { beforeEach(() => { spy = sinon.spy(dispatch) }) + it('is exported', () => { expect(uploadFileWithProgress).to.be.a.function }) + it.skip('runs given basic params', () => uploadFileWithProgress(dispatch, fakeFirebase, { path: 'projects', file: { name: 'test.png' } }) .then((snap) => { @@ -67,9 +68,11 @@ describe('Actions: Storage', () => { beforeEach(() => { spy = sinon.spy(dispatch) }) + it('is exported', () => { expect(uploadFile).to.be.a.function }) + it.skip('runs given basic params', () => uploadFile(dispatch, fakeFirebase, { path: 'projects', file: { name: 'test.png' } }) .then((snap) => { @@ -83,6 +86,7 @@ describe('Actions: Storage', () => { it('is exported', () => { expect(uploadFiles).to.be.a.function }) + it('runs given basic params', () => uploadFiles(dispatch, fakeFirebase, { path: 'projects', file: { name: 'test.png' } }) .then((snap) => { diff --git a/tests/unit/compose.spec.js b/tests/unit/compose.spec.js index 6fbfb737c..70d1300b5 100644 --- a/tests/unit/compose.spec.js +++ b/tests/unit/compose.spec.js @@ -1,14 +1,11 @@ -import { omit } from 'lodash' -import { createStore, combineReducers, compose } from 'redux' +import { createStore, compose } from 'redux' import composeFunc, { getFirebase } from '../../src/compose' -import { login } from '../../src/actions/auth' -const exampleData = { data: { some: 'data' } } const reducer = sinon.spy() const generateCreateStore = (params) => compose(composeFunc( - params ? omit(fbConfig, params) : fbConfig, + Firebase, { userProfile: 'users', enableLogging: false, @@ -16,17 +13,17 @@ const generateCreateStore = (params) => } ))(createStore) -generateCreateStore()(reducer) - -const helpers = getFirebase().helpers +const store = generateCreateStore()(reducer) describe('Compose', () => { it('is a function', () => { expect(composeFunc).to.be.a.function }) + it('returns an object', () => { - expect(composeFunc(fbConfig)).to.be.a.function + expect(composeFunc(Firebase)).to.be.a.function }) + it('allows enabling of Firebase database logging', () => { expect(generateCreateStore()(reducer)) .to.be.an.object @@ -35,117 +32,135 @@ describe('Compose', () => { describe('helpers', () => { describe('ref', () => { it('exists', () => { - expect(helpers.ref('test')).to.be.an.object + expect(store.firebase.ref('test')).to.be.an.object }) + it('has child', () => { - expect(helpers.ref('test').child('asdf')).to.be.an.object + expect(store.firebase.ref('test').child('asdf')).to.be.an.object }) }) describe('set', () => { it('accepts object', () => - expect(helpers.set('test', {some: 'asdf'})).to.eventually.become(undefined) + expect(store.firebase.set('test', {some: 'asdf'})) + .to.eventually.become(undefined) ) }) describe('setWithMeta', () => { describe('accepts object', () => { it('accepts object', () => - expect(helpers.setWithMeta('test', {some: 'asdf'})).to.eventually.become(undefined) + expect(store.firebase.setWithMeta('test', {some: 'asdf'})) + .to.eventually.become(undefined) ) }) describe('does not attach meta to string', () => { // TODO: confirm that data set actually does not include meta it('accepts object', () => - expect(helpers.setWithMeta('test', 'asdd')).to.eventually.become(undefined) + expect(store.firebase.setWithMeta('test', 'asdd')) + .to.eventually.become(undefined) ) }) }) describe('push', () => { it('accepts object', () => - expect(helpers.push('test', {some: 'asdf'})).to.eventually.have.property('key') + expect(store.firebase.push('test', {some: 'asdf'})) + .to.eventually.have.property('key') ) }) describe('pushWithMeta', () => { it('accepts object', () => - expect(helpers.pushWithMeta('test', {some: 'asdf'})).to.eventually.have.property('key') + expect(store.firebase.pushWithMeta('test', {some: 'asdf'})) + .to.eventually.have.property('key') ) }) describe('update', () => { it('accepts object', () => // undefined represents snapshot - expect(helpers.update('test', {some: 'asdf'})).to.eventually.become(undefined) + expect(store.firebase.update('test', {some: 'asdf'})) + .to.eventually.become(undefined) ) }) describe('updateWithMeta', () => { it('accepts object', () => - expect(helpers.updateWithMeta('test', {some: 'asdf'})).to.eventually.become(undefined) + expect(store.firebase.updateWithMeta('test', {some: 'asdf'})) + .to.eventually.become(undefined) ) }) - describe('uniqueSet', () =>{ + describe('uniqueSet', () => { // remove test root after test are complete - after(() => - helpers.remove('test') - ) + // after(() => { + // if (helpers && store.firebase.remove) { + // return store.firebase.remove('test') + // } + // }) it('sets if unique', () => - helpers.uniqueSet('test/unique', {some: 'asdf'}) + store.firebase.uniqueSet('test/unique', {some: 'asdf'}) ) it('throws if not unique', () => - helpers.uniqueSet('test', {some: 'asdf'}) + store.firebase.uniqueSet('test', {some: 'asdf'}) .catch((err) => { expect(err.toString()).to.equal('Error: Path already exists.') }) ) it('has on err onComplete', () => { const func = sinon.spy() - return helpers.uniqueSet('test', {some: 'asdf'}, func) + return store.firebase.uniqueSet('test', {some: 'asdf'}, func) .catch((err) => { expect(func).to.have.been.calledOnce + expect(err).to.exist }) }) }) - describe('remove', () => - helpers.remove('test') - ) + describe('remove', () => { + it('runs', () => { + return store.firebase.remove('test') + }) + }) describe('watchEvent', () => { it('starts watcher', () => { - helpers.watchEvent('value', 'test') + store.firebase.watchEvent('value', 'test') }) }) describe('unWatchEvent', () => { it('unWatchesEvent', () => - helpers.unWatchEvent('value', 'test') + store.firebase.unWatchEvent('value', 'test') ) }) describe('login', () => { try { - helpers.login({ email: 'test' }) + store.firebase.login({ email: 'test' }) } catch (err) { expect(err).to.be.an.object } }) - describe('logout', () => - helpers.logout() - ) + describe('logout', () => { + it('runs', () => { + return store.firebase.logout() + }) + }) - describe('createUser', () => - helpers.createUser({ email: 'test' }, { email: 'test' }) - ) + describe('createUser', () => { + it('runs', () => { + return expect(store.firebase.createUser({ email: 'test' }, { email: 'test' })) + .to.eventually.be.an.object + }) + }) describe('resetPassword', () => { try { - helpers.resetPassword({ email: 'test' }) + store.firebase.resetPassword({ email: 'test' }) } catch (err) { expect(err).to.be.an.object } @@ -153,69 +168,72 @@ describe('Compose', () => { describe('confirmPasswordReset', () => { try { - helpers.confirmPasswordReset({ code: 'test', password: 'test' }) + store.firebase.confirmPasswordReset({ code: 'test', password: 'test' }) } catch (err) { expect(err).to.be.an.object } }) - describe('updateProfile', () => { + describe.skip('updateProfile', () => { it('acccepts an object', () => - expect(helpers.updateProfile({ displayName: 'test' })).to.eventually.become(undefined) + expect(store.firebase.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.') + expect(store.firebase.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) + expect(store.firebase.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) + expect(store.firebase.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.') + expect(store.firebase.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) + expect(store.firebase.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) + expect(store.firebase.updateEmail({}, true)).to.eventually.become(undefined) ) }) describe('storage', () => { try { - helpers.storage() - } catch(err) { + store.firebase.storage() + } catch (err) { expect(err).to.be.an.object } }) }) - describe('throws for missing fbConfig parameters', () => { + describe.skip('throws for missing fbConfig parameters', () => { const errorSuffix = 'is a required config parameter for react-redux-firebase.' it('databaseURL', () => { expect(() => generateCreateStore('databaseURL')(reducer)) .to.throw(`databaseURL ${errorSuffix}`) }) + it('authDomain', () => { expect(() => generateCreateStore('authDomain')(reducer)) .to.throw(`authDomain ${errorSuffix}`) }) + it('apiKey', () => { expect(() => generateCreateStore('apiKey')(reducer)) .to.throw(`apiKey ${errorSuffix}`) diff --git a/tests/unit/connect.spec.js b/tests/unit/connect.spec.js index 1b6008108..c3ee37ed4 100644 --- a/tests/unit/connect.spec.js +++ b/tests/unit/connect.spec.js @@ -1,6 +1,5 @@ -import React, { createClass, Children, Component } from 'react' +import React, { Children, Component } from 'react' import PropTypes from 'prop-types' -import ReactDOM from 'react-dom' import TestUtils from 'react-addons-test-utils' import { createStore, compose, combineReducers } from 'redux' import connect from '../../src/connect' @@ -27,30 +26,28 @@ describe('Connect', () => { store: PropTypes.object.isRequired } - function stringBuilder (prev = '', action) { - return action.type === 'APPEND' - ? prev + action.body - : prev + ProviderMock.propTypes = { + children: PropTypes.node, + store: PropTypes.object.isRequired } - it('should receive the store in the context', () => { const createStoreWithMiddleware = compose( - reactReduxFirebase(fbConfig, { userProfile: 'users' }), + reactReduxFirebase(Firebase, { userProfile: 'users' }), typeof window === 'object' && typeof window.devToolsExtension !== 'undefined' ? window.devToolsExtension() : f => f )(createStore) const store = createStoreWithMiddleware(combineReducers({ test: (state = {}) => state })) @connect() class Container extends Component { - render() { + render () { return } } const tree = TestUtils.renderIntoDocument( - + ) @@ -60,5 +57,4 @@ describe('Connect', () => { }) expect(container.context.store).to.equal(store) }) - }) diff --git a/tests/unit/helpers.spec.js b/tests/unit/helpers.spec.js index bb79c9410..255cc9e6f 100644 --- a/tests/unit/helpers.spec.js +++ b/tests/unit/helpers.spec.js @@ -1,5 +1,4 @@ import * as helpers from '../../src/helpers' -import { buildChildList } from '../../src/helpers' const exampleData = { data: { @@ -8,7 +7,7 @@ const exampleData = { CDF: { owner: 'ABC', notes: { - 123: true, + 123: true }, collaborators: { ABC: true, @@ -21,7 +20,7 @@ const exampleData = { OKF: { owner: 'asdfasdf', notes: { - 123: true, + 123: true }, collaborators: { ABC: true, @@ -34,7 +33,7 @@ const exampleData = { owner: 'ABC' }, notes: { - 123: true, + 123: true }, collaborators: { ABC: true, @@ -58,13 +57,13 @@ const exampleData = { { owner: 'ABC', notes: { - 123: true, + 123: true }, collaborators: { ABC: true, abc: true } - }, + } ] }, timestamp: { 'some/path': { test: 'key' } }, @@ -128,12 +127,12 @@ describe('Helpers:', () => { const path = 'projects/CDF' const rootName = 'users' const populates = [{ child: 'owner', root: rootName }] - console.log('\n\nhere', helpers.populate(exampleData, path, populates)) expect(helpers.populate(exampleData, path, populates).owner) .to .have .property('displayName', 'scott') }) + it('handles child path', () => { const path = 'projects/QRS' const rootName = 'users' @@ -144,6 +143,7 @@ describe('Helpers:', () => { .have .property('displayName', 'scott') }) + it('populates childParam', () => { const path = 'projects/CDF' const rootName = 'users' @@ -166,7 +166,7 @@ describe('Helpers:', () => { const path = 'projects/OKF' const rootName = 'users' const populates = [ - { child: 'collaborators', root: rootName }, + { child: 'collaborators', root: rootName } ] const populatedData = helpers.populate(exampleData, path, populates) expect(populatedData) @@ -192,11 +192,9 @@ describe('Helpers:', () => { .property('displayName', 'scott') }) }) - }) describe('list', () => { - describe('single param', () => { it('populates value', () => { const path = 'projects' @@ -225,7 +223,7 @@ describe('Helpers:', () => { const rootName = 'users' const valName = 'OKF' const populates = [ - { child: 'collaborators', root: rootName }, + { child: 'collaborators', root: rootName } ] const populatedData = helpers.populate(exampleData, path, populates) expect(populatedData) @@ -240,7 +238,7 @@ describe('Helpers:', () => { const rootName = 'users' const valName = 'OKF' const populates = [ - { child: 'collaborators', root: rootName }, + { child: 'collaborators', root: rootName } ] expect(helpers.populate(exampleData, path, populates)) .to @@ -263,7 +261,7 @@ describe('Helpers:', () => { const valName = 'CDF' const populates = [ { child: 'owner', root: rootName }, - { child: 'notes', root: 'notes' }, + { child: 'notes', root: 'notes' } ] // check that notes are populated expect(helpers.populate(exampleData, `/${path}`, populates)) @@ -286,7 +284,7 @@ describe('Helpers:', () => { const valName = 'CDF' const populates = [ { child: 'owner', root: rootName }, - { child: 'collaborators', root: rootName }, + { child: 'collaborators', root: rootName } ] // TODO: Test both children are populated expect(helpers.populate(exampleData, `/${path}`, populates)) @@ -301,8 +299,6 @@ describe('Helpers:', () => { .property(`${valName}.collaborators.ABC.displayName`, exampleData.data[rootName].ABC.displayName) }) }) - - }) describe('isLoaded', () => { diff --git a/tests/unit/reducer.spec.js b/tests/unit/reducer.spec.js index 9fb71c75f..1ed804d8e 100644 --- a/tests/unit/reducer.spec.js +++ b/tests/unit/reducer.spec.js @@ -1,207 +1,155 @@ +import { set } from 'lodash' import { firebaseStateReducer } from '../../src' import { actionTypes } from '../../src/constants' -const emptyState = { - auth: undefined, - authError: undefined, - profile: undefined, - isInitializing: undefined, - data: {} -} + const initialState = { - auth: undefined, - authError: undefined, - profile: undefined, - isInitializing: undefined, + auth: { isLoaded: false, isEmpty: true }, + profile: { isLoaded: false, isEmpty: true }, + isInitializing: false, + errors: [], data: {}, - timestamp: {}, + listeners: { allIds: [], byId: {} }, + ordered: {}, + timestamps: {}, requesting: {}, requested: {} } -const intializedState = Object.assign({}, initialState, { isInitializing: true }) -const noError = { authError: null } -const noAuth = { auth: null, profile: null } + +const noError = { ...initialState, errors: [] } +const loadedState = { + ...noError, + auth: { isLoaded: true, isEmpty: true }, + profile: { isLoaded: true, isEmpty: true } +} const exampleData = { some: 'data' } const externalState = { data: { asdfasdf: {} } } -const exampleState = {} -const exampleEmptyState = emptyState + +let path = 'test' +let childKey = 'abc' +let childPath = `${path}/${childKey}` +let action = {} +let childDotPath +const newData = { some: 'val' } +const profile = { email: 'test@test.com' } +const getDotPath = path => path.split('/').join('.') describe('reducer', () => { it('is a function', () => { expect(firebaseStateReducer).to.be.a.function }) - it('handles no initialState', () => { + it.skip('handles no initialState', () => { expect(firebaseStateReducer(undefined, {})).to.equal(initialState) }) it('returns state by default', () => { - expect(firebaseStateReducer(exampleData)).to.equal(exampleData) + expect(firebaseStateReducer(externalState, {})) + .to.have.property('data', externalState.data) + }) + + beforeEach(() => { + // reset defaults + path = 'test' + childKey = 'abc' + childPath = `${path}/${childKey}` + action = {} + childDotPath = getDotPath(childPath) }) describe('SET action', () => { - it('deletes data from state when data is null', () => { - expect( - firebaseStateReducer(exampleState, - { type: actionTypes.SET, path: 'test' } - ) - ).to.equal(exampleState) + it.skip('deletes data from state when data is null', () => { + action = { type: actionTypes.SET, path: 'test' } + expect(firebaseStateReducer({}, action)) + .to.deep.equal(initialState) }) it('sets state', () => { - const path = 'test' - const pathArray = path.split(/\//).filter(p => !!p) - expect( - firebaseStateReducer( - exampleState, - { type: actionTypes.SET, path, data: {} } - ) - ).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) + action = { type: actionTypes.SET, path, data: exampleData } + expect(firebaseStateReducer({}, action).data) + .to.deep.equal({ + ...initialState.data, + [path]: exampleData + }) }) - 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( - JSON.stringify(firebaseStateReducer( - fromJS({ data: { test: null } }), - { type: actionTypes.SET, path, data: newData } - ).toJS()) - ).to.equal( - JSON.stringify( - exampleState.setIn( - ['data', ...pathArray], - fromJS(newData) - ).toJS() - ) - ) + it('handles already existing parent of null', () => { + action = { type: actionTypes.SET, path: childPath, data: exampleData } + expect(firebaseStateReducer({ data: { test: null } }, action).data) + .to.deep.equal(set({}, childDotPath, exampleData)) }) it('handles already existing value of null', () => { - const path = 'test/123' - const pathArray = path.split(/\//).filter(p => !!p) - const newData = { some: 'val' } - expect( - JSON.stringify(firebaseStateReducer( - fromJS({ data: { test: { '123': null } } }), - { type: actionTypes.SET, path, data: newData } - )) - ).to.equal( - JSON.stringify( - exampleState.setIn( - ['data', ...pathArray], - fromJS(newData) - ).toJS() - ) - ) + action = { type: actionTypes.SET, path: childPath, data: newData } + expect(firebaseStateReducer({ data: { test: { [childKey]: null } } }, action).data) + .to.deep.equal(set({}, childDotPath, newData)) }) - }) describe('NO_VALUE action', () => { - it('sets state', () => { - expect( - firebaseStateReducer( - exampleState, - { type: actionTypes.NO_VALUE, path: 'asdfasdf' } - ) - ).to.equal(externalState) + it('sets path to null', () => { + action = { type: actionTypes.NO_VALUE, path } + expect(firebaseStateReducer({}, action).data) + .to.deep.equal({ [path]: null }) }) }) describe('UNSET_LISTENER action', () => { it('sets state', () => { - expect( - firebaseStateReducer( - exampleState, - { type: actionTypes.UNSET_LISTENER, path: 'asdfasdf' } - ) - ).to.equal({}) + action = { type: actionTypes.UNSET_LISTENER, path: 'asdfasdf', payload: { id: 1 } } + expect(firebaseStateReducer({}, action)) + .to.deep.equal(initialState) }) }) describe('UNSET_LISTENER action', () => { + action = { type: actionTypes.UNSET_LISTENER, path: 'asdfasdf', payload: { id: 1 } } it('sets state', () => { - expect( - JSON.stringify(firebaseStateReducer( - exampleState, - { type: actionTypes.UNSET_LISTENER, path: 'asdfasdf' } - ).toJS()) - ).to.equal(JSON.stringify({})) + expect(firebaseStateReducer({}, action)) + .to.deep.equal(initialState) }) }) describe('SET_PROFILE action', () => { - it('sets state', () => { - const profile = { email: 'test@test.com' } - expect( - firebaseStateReducer( - exampleState, - { type: actionTypes.SET_PROFILE, profile } - ) - ).to.equal({ profile }) + it('sets profile to state', () => { + action = { type: actionTypes.SET_PROFILE, profile } + expect(firebaseStateReducer({}, action)).to.deep.equal({ + ...initialState, + profile: { ...profile, isLoaded: true, isEmpty: false } + }) }) + it('removes for no profile', () => { - const profile = { email: 'test@test.com' } - expect( - firebaseStateReducer( - exampleState, - { type: actionTypes.SET_PROFILE } - ) - ).to.equal(exampleState.deleteIn(['profile'])) + action = { type: actionTypes.SET_PROFILE } + expect(firebaseStateReducer({}, action)) + .to.deep.equal({ + ...initialState, + profile: { isLoaded: true, isEmpty: true } + }) }) }) describe('LOGOUT action', () => { it('sets state', () => { - expect( - firebaseStateReducer( - exampleState, - { type: actionTypes.LOGOUT } - ) - ).to.equal({ - auth: null, - authError: null, - profile: null, - isInitializing: false, - data: {} - }) + action = { type: actionTypes.LOGOUT } + expect(firebaseStateReducer({}, action)) + .to.deep.equal(loadedState) }) }) describe('LOGIN action', () => { it('sets state', () => { - expect( - firebaseStateReducer( - exampleState, - { type: actionTypes.LOGIN } - ) - ).to.equal(noError) + const auth = { some: 'value' } + action = { type: actionTypes.LOGIN, auth } + expect(firebaseStateReducer({}, action)) + .to.deep.equal({ + ...noError, + auth: { ...noError.auth, ...auth, isLoaded: true, isEmpty: false } + }) + }) + it.skip('sets empty if auth not provided', () => { + action = { type: actionTypes.LOGIN } + expect(firebaseStateReducer({}, action)) + .to.deep.equal(noError) }) }) @@ -209,78 +157,67 @@ describe('reducer', () => { it('sets state', () => { expect( firebaseStateReducer( - exampleState, + {}, { type: actionTypes.LOGIN_ERROR } ) - ).to.equal(noAuth) + ).to.deep.equal({ + ...initialState, + auth: { isLoaded: true, isEmpty: true }, + profile: { isLoaded: true, isEmpty: true } + }) }) }) describe('AUTHENTICATION_INIT_STARTED action', () => { - it('sets state', () => { - expect( - firebaseStateReducer( - exampleState, - { type: actionTypes.AUTHENTICATION_INIT_STARTED } - ) - ).to.equal({ - isInitializing: true, - data: {}, - timestamp: {}, - requesting: {}, - requested: {} - }) + it('sets isInitializing to true', () => { + action = { type: actionTypes.AUTHENTICATION_INIT_STARTED } + expect(firebaseStateReducer({}, action)) + .to.have.property('isInitializing', true) }) }) describe('AUTHENTICATION_INIT_FINISHED action', () => { - it('sets state', () => { - expect( - firebaseStateReducer( - exampleState, - { type: actionTypes.AUTHENTICATION_INIT_FINISHED } - ) - ).to.equal(exampleState, false) + it('sets isInitializing to false', () => { + action = { type: actionTypes.AUTHENTICATION_INIT_FINISHED } + expect(firebaseStateReducer({}, action)) + .to.have.property('isInitializing', false) }) }) describe('UNAUTHORIZED_ERROR action', () => { it('sets state', () => { - const authError = {} - expect( - firebaseStateReducer( - exampleState, - { type: actionTypes.UNAUTHORIZED_ERROR, authError } - ) - ).to.equal(exampleState, authError) + const authError = { some: 'error' } + action = { type: actionTypes.UNAUTHORIZED_ERROR, authError } + expect(firebaseStateReducer({}, action)) + .to.deep.equal({ ...initialState, errors: [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) - }) - }) - - describe('AUTH_UPDATE_SUCCESS action', () => { - it('sets state', () => { - const authUpdate = { email: 'newEmail' } - expect( - JSON.stringify(firebaseStateReducer( - exampleState, - { type: actionTypes.AUTH_UPDATE_SUCCESS, payload: authUpdate } - ).toJS()) - ).to.equal( - JSON.stringify( - exampleState.setIn(['auth'], authUpdate).toJS() - ) - ) + action = { type: actionTypes.AUTH_UPDATE_SUCCESS, auth: authUpdate } + expect(firebaseStateReducer({}, action)) + .to.deep.equal({ + ...noError, + auth: { + ...noError.auth, + ...authUpdate, + isEmpty: false, + isLoaded: true + } + }) + }) + it('handles undefined auth', () => { + action = { type: actionTypes.AUTH_UPDATE_SUCCESS } + expect(firebaseStateReducer({}, action)) + .to.deep.equal({ + ...noError, + auth: { + isEmpty: true, + isLoaded: true + } + }) }) }) }) diff --git a/tests/unit/utils/actions.spec.js b/tests/unit/utils/actions.spec.js index 35862b0fc..b51cdb2ae 100644 --- a/tests/unit/utils/actions.spec.js +++ b/tests/unit/utils/actions.spec.js @@ -3,10 +3,9 @@ import { wrapInDispatch } from '../../../src/utils/actions' const method = () => Promise.resolve() -const failMethod = () => Promise.reject() -const dispatch = () => { - // console.log('dispatch called') -} +const failMethod = () => Promise.reject(new Error('Some Error')) +const dispatch = () => {} + describe('Utils: Auth', () => { describe('wrapInDispatch', () => { // Skipped due to capatalize and auth provider function @@ -20,5 +19,4 @@ describe('Utils: Auth', () => { .to.be.rejectedWith('Failed') }) }) - }) diff --git a/tests/unit/utils/events.spec.js b/tests/unit/utils/events.spec.js index 33bcfa8af..5d07aafc1 100644 --- a/tests/unit/utils/events.spec.js +++ b/tests/unit/utils/events.spec.js @@ -44,6 +44,5 @@ describe('Utils: Events', () => { expect(getEventsFromInput(['some'])[0]).to.not.include.keys('populates') }) }) - }) }) diff --git a/tests/unit/utils/index.spec.js b/tests/unit/utils/index.spec.js index 3a98dd268..3e9f8e103 100644 --- a/tests/unit/utils/index.spec.js +++ b/tests/unit/utils/index.spec.js @@ -1,6 +1,5 @@ -/* global describe expect it */ import { validateConfig } from '../../../src/utils' -console.log('validateConfig', validateConfig) + const validConfig = { databaseURL: 'asdfasdf', authDomain: 'asdfasdf', @@ -9,13 +8,17 @@ const validConfig = { const invalidConfig = { fileMetadataFactory: 'asdfasdf' } + describe('Utils: Index', () => { describe('validateConfig', () => { it('throws for missing param', () => { - expect(() => validateConfig(Object.assign(invalidConfig))).to.throw('databaseURL is a required config parameter for react-redux-firebase') + expect(() => validateConfig(Object.assign(invalidConfig))) + .to.throw('databaseURL is a required config parameter for react-redux-firebase') }) + it('throws for function param not being function', () => { - expect(() => validateConfig(Object.assign(validConfig, invalidConfig))).to.throw('fileMetadataFactory parameter in react-redux-firebase config must be a function. check your compose function.') + expect(() => validateConfig(Object.assign(validConfig, invalidConfig))) + .to.throw('fileMetadataFactory parameter in react-redux-firebase config must be a function. check your compose function.') }) }) }) diff --git a/tests/unit/utils/populate.spec.js b/tests/unit/utils/populate.spec.js index 76faa8dd2..f51241bd8 100644 --- a/tests/unit/utils/populate.spec.js +++ b/tests/unit/utils/populate.spec.js @@ -50,7 +50,6 @@ describe('Utils: Populate', () => { it('handles basic populates', () => { expect(getPopulates(['populate=uid:users'])) }) - }) describe('getPopulateChild', () => { diff --git a/tests/unit/utils/query.spec.js b/tests/unit/utils/query.spec.js index 4ea1c9714..014988864 100644 --- a/tests/unit/utils/query.spec.js +++ b/tests/unit/utils/query.spec.js @@ -6,20 +6,21 @@ import { getQueryIdFromPath, applyParamsToQuery } from '../../../src/utils/query' + const fakeFirebase = { _: { authUid: '123', config: { userProfile: 'users', - disableRedirectHandling: true, - }, + disableRedirectHandling: true + } }, database: () => ({ ref: () => ({ orderByValue: () => ({ - on: () => ({ val: () => { some: 'obj' } }), - off: () => Promise.resolve({ val: () => { some: 'obj' }}), - once: () => Promise.resolve({ val: () => { some: 'obj' }}) + on: () => ({ val: () => ({ some: 'obj' }) }), + off: () => Promise.resolve({ val: () => ({ some: 'obj' }) }), + once: () => Promise.resolve({ val: () => ({ some: 'obj' }) }) }), orderByPriority: () => ({ startAt: (startParam) => startParam, @@ -37,26 +38,28 @@ const fakeFirebase = { limitToLast: () => ({ }), equalTo: () => ({ }), startAt: () => ({ }), - endAt: () => ({ }), + endAt: () => ({ }) }) - }), + }) } -let spy let createQueryFromParams = (queryParams) => applyParamsToQuery(queryParams, fakeFirebase.database().ref()) + const dispatch = () => {} -let ref + describe('Utils: Query', () => { describe('getWatchPath', () => { it('handles basic path', () => { expect(getWatchPath('once', '/todos')).to.be.a.string }) it('throws for no event', () => { - expect(() => getWatchPath(null, '/todos')).to.throw('Event and path are required') + expect(() => getWatchPath(null, '/todos')) + .to.throw('Event and path are required') }) it('throws for no path', () => { - expect(() => getWatchPath(null, null)).to.throw('Event and path are required') + expect(() => getWatchPath(null, null)) + .to.throw('Event and path are required') }) }) @@ -67,11 +70,11 @@ describe('Utils: Query', () => { } }) it('handles incrementating path watcher count', () => { - setWatcher(Firebase, 'once', '/todos') + setWatcher(Firebase, dispatch, 'once', '/todos') expect(Firebase._.watchers['once:/todos']).to.equal(2) }) it('handles basic path', () => { - setWatcher(Firebase, 'once', '/todo') + setWatcher(Firebase, dispatch, 'once', '/todo') expect(Firebase._.watchers['once:/todo']).to.equal(1) }) }) @@ -88,11 +91,6 @@ 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') @@ -104,14 +102,6 @@ describe('Utils: Query', () => { 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) @@ -142,7 +132,7 @@ describe('Utils: Query', () => { }) describe('with startAt', () => { - it ('string containing number', () => { + it('string containing number', () => { const startAt = '123abc' expect(createQueryFromParams(['orderByPriority', `startAt=${startAt}`]).toString()) .to.equal(startAt) @@ -228,7 +218,6 @@ describe('Utils: Query', () => { .equal(equalTo) }) }) - }) it('limitToFirst', () => { diff --git a/tests/unit/utils/storage.spec.js b/tests/unit/utils/storage.spec.js index 4d4fbf31c..fabc4abc3 100644 --- a/tests/unit/utils/storage.spec.js +++ b/tests/unit/utils/storage.spec.js @@ -1,28 +1,30 @@ import { deleteFile } from '../../../src/utils/storage' + const fakeFirebase = { _: { authUid: '123', config: { userProfile: 'users', - disableRedirectHandling: true, - }, + disableRedirectHandling: true + } }, storage: () => ({ ref: () => ({ - delete: () => Promise.resolve({ val: () => { some: 'obj' }}), + delete: () => Promise.resolve({ val: () => ({ some: 'obj' }) }) }) }), database: () => ({ ref: () => ({ remove: () => Promise.resolve({ }), child: () => ({ - on: () => Promise.resolve({ val: () => { some: 'obj' }}), - off: () => Promise.resolve({ val: () => { some: 'obj' }}), - once: () => Promise.resolve({ val: () => { some: 'obj' }}) + on: () => Promise.resolve({ val: () => ({ some: 'obj' }) }), + off: () => Promise.resolve({ val: () => ({ some: 'obj' }) }), + once: () => Promise.resolve({ val: () => ({ some: 'obj' }) }) }) }) - }), + }) } + describe('Utils: Storage', () => { describe('deleteFile', () => { it('returns dbPath', () => diff --git a/yarn.lock b/yarn.lock index 4e2e00796..6304205d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1110,6 +1110,10 @@ body-parser@~1.14.0: raw-body "~2.1.5" type-is "~1.6.10" +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" @@ -1329,6 +1333,27 @@ check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" +cheerio@0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash.assignin "^4.0.9" + lodash.bind "^4.1.4" + lodash.defaults "^4.0.1" + lodash.filter "^4.4.0" + lodash.flatten "^4.2.0" + lodash.foreach "^4.3.0" + lodash.map "^4.4.0" + lodash.merge "^4.4.0" + lodash.pick "^4.2.1" + lodash.reduce "^4.4.0" + lodash.reject "^4.4.0" + lodash.some "^4.4.0" + chmodr@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/chmodr/-/chmodr-1.0.2.tgz#04662b932d0f02ec66deaa2b0ea42811968e3eb9" @@ -1385,6 +1410,14 @@ cli@0.11.3: exit "0.1.2" glob "^7.0.5" +clipboard@^1.5.5: + version "1.7.1" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-1.7.1.tgz#360d6d6946e99a7a1fef395e42ba92b5e9b5a16b" + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -1614,6 +1647,19 @@ crypto-browserify@^3.11.0: public-encrypt "^4.0.0" randombytes "^2.0.0" +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" + cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": version "0.3.2" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" @@ -1729,6 +1775,10 @@ delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" +delegate@^3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.1.3.tgz#9a8251a777d7025faa55737bc3b071742127a9fd" + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" @@ -1888,6 +1938,13 @@ documentation@^4.0.0-beta15: vinyl-fs "^2.3.1" yargs "^4.3.1" +dom-serializer@0, dom-serializer@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + 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" @@ -1896,6 +1953,27 @@ domain-browser@^1.1.1: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" +domelementtype@1, domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259" + dependencies: + domelementtype "1" + +domutils@1.5.1, domutils@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + duplexer2@^0.1.2, duplexer2@~0.1.0: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -1989,6 +2067,10 @@ enhanced-resolve@^3.0.0: object-assign "^4.0.1" tapable "^0.2.5" +entities@^1.1.1, entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + errno@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" @@ -2701,6 +2783,30 @@ gitbook-cli@^2.3.0: tmp "0.0.28" user-home "2.0.0" +gitbook-plugin-anchorjs@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/gitbook-plugin-anchorjs/-/gitbook-plugin-anchorjs-1.1.1.tgz#728e0471ba7427fcde2149227c29fe172b5eecdd" + +gitbook-plugin-edit-link@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/gitbook-plugin-edit-link/-/gitbook-plugin-edit-link-2.0.2.tgz#d8fcd927eced81e7a662a72d59db609eafd7e72f" + +gitbook-plugin-github@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/gitbook-plugin-github/-/gitbook-plugin-github-2.0.0.tgz#5166e763cfcc402d432880b7a6c85c1c54b56a8d" + +gitbook-plugin-prism@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/gitbook-plugin-prism/-/gitbook-plugin-prism-2.2.0.tgz#7d941a6e1aaba0340f88d9b375502a6533eaa69c" + dependencies: + cheerio "0.22.0" + mkdirp "0.5.1" + prismjs "1.6.0" + +gitbook-plugin-versions-select@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/gitbook-plugin-versions-select/-/gitbook-plugin-versions-select-0.1.1.tgz#2723ad4692244ddbc79540a0e445f45ee2497909" + github-slugger@1.1.1, github-slugger@^1.0.0, github-slugger@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.1.1.tgz#5444671f65e5a5a424cfa8ba3255cc1f7baf07ea" @@ -2840,6 +2946,12 @@ globby@^5.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + dependencies: + delegate "^3.1.2" + graceful-fs@^4.0.0, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@~4.1.3, graceful-fs@~4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -2976,6 +3088,17 @@ html-encoding-sniffer@^1.0.1: dependencies: whatwg-encoding "^1.0.1" +htmlparser2@^3.9.1: + version "3.9.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" + http-errors@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" @@ -3759,6 +3882,14 @@ lodash.assign@^4.0.3, lodash.assign@^4.0.6: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" +lodash.assignin@^4.0.9: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" + +lodash.bind@^4.1.4: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" + lodash.clonedeep@~4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.3.2.tgz#d0112c02c76b5223833aebc6a4b6e334f0d057de" @@ -3777,6 +3908,22 @@ lodash.create@3.1.1: lodash._basecreate "^3.0.0" lodash._isiterateecall "^3.0.0" +lodash.defaults@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + +lodash.filter@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" + +lodash.flatten@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + +lodash.foreach@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + lodash.isarguments@^3.0.0, lodash.isarguments@~3.0.7: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.0.9.tgz#3c4994a4210f340d49ccfafa62176296207d8675" @@ -3805,6 +3952,14 @@ lodash.keys@~4.0.3: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.0.8.tgz#c0cf45d2fcf576c83055404d674c7e637c83ae81" +lodash.map@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + +lodash.merge@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" + lodash.mergewith@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" @@ -3825,6 +3980,18 @@ lodash.padstart@^4.1.0: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" +lodash.pick@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + +lodash.reduce@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" + +lodash.reject@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" + lodash.rest@^4.0.0: version "4.0.5" resolved "https://registry.yarnpkg.com/lodash.rest/-/lodash.rest-4.0.5.tgz#954ef75049262038c96d1fc98b28fdaf9f0772aa" @@ -3833,6 +4000,10 @@ lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" +lodash.some@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" + lodash.union@~4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.2.1.tgz#6871017b9b1ff71952c1e2bb2e25b1046a7e2842" @@ -4498,6 +4669,12 @@ npmi@1.0.1: gauge "~2.7.1" set-blocking "~2.0.0" +nth-check@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" + dependencies: + boolbase "~1.0.0" + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -4808,6 +4985,12 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prismjs@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.6.0.tgz#118d95fb7a66dba2272e343b345f5236659db365" + optionalDependencies: + clipboard "^1.5.5" + private@^0.1.6: version "0.1.7" resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" @@ -5528,6 +5711,10 @@ sax@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828" +select@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + "semver@2 >=2.2.1 || 3.x || 4 || 5", "semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@4 || 5", semver@5.3.0, "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.3.0, semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -5931,6 +6118,10 @@ timers-browserify@^2.0.2: dependencies: setimmediate "^1.0.4" +tiny-emitter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.0.tgz#bad327adb1804b42a231afa741532bd884cd09ad" + tiny-lr@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-0.2.1.tgz#b3fdba802e5d56a33c2f6f10794b32e477ac729d"