~ajk/santas-little-helper

91d50f4984a2da914cc8f4a2a2249cb845d166f8 — Andrew Kay 2 years ago master
Initial squash
50 files changed, 12948 insertions(+), 0 deletions(-)

A .gitignore
A api-flask/.gitignore
A api-flask/Procfile
A api-flask/api.py
A api-flask/database.py
A api-flask/extensions/flask_jwt/__init__.py
A api-flask/jwt_utility.py
A api-flask/models.py
A api-flask/requirements.txt
A api-flask/schemas.py
A api-flask/security.py
A api-flask/services.py
A database-postgresql/.gitignore
A database-postgresql/create.sql
A ui-react/.gitignore
A ui-react/package-lock.json
A ui-react/package.json
A ui-react/public/favicon.ico
A ui-react/public/footer.css
A ui-react/public/images/artwork/fox.png
A ui-react/public/images/artwork/reindeer.png
A ui-react/public/index.html
A ui-react/public/manifest.json
A ui-react/src/Auth.js
A ui-react/src/api/GroupApi.js
A ui-react/src/api/InvitationApi.js
A ui-react/src/api/ListApi.js
A ui-react/src/api/SessionApi.js
A ui-react/src/api/common.js
A ui-react/src/components/App.js
A ui-react/src/components/AuthCallback.js
A ui-react/src/components/common/Footer.js
A ui-react/src/components/common/Header.js
A ui-react/src/components/common/ListItemButtons.js
A ui-react/src/components/groups/GroupPage.js
A ui-react/src/components/groups/InviteGroupMemberModal.js
A ui-react/src/components/groups/ListCard.js
A ui-react/src/components/groups/MemberCard.js
A ui-react/src/components/home/Groups.js
A ui-react/src/components/home/HomePage.js
A ui-react/src/components/invitations/InvitationPage.js
A ui-react/src/components/lists/ListItemModal.js
A ui-react/src/components/lists/ListPage.js
A ui-react/src/components/login/LoginFailedPage.js
A ui-react/src/components/login/LoginPage.js
A ui-react/src/history.js
A ui-react/src/index.js
A ui-react/src/predicates.js
A ui-react/src/utility.js
A ui-react/static.json
A  => .gitignore +1 -0
@@ 1,1 @@
*.swp

A  => api-flask/.gitignore +3 -0
@@ 1,3 @@
__pycache__
VIRTUALENV
.env

A  => api-flask/Procfile +1 -0
@@ 1,1 @@
web: gunicorn api:app

A  => api-flask/api.py +354 -0
@@ 1,354 @@
#!/usr/bin/env python

from os import path, environ as env

from dotenv import load_dotenv

from flask import Flask, request, jsonify, abort
from flask_restful import Api, Resource
from flask_cors import CORS
from flask_jwt import jwt_required, current_identity
from jwt_utility import jwks_load_rs256_key

from database import db
from services import get_user_by_email_address, get_user_by_authentication_subject, add_user, get_groups, get_group, get_group_member_names, invite_group_member, get_group_purchases_summary, get_invitation, accept_invitation, get_list, add_list, update_list, get_item, add_item, update_item, move_item, reserve_item, purchase_item, delete_item
from schemas import ma, _create_schema_context, _get_context_user_id, _get_group_member_name, UserSchema, GroupSchema, GroupSummarySchema, GroupMemberSchema, AddGroupMemberRequestSchema, GroupInvitationSchema, GroupPurchasesSummarySchema, ListSchema, ListSummarySchema, ItemReservationSchema, ItemPurchaseSchema, ItemSchema
from security import jwt

#

load_dotenv(path.join(path.dirname(__file__), ".env"))

app = Flask(__name__)

app.config["JSON_SORT_KEYS"] = False

app.config["SQLALCHEMY_DATABASE_URI"] = env["DATABASE_URL"]
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

#if app.debug:
#    app.config["SQLALCHEMY_ECHO"] = True

app.config["JWT_AUTH_URL_RULE"] = None
app.config["JWT_AUTH_HEADER_PREFIX"] = "Bearer"
app.config["JWT_ALGORITHM"] = "RS256"
app.config["JWT_SECRET_KEY"] = jwks_load_rs256_key(env["JWKS_URL"])
app.config["JWT_AUDIENCE"] = env["JWT_AUDIENCE"]
app.config["JWT_VERIFY_CLAIMS"] = ["signature", "exp", "iat"]
app.config["JWT_REQUIRED_CLAIMS"] = ["exp", "iat"]

db.init_app(app)
ma.init_app(app)
jwt.init_app(app)

api = Api(app)

CORS(app)

#

def _get_group_or_abort(group_id):
    group = get_group(db.session, group_id)

    if group is None:
        abort(404)

    if not current_identity.user.id in [user.id for user in group.users]:
        abort(403)

    return group

def _get_invitation_or_abort(invitation_id):
    invitation = get_invitation(db.session, invitation_id)

    if invitation is None:
        abort(404)

    return invitation

def _get_list_or_abort(list_id):
    list = get_list(db.session, list_id)

    if list is None:
        abort(404)

    if not current_identity.user.id in [user.id for user in list.group.users]:
        abort(403)

    return list

def _get_item_or_abort(item_id):
    item = get_item(db.session, item_id)

    if item is None:
        abort(404)

    if not current_identity.user.id in [user.id for user in item.list.group.users]:
        abort(403)

    return item

#

@app.route("/api/login", methods=["POST"])
@jwt_required()
def login():
    user = current_identity.user

    if user is None:
        invitation_id = request.args.get("invitationId", None)

        if invitation_id is None:
            abort(418)  # TODO
        
        invitation = get_invitation(db.session, invitation_id)

        if invitation is None:
            abort(418)  # TODO

        if invitation.accepted_date_time is not None:
            abort(418)  # TODO

        # NOTE: this does not actually accept the invitation - it just creates the user,
        #       the client is responsible for accepting the invitation afterwords.
        user = add_user(db.session, invitation.first_name, invitation.last_name, invitation.email_address, current_identity.jwt_payload["sub"])

    response_schema = UserSchema()

    return response_schema.jsonify(user)

@app.route("/api/logout", methods=["POST"])
@jwt_required()
def logout():
    user = current_identity.user

    return jsonify(True)

class GroupsResource(Resource):
    @jwt_required()
    def get(self):
        groups = get_groups(db.session, current_identity.user.id)

        response_schema = GroupSummarySchema(many=True)

        return response_schema.jsonify(groups)

class GroupResource(Resource):
    @jwt_required()
    def get(self, group_id):
        group = _get_group_or_abort(group_id)

        context = _create_schema_context(current_identity.user.id, group_member_names=get_group_member_names(group))

        response_schema = GroupSchema(context=context)
        
        return response_schema.jsonify(group)

class GroupInvitationsResource(Resource):
    @jwt_required()
    def post(self, group_id):
        group = _get_group_or_abort(group_id)

        if not request.is_json:
            abort(415)

        request_schema = AddGroupMemberRequestSchema()

        (data, errors) = request_schema.loads(request.data)

        if errors:
            return jsonify(errors), 422

        group_member = invite_group_member(db.session, group.id, data["first_name"], data["last_name"], data["email_address"], current_identity.user.id)

        context = _create_schema_context(current_identity.user.id, group_member_names=get_group_member_names(group))

        response_schema = GroupMemberSchema(context=context)

        return response_schema.jsonify(group_member)

class GroupListsResource(Resource):
    @jwt_required()
    def post(self, group_id):
        group = _get_group_or_abort(group_id)

        if not request.is_json:
            abort(415)

        # TODO: Use Marshmallow to perform validation.
        data = request.get_json()

        # List name must be unique - user can only have one personal list.
        if data["name"] in [list.name for list in group.lists if list.user_id == current_identity.user.id]:
            abort(409)

        list = add_list(db.session, group_id, current_identity.user.id, data["name"])

        context = _create_schema_context(current_identity.user.id, group_member_names=get_group_member_names(list.group))

        response_schema = ListSchema(context=context)

        return response_schema.jsonify(list)

class GroupPurchasesSummaryResource(Resource):
    @jwt_required()
    def get(self, group_id):
        group = _get_group_or_abort(group_id)

        group_summary = get_group_purchases_summary(db.session, group.id, current_identity.user.id)
        
        context = _create_schema_context(current_identity.user.id, group_member_names=get_group_member_names(group))

        response_schema = GroupPurchasesSummarySchema(context=context)

        return response_schema.jsonify(group_summary)

class InvitationResource(Resource):
    def get(self, invitation_id):
        invitation = _get_invitation_or_abort(invitation_id)

        response_schema = GroupInvitationSchema()

        return response_schema.jsonify(invitation)

    @jwt_required()
    def post(self, invitation_id):
        _get_invitation_or_abort(invitation_id)

        data = request.get_json()

        invitation = None

        # TODO: Redesign this.
        if data["action"] == "ACCEPT":
            invitation = accept_invitation(db.session, invitation_id, current_identity.user.id)

        response_schema = GroupInvitationSchema()

        return response_schema.jsonify(invitation)

class ListResource(Resource):
    @jwt_required()
    def get(self, list_id):
        list = _get_list_or_abort(list_id)

        context = _create_schema_context(current_identity.user.id, group_member_names=get_group_member_names(list.group))

        response_schema = ListSchema(context=context)

        return response_schema.jsonify(list)

    @jwt_required()
    def put(self, list_id):
        _get_list_or_abort(list_id)

        if not request.is_json:
            abort(415)

        # TODO: Use Marshmallow to perform validation.
        data = request.get_json()

        list = update_list(db.session, list_id, data["draft"], current_identity.user.id)

        context = _create_schema_context(current_identity.user.id, group_member_names=get_group_member_names(list.group))

        response_schema = ListSchema(context=context)

        return response_schema.jsonify(list)

class ListItemsResource(Resource):
    @jwt_required()
    def post(self, list_id):
        list = _get_list_or_abort(list_id)

        if not request.is_json:
            abort(415)

        request_schema = ItemSchema(only=["name", "store", "url", "notes"])

        (data, errors) = request_schema.loads(request.data)

        if errors:
            return jsonify(errors), 422

        item = add_item(db.session, list.id, data["name"], data["store"], data["url"], data["notes"])

        context = _create_schema_context(current_identity.user.id, group_member_names=get_group_member_names(item.list.group))

        response_schema = ItemSchema(context=context)

        return response_schema.jsonify(item)

class ItemResource(Resource):
    @jwt_required()
    def patch(self, item_id):
        _get_item_or_abort(item_id)

        if request.content_type != "application/merge-patch+json":
            abort(415)

        request_schema = ItemSchema(only=["name", "ordinal", "store", "url", "notes"], partial=True)

        (data, errors) = request_schema.loads(request.data)

        if errors:
            return jsonify(errors), 422

        updatable_attributes = set(["name", "store", "url", "notes"])

        if updatable_attributes.intersection(data.keys()):
            values = { key: value for (key, value) in data.items() if key in updatable_attributes }

            item = update_item(db.session, item_id, values, current_identity.user.id)

        if "ordinal" in data:
            item = move_item(db.session, item_id, data["ordinal"], current_identity.user.id)

        context = _create_schema_context(current_identity.user.id, group_member_names=get_group_member_names(item.list.group))

        response_schema = ItemSchema(context=context)

        return response_schema.jsonify(item)

    @jwt_required()
    def post(self, item_id):
        _get_item_or_abort(item_id)

        if not request.is_json:
            abort(415)

        # TODO: Use Marshmallow to perform validation.
        data = request.get_json()

        item = None

        # TODO: Redesign this.
        if data["action"] == "RESERVE":
            item = reserve_item(db.session, item_id, current_identity.user.id)
        elif data["action"] == "PURCHASE":
            item = purchase_item(db.session, item_id, current_identity.user.id)

        context = _create_schema_context(current_identity.user.id, group_member_names=get_group_member_names(item.list.group))

        response_schema = ItemSchema(context=context)
        
        return response_schema.jsonify(item)

    @jwt_required()
    def delete(self, item_id):
        item = _get_item_or_abort(item_id)

        delete_item(db.session, item_id, current_identity.user.id)

api.add_resource(GroupsResource, "/api/groups")
api.add_resource(GroupResource, "/api/groups/<uuid:group_id>")
api.add_resource(GroupInvitationsResource, "/api/groups/<uuid:group_id>/invitations")
api.add_resource(GroupListsResource, "/api/groups/<uuid:group_id>/lists")
api.add_resource(GroupPurchasesSummaryResource, "/api/groups/<uuid:group_id>/purchases/summary")

api.add_resource(InvitationResource, "/api/invitations/<uuid:invitation_id>")

api.add_resource(ListResource, "/api/lists/<uuid:list_id>")
api.add_resource(ListItemsResource, "/api/lists/<uuid:list_id>/items")

api.add_resource(ItemResource, "/api/items/<uuid:item_id>")

A  => api-flask/database.py +3 -0
@@ 1,3 @@
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

A  => api-flask/extensions/flask_jwt/__init__.py +31 -0
@@ 1,31 @@
from jwt import decode as jwt_decode

from flask import current_app

# Based on _default_jwt_decode_handler with support for audience
def jwt_decode_handler(token):
    secret = current_app.config['JWT_SECRET_KEY']
    algorithm = current_app.config['JWT_ALGORITHM']
    leeway = current_app.config['JWT_LEEWAY']

    verify_claims = current_app.config['JWT_VERIFY_CLAIMS']
    required_claims = current_app.config['JWT_REQUIRED_CLAIMS']

    audience = current_app.config.get("JWT_AUDIENCE", None)

    options = {
        'verify_' + claim: True
        for claim in verify_claims
    }

    options.update({
        'require_' + claim: True
        for claim in required_claims
    })

    # By default PyJWT will verify the aud attribute which will fail if no audience is
    # specified.
    if audience is None:
        options["verify_aud"] = False

    return jwt_decode(token, secret, options=options, algorithms=[algorithm], leeway=leeway, audience=audience)

A  => api-flask/jwt_utility.py +17 -0
@@ 1,17 @@
import json

import requests
from jwt.algorithms import RSAAlgorithm

def jwks_load_rs256_key(url):
    response = requests.get(url)

    data = response.json()

    key_data = next(iter([key for key in data["keys"] if key["alg"] == "RS256"]), None)

    algo = RSAAlgorithm(RSAAlgorithm.SHA256)

    key = algo.from_jwk(json.dumps(key_data))

    return key

A  => api-flask/models.py +191 -0
@@ 1,191 @@
from itertools import chain

from sqlalchemy import Table, Column, ForeignKey, Integer, String, Boolean, DateTime
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy_utils.types.uuid import UUIDType

Base = declarative_base()

group_users = Table("group_users", Base.metadata,
    Column("group_id", UUIDType(binary=False), ForeignKey("groups.id")),
    Column("user_id", UUIDType(binary=False), ForeignKey("users.id"))
)

class User(Base):
    __tablename__ = "users"

    id = Column(UUIDType(binary=False), primary_key=True)
    first_name = Column(String(length=50))
    last_name = Column(String(length=50))
    email_address = Column(String(length=100))
    authentication_subject = Column(String(length=100))

    groups = relationship("Group", secondary=group_users, back_populates="users")

class Group(Base):
    __tablename__ = "groups"

    id = Column(UUIDType(binary=False), primary_key=True)
    name = Column(String(length=100))
    show_reserver = Column(Boolean)
    show_purchaser = Column(Boolean)

    users = relationship("User", secondary=group_users, back_populates="groups")
    invitations = relationship("GroupInvitation")
    lists = relationship("List")

    def get_active_invitations(self):
        return [invitation for invitation in self.invitations if invitation.accepted_date_time is None]

    def get_members(self):
        return list(chain(self.users, self.get_active_invitations()))

class GroupInvitation(Base):
    __tablename__ = "group_invitations"

    id = Column(UUIDType(binary=False), primary_key=True)
    group_id = Column(UUIDType(binary=False), ForeignKey("groups.id"))
    first_name = Column(String(length=50))
    last_name = Column(String(length=50))
    email_address = Column(String(length=100))
    accepted_date_time = Column(DateTime)
    accepted_by_user_id = Column(UUIDType(binary=False), ForeignKey("users.id"))
    created_date_time = Column(DateTime)
    created_by_user_id = Column(UUIDType(binary=False), ForeignKey("users.id"))

    group = relationship("Group", innerjoin=True)
    accepted_by = relationship("User", foreign_keys=[accepted_by_user_id])
    created_by = relationship("User", foreign_keys=[created_by_user_id])

class List(Base):
    __tablename__ = "lists"

    id = Column(UUIDType(binary=False), primary_key=True)
    group_id = Column(UUIDType(binary=False), ForeignKey("groups.id"))
    user_id = Column(UUIDType(binary=False), ForeignKey("users.id"))
    name = Column(String(length=100))
    draft = Column(Boolean)

    group = relationship("Group", innerjoin=True)
    user = relationship("User", innerjoin=True)
    items = relationship("Item", order_by="Item.ordinal")

    def are_items_visible(self, user_id):
        if self.draft and self.user_id != user_id:
            return False

        return True

    def can_add_item(self, user_id):
        if self.user_id != user_id:
            return (False, "Cannot add item to other user's list.")

        return (True, None)

class Item(Base):
    __tablename__ = "items"

    id = Column(UUIDType(binary=False), primary_key=True)
    list_id = Column(UUIDType(binary=False), ForeignKey("lists.id"))
    name = Column(String(length=100))
    ordinal = Column(Integer)
    store = Column(String(length=100))
    url = Column(String)
    notes = Column(String)
    reserved_by_user_id = Column(UUIDType(binary=False), ForeignKey("users.id"))
    purchased_by_user_id = Column(UUIDType(binary=False), ForeignKey("users.id"))

    list = relationship("List", innerjoin=True)
    reserved_by = relationship("User", foreign_keys=[reserved_by_user_id])
    purchased_by = relationship("User", foreign_keys=[purchased_by_user_id])

    def is_reservation_visible(self, user_id):
        if self.list.user_id == user_id and not self.list.name:
            return False

        return True

    def is_reserver_visible(self, user_id):
        if self.reserved_by_user_id == user_id:
            return True

        return self.list.group.show_reserver

    def can_reserve_item(self, user_id, allow_toggle=True):
        if self.list.user_id == user_id and not self.list.name:
            return (False, "Cannot reserve item from own personal list.")

        if self.list.draft:
            return (False, "Cannot reserve item from draft list.")

        if self.purchased_by_user_id:
            return (False, "Item was purchased.")

        if self.reserved_by_user_id and not allow_toggle:
            return (False, "Item is reserved and allow toggle not enabled.")

        if self.reserved_by_user_id and self.reserved_by_user_id != user_id:
            return (False, "Item is reserved by a different user.")

        return (True, None)

    def is_purchase_visible(self, user_id):
        if self.list.user_id == user_id and not self.list.name:
            return False

        return True

    def is_purchaser_visible(self, user_id):
        if self.purchased_by_user_id == user_id:
            return True

        return self.list.group.show_purchaser

    def can_purchase_item(self, user_id, allow_toggle=True):
        if self.list.user_id == user_id and not self.list.name:
            return (False, "Cannot purchase item from own personal list.")

        if self.list.draft:
            return (False, "Cannot purchase item from draft list.")

        if self.purchased_by_user_id and not allow_toggle:
            return (False, "Item is purchased and allow toggle not enabled.")

        if self.purchased_by_user_id and self.purchased_by_user_id != user_id:
            return (False, "Item was purchased by a different user.")

        if self.reserved_by_user_id and self.reserved_by_user_id != user_id:
            return (False, "Item is reserved by a different user.")

        return (True, None)

    def can_edit_item(self, user_id):
        if self.list.user_id != user_id:
            return (False, "Cannot edit item from other user's list.")

        if not self.list.draft:
            return (False, "Cannot edit item from non-draft list.")

        if self.reserved_by_user_id:
            return (False, "Item is reserved.")

        if self.purchased_by_user_id:
            return (False, "Item was purchased.")

        return (True, None)

    def can_delete_item(self, user_id):
        if self.list.user_id != user_id:
            return (False, "Cannot delete item from other user's list.")

        if not self.list.draft:
            return (False, "Cannot delete item from non-draft list.")

        if self.reserved_by_user_id:
            return (False, "Item is reserved.")

        if self.purchased_by_user_id:
            return (False, "Item was purchased.")

        return (True, None)

A  => api-flask/requirements.txt +17 -0
@@ 1,17 @@
cryptography
Flask
Flask-Cors
Flask-JWT
flask-marshmallow
Flask-RESTful
Flask-SQLAlchemy
PyJWT==1.5.2
python-dotenv
requests
pika
stringcase
sqlalchemy_utils
psycopg2
marshmallow_sqlalchemy
gunicorn
git+https://bitbucket.org/ajk/python-uniquenames

A  => api-flask/schemas.py +285 -0
@@ 1,285 @@
from werkzeug.datastructures import MultiDict

from marshmallow import fields, pre_load, post_load
from marshmallow.utils import get_func_args

from flask_marshmallow import Marshmallow

from uniquenames.formatters import full_name

from models import User, Group, GroupInvitation, List, Item

#

class SelfNested(fields.Nested):
    def __init__(self, nested, dump_when=None, **kwargs):
        self.dump_when = dump_when

        super(SelfNested, self).__init__(nested, **kwargs)

    def get_value(self, obj, attr, accessor=None):
        return attr

    def _serialize(self, value, attr, obj):
        if self.dump_when and not self._call_with_context(self.dump_when, obj):
            return self.default

        return super(SelfNested, self)._serialize(value, attr, obj)

    def _call_with_context(self, func, value):
        if len(get_func_args(func)) > 1:
            if self.parent.context is None:
                raise ValidationError("No context available for dump_when")

            return func(value, self.parent.context)
        else:
            return func(value)

#

def _create_schema_context(user_id, group_member_names=None):
    return { "USER_ID": user_id, "GROUP_MEMBER_NAMES": group_member_names }

def _get_context_user_id(context):
    return context["USER_ID"] if "USER_ID" in context else None

def _get_group_member_name(context, id, first_name, last_name):
    if "GROUP_MEMBER_NAMES" in context and id in context["GROUP_MEMBER_NAMES"]:
        return context["GROUP_MEMBER_NAMES"][id]

    return full_name((first_name, last_name))

#

ma = Marshmallow()

#

class UserSchema(ma.Schema):
    class Meta:
        model = User
        fields = ["id", "name", "first_name"]
        ordered = True

    name = fields.Function(lambda user, context: _get_group_member_name(context, user.id, user.first_name, user.last_name))
    first_name = fields.String(dump_to="firstName")

class GroupSchema(ma.Schema):
    class Meta:
        model = Group
        fields = ["id", "name", "members", "lists"]
        ordered = True

    members = fields.Method("_serialize_members")
    lists = fields.Method("_serialize_lists")

    def _serialize_members(self, group):
        lists_by_user_id = MultiDict([(list.user_id, list) for list in group.lists])

        context = { "LISTS_BY_USER_ID": lists_by_user_id }
 
        member_schema = GroupMemberSchema(context={ **self.context, **context })

        data, errors = member_schema.dump(group.get_members(), many=True)

        return data

    def _serialize_lists(self, group):
        list_summary_schema = ListSummarySchema(context=self.context)

        data, errors = list_summary_schema.dump(group.lists, many=True)

        return data

class GroupSummarySchema(GroupSchema):
    class Meta:
        fields = ["id", "name"]

class GroupMemberSchema(ma.Schema):
    class Meta:
        fields = ["type", "id", "name", "first_name", "invitation", "lists"]
        ordered = True

    type = fields.Method("_serialize_type")
    id = fields.UUID()
    name = fields.Function(lambda member, context: _get_group_member_name(context, member.id, member.first_name, member.last_name))
    first_name = fields.String(dump_to="firstName")
    invitation = SelfNested("GroupInvitationSchema", dump_when=lambda member: type(member).__name__ == "GroupInvitation", default=None, only=["created_date_time"])
    lists = fields.Method("_serialize_lists")

    def _serialize_type(self, member):
        type_name = type(member).__name__

        if type_name == "User":
            return "user"
        elif type_name == "GroupInvitation":
            return "invitation"

        return type_name

    def _serialize_lists(self, member):
        if not type(member).__name__ == "User":
            return []

        # TODO: This may not be entirely valid - this is used in the case where a
        #      group member has been added and we KNOW they have no lists.
        if not "LISTS_BY_USER_ID" in self.context:
            return []

        return [list.id for list in self.context["LISTS_BY_USER_ID"].getlist(member.id)]

class AddGroupMemberRequestSchema(ma.Schema):
    class Meta:
        fields = ["first_name", "last_name", "email_address"]
        ordered = True

    first_name = fields.String(required=True, load_from="firstName")
    last_name = fields.String(load_from="lastName")
    email_address = fields.Email(required=True, load_from="emailAddress")

    @pre_load
    def pre_load(self, data):
        data["firstName"] = data["firstName"].strip()
        data["lastName"] = data["lastName"].strip()
        data["emailAddress"] = data["emailAddress"].strip()

    @post_load
    def post_load(self, data):
        # TODO: Should this even be done here or should it be done in the "DAO"?
        last_name = data["last_name"]

        data["last_name"] = last_name if last_name else None

class GroupInvitationSchema(ma.Schema):
    class Meta:
        model = GroupInvitation
        fields = ["id", "group", "name", "first_name", "email_address", "accepted_date_time", "created_date_time", "created_by"]
        ordered = True
    
    group = fields.Nested("GroupSummarySchema")
    name = fields.Function(lambda invitation, context: _get_group_member_name(context, invitation.id, invitation.first_name, invitation.last_name))
    first_name = fields.String(dump_to="firstName")
    email_address = fields.Email(dump_to="emailAddress")
    accepted_date_time = fields.DateTime(dump_to="acceptedDateTime")
    created_date_time = fields.DateTime(dump_to="createdDateTime")
    created_by = fields.Nested("UserSchema", dump_to="createdBy")

class GroupPurchasesSummarySchema(ma.Schema):
    class Meta:
        ordered = True

    purchased_from = fields.Nested("ListSummarySchema", many=True, dump_to="purchasedFrom")
    not_purchased_from = fields.Nested("ListSummarySchema", many=True, dump_to="notPurchasedFrom")

class ListSchema(ma.Schema):
    class Meta:
        model = List
        fields = ["id", "group", "user", "name", "type", "draft", "items", "can_add_item"]
        ordered = True

    group = fields.Nested("GroupSummarySchema")
    user = fields.Nested("UserSchema")
    name = fields.Function(lambda list, context: list.name or _get_group_member_name(context, list.user.id, list.user.first_name, list.user.last_name))
    type = fields.Function(lambda list: "Proxy" if list.name else "Personal")
    items = fields.Method("_serialize_items")

    can_add_item = fields.Function(lambda list, context: list.can_add_item(_get_context_user_id(context))[0], dump_to="canAddItem")

    def _serialize_items(self, list):
        if not list.are_items_visible(_get_context_user_id(self.context)):
            return None

        items_schema = ItemSchema(many=True, context=self.context)

        data, errors = items_schema.dump(list.items)

        return data

class ListSummarySchema(ListSchema):
    class Meta:
        fields = ["id", "user", "name", "type", "draft"]
        ordered = True

    user = fields.Nested("UserSchema", only=["id", "name", "first_name"])

class ItemReservationSchema(ma.Schema):
    class Meta:
        model = Item
        fields = ["reserved", "user"]
        ordered = True

    reserved = fields.Constant(True)
    user = fields.Method("_serialize_user")

    def _serialize_user(self, item):
        if not item.is_reserver_visible(_get_context_user_id(self.context)):
            return None

        user_schema = UserSchema(context=self.context)

        data, errors = user_schema.dump(item.reserved_by)

        return data

class ItemPurchaseSchema(ma.Schema):
    class Meta:
        model = Item
        fields = ["purchased", "user"]
        ordered = True

    purchased = fields.Constant(True)
    user = fields.Method("_serialize_user")

    def _serialize_user(self, item):
        if not item.is_purchaser_visible(_get_context_user_id(self.context)):
            return None

        user_schema = UserSchema(context=self.context)

        data, errors = user_schema.dump(item.purchased_by)

        return data

class ItemSchema(ma.Schema):
    class Meta:
        model = Item
        fields = ["id", "name", "ordinal", "store", "url", "notes", "reservation", "can_reserve", "purchase", "can_purchase", "can_edit", "can_delete"]
        ordered = True

    reservation = SelfNested("ItemReservationSchema", dump_when=lambda item, context: item.reserved_by_user_id and item.is_reservation_visible(_get_context_user_id(context)), default=None)
    can_reserve = fields.Function(lambda item, context: item.can_reserve_item(_get_context_user_id(context))[0], dump_to="canReserve")

    purchase = SelfNested("ItemPurchaseSchema", dump_when=lambda item, context: item.purchased_by_user_id and item.is_purchase_visible(_get_context_user_id(context)), default=None)
    can_purchase = fields.Function(lambda item, context: item.can_purchase_item(_get_context_user_id(context))[0], dump_to="canPurchase")

    can_edit = fields.Function(lambda item, context: item.can_edit_item(_get_context_user_id(context))[0], dump_to="canEdit")
    can_delete = fields.Function(lambda item, context: item.can_delete_item(_get_context_user_id(context))[0], dump_to="canDelete")

    @pre_load
    def pre_load(self, data):
        if "name" in data:
            data["name"] = data["name"].strip()

        if "store" in data:
            data["store"] = data["store"].strip()

        if "url" in data:
            data["url"] = data["url"].strip()

        if "notes" in data:
            data["notes"] = data["notes"].strip()

    @post_load
    def post_load(self, data):
        # TODO: Should this even be done here or should it be done in the "DAO"?
        if "store" in data:
            store = data["store"]
            data["store"] = store if store else None

        if "url" in data:
            url = data["url"]
            data["url"] = url if url else None

        if "notes" in data:
            notes = data["notes"]
            data["notes"] = notes if notes else None

A  => api-flask/security.py +18 -0
@@ 1,18 @@
from collections import namedtuple

from flask_jwt import JWT
from extensions.flask_jwt import jwt_decode_handler

from database import db
from services import get_user_by_authentication_subject

Identity = namedtuple("Identity", ["user", "jwt_payload"])

def jwt_identity(payload):
    user = get_user_by_authentication_subject(db.session, payload["sub"])

    return Identity(user, payload)

jwt = JWT(identity_handler=jwt_identity)

jwt.jwt_decode_callback = jwt_decode_handler

A  => api-flask/services.py +226 -0
@@ 1,226 @@
from datetime import datetime
from uuid import uuid4

from sqlalchemy import select, func
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.expression import not_, and_, bindparam

from uniquenames import uniquenames
from uniquenames.formatters import first_name, first_name_and_last_initial, full_name

from models import User, Group, GroupInvitation, List, Item

def get_user_by_email_address(session, email_address):
    return session.query(User).filter(User.email_address == email_address).one_or_none()

def get_user_by_authentication_subject(session, authentication_subject):
    return session.query(User).filter(User.authentication_subject == authentication_subject).one_or_none()

def add_user(session, first_name, last_name, email_address, authentication_subject):
    id = uuid4()

    user = User(id=id, first_name=first_name, last_name=last_name, email_address=email_address, authentication_subject=authentication_subject)

    session.add(user)
    session.commit()

    return user

def get_groups(session, user_id):
    return session.query(Group).filter(Group.users.any(User.id == user_id)).order_by("name").all()

def get_group(session, id):
    return session.query(Group).options(joinedload(Group.users), joinedload(Group.invitations), joinedload(Group.lists)).get(id)

# TODO: should this be moved to the Group model?
def get_group_member_names(group):
    members = group.get_members()

    proxy_list_names = [list.name for list in group.lists if list.name is not None]

    names = uniquenames([(member.first_name, member.last_name) for member in members], formatters=[first_name, first_name_and_last_initial, full_name], additional_names=proxy_list_names, canonicalizer=lambda name: name.upper())

    return { pair[0].id: pair[1] for pair in zip(members, names) }

def get_invitation(session, id):
    return session.query(GroupInvitation).get(id)

def invite_group_member(session, group_id, first_name, last_name, email_address, current_user_id):
    group = session.query(Group).get(group_id)

    # If a user with the email address already exists create an invitation for the user.
    #
    # TODO: All group members should be added through invitation not directly
    #       added to a group.
    existing_user = get_user_by_email_address(session, email_address)

    if existing_user is not None:
        if existing_user.id in [user.id for user in group.users]:
            return None  # TODO: what should this error be - user exists!

        # TODO: We loose track of who added the user here!
        group.users.append(existing_user)

        session.commit()

        return existing_user

    # Create a new invitation.
    if email_address in [invitation.email_address for invitation in group.invitations]:
        return None  # TODO: what should this error be - invitation exists!

    invitation = GroupInvitation(id=uuid4(), group_id=group.id, first_name=first_name, last_name=last_name, email_address=email_address, created_date_time=datetime.utcnow(), created_by_user_id=current_user_id)

    session.add(invitation)

    session.commit()

    return invitation

def accept_invitation(session, id, user_id):
    invitation = session.query(GroupInvitation).get(id)
    group = session.query(Group).get(invitation.group_id)
    user = session.query(User).get(user_id)

    if invitation.accepted_date_time is not None:
        raise Exception("Invitation has already been accepted.")

    invitation.accepted_date_time = datetime.utcnow()
    invitation.accepted_by_user_id = user_id

    group.users.append(user)

    session.commit()

    return invitation

def get_group_purchases_summary(session, id, user_id):
    purchased_items_query = session.query(Item).join(List).filter(List.group_id == id, Item.purchased_by_user_id == user_id).subquery()

    results = session.query(List, func.count(purchased_items_query.c.id)).outerjoin(purchased_items_query, List.items).filter(List.group_id == id, not_(and_(List.user_id == user_id, List.name.is_(None)))).group_by(List.id).all()

    return { "purchased_from": [list for (list, count) in results if count > 0], "not_purchased_from": [list for (list, count) in results if count == 0] }

def get_list(session, id):
    return session.query(List).options(joinedload(List.group, innerjoin=True), joinedload(List.items)).get(id)

def add_list(session, group_id, user_id, name):
    id = uuid4()

    list = List(id=id, group_id=group_id, user_id=user_id, name=name, draft=True)

    session.add(list)

    session.commit()

    return list

def update_list(session, id, draft, user_id):
    list = session.query(List).get(id)

    list.draft = draft

    session.commit()

    return list

def get_item(session, id):
    return session.query(Item).get(id)

def add_item(session, list_id, name, store, url, notes):
    id = uuid4()

    ordinal = (session.query(func.max(Item.ordinal)).filter(Item.list_id == list_id).scalar() or 0) + 1

    item = Item(id=id, list_id=list_id, name=name, ordinal=ordinal, store=store, url=url, notes=notes)

    session.add(item)

    session.commit()

    return item

def update_item(session, id, values, user_id):
    item = session.query(Item).get(id)

    (valid, message) = item.can_edit_item(user_id)

    if not valid:
        raise Exception(message)

    for (key, value) in values.items():
        setattr(item, key, value)

    session.commit()

    return item

def move_item(session, id, ordinal, user_id):
    # TODO: very inefficient
    list_id = session.query(Item.list_id).filter(Item.id == id).scalar()

    items = session.query(Item.id).filter(Item.list_id == list_id).order_by(Item.ordinal)

    item_keys_excluding_item = [item.id for item in items if item.id != id]

    preceding_keys = item_keys_excluding_item[:ordinal - 1]
    following_keys = item_keys_excluding_item[ordinal - 1:]

    ordered_keys = preceding_keys + [id] + following_keys

    statement = Item.__table__.update().where(Item.id == bindparam("_id")).values({ "ordinal": bindparam("ordinal") })

    session.execute(statement, [{ "_id": key, "ordinal": index + 1 } for (index, key) in enumerate(ordered_keys)])

    session.commit()

    item = session.query(Item).get(id)

    return item

def reserve_item(session, id, user_id, allow_toggle=True):
    item = session.query(Item).get(id)

    (valid, message) = item.can_reserve_item(user_id, allow_toggle)

    if not valid:
        raise Exception(message)

    if not item.reserved_by_user_id:
        item.reserved_by_user_id = user_id
    else:
        item.reserved_by_user_id = None

    session.commit()

    return item

def purchase_item(session, id, user_id, allow_toggle=True):
    item = session.query(Item).get(id)

    (valid, message) = item.can_purchase_item(user_id, allow_toggle)

    if not valid:
        raise Exception(message)

    if not item.purchased_by_user_id:
        item.reserved_by_user_id = None
        item.purchased_by_user_id = user_id
    else:
        item.purchased_by_user_id = None

    session.commit()

    return item

def delete_item(session, id, user_id):
    item = session.query(Item).get(id)

    (valid, message) = item.can_delete_item(user_id)

    if not valid:
        raise Exception(message)

    session.delete(item)

    session.commit()

A  => database-postgresql/.gitignore +1 -0
@@ 1,1 @@
_*.sql

A  => database-postgresql/create.sql +85 -0
@@ 1,85 @@
create table users (
    id uuid not null,
    first_name varchar(50) not null,
    last_name varchar(50) null,
    email_address varchar(100) not null,
    authentication_subject varchar(100) not null,

    constraint pk_users primary key (id),

    constraint ux_users_email_address unique (email_address),
    constraint ux_users_authentication_subject unique (authentication_subject) );

create table groups (
    id uuid not null,
    name varchar(100) not null,
    show_reserver boolean not null,
    show_purchaser boolean not null,

    constraint pk_groups primary key (id) );

create table group_users (
    group_id uuid not null,
    user_id uuid not null,

    constraint pk_group_users primary key (group_id, user_id),

    constraint fk_group_users_groups foreign key (group_id) references groups (id),
    constraint fk_group_users_users foreign key (user_id) references users (id) );

create table group_invitations (
    id uuid not null,
    group_id uuid not null,
    first_name varchar(50) not null,
    last_name varchar(50) null,
    email_address varchar(100) not null,
    accepted_date_time timestamp null,
    accepted_by_user_id uuid null,
    created_date_time timestamp not null,
    created_by_user_id uuid not null,

    constraint pk_group_invitations primary key (id),

    constraint fk_group_invitations_groups foreign key (group_id) references groups (id),
    constraint fk_group_invitations_users_accepted_by foreign key (accepted_by_user_id) references users (id),
    constraint fk_group_invitations_users_created_by foreign key (created_by_user_id) references users (id),

    constraint ux_group_invitations_group_email_address unique (group_id, email_address) );

-- check constraint on accepted_date_time and accepted_by_user_id if one provided both must be provided

create table lists (
    id uuid not null,
    group_id uuid not null,
    user_id uuid not null,
    name varchar(100) null,
    draft boolean not null,

    constraint pk_lists primary key (id),

    constraint fk_lists_groups foreign key (group_id) references groups (id),
    constraint fk_lists_users foreign key (user_id) references users (id),

    constraint ux_lists unique (group_id, user_id, name) );

create table items (
    id uuid not null,
    list_id uuid not null,
    name varchar(100) not null,
    ordinal integer not null,
    store varchar(100) null,
    url text null,
    notes text null,
    reserved_by_user_id uuid null,
    purchased_by_user_id uuid null,

    constraint pk_items primary key (id),

    constraint fk_items_lists foreign key (list_id) references lists (id),
    constraint fk_items_users_reserved_by foreign key (reserved_by_user_id) references users (id),
    constraint fk_items_users_purchased_by foreign key (purchased_by_user_id) references users (id) );

    -- constraint below won't work as it is violated during an item move
    --constraint ux_items_ordinal unique (list_id, ordinal) );

create index ix_items_list on items (list_id);

A  => ui-react/.gitignore +22 -0
@@ 1,22 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.

# dependencies
/node_modules

# testing
/coverage

# production
/build

# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

A  => ui-react/package-lock.json +9705 -0
@@ 1,9705 @@
{
  "name": "santas-little-helper",
  "version": "0.1.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "abab": {
      "version": "1.0.4",
      "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
      "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4="
    },
    "accepts": {
      "version": "1.3.4",
      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
      "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
      "requires": {
        "mime-types": "2.1.17",
        "negotiator": "0.6.1"
      }
    },
    "acorn": {
      "version": "5.2.1",
      "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz",
      "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w=="
    },
    "acorn-dynamic-import": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz",
      "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=",
      "requires": {
        "acorn": "4.0.13"
      },
      "dependencies": {
        "acorn": {
          "version": "4.0.13",
          "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
          "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
        }
      }
    },
    "acorn-globals": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz",
      "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=",
      "requires": {
        "acorn": "4.0.13"
      },
      "dependencies": {
        "acorn": {
          "version": "4.0.13",
          "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
          "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
        }
      }
    },
    "acorn-jsx": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
      "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
      "requires": {
        "acorn": "3.3.0"
      },
      "dependencies": {
        "acorn": {
          "version": "3.3.0",
          "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
          "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
        }
      }
    },
    "address": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz",
      "integrity": "sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg=="
    },
    "ajv": {
      "version": "5.3.0",
      "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz",
      "integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=",
      "requires": {
        "co": "4.6.0",
        "fast-deep-equal": "1.0.0",
        "fast-json-stable-stringify": "2.0.0",
        "json-schema-traverse": "0.3.1"
      }
    },
    "ajv-keywords": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
      "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I="
    },
    "align-text": {
      "version": "0.1.4",
      "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
      "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
      "requires": {
        "kind-of": "3.2.2",
        "longest": "1.0.1",
        "repeat-string": "1.6.1"
      }
    },
    "alphanum-sort": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
      "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM="
    },
    "amdefine": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
    },
    "ansi-align": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz",
      "integrity": "sha1-LwwWWIKXOa3V67FeawxuNCPwFro=",
      "requires": {
        "string-width": "1.0.2"
      },
      "dependencies": {
        "is-fullwidth-code-point": {
          "version": "1.0.0",
          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
          "requires": {
            "number-is-nan": "1.0.1"
          }
        },
        "string-width": {
          "version": "1.0.2",
          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
          "requires": {
            "code-point-at": "1.1.0",
            "is-fullwidth-code-point": "1.0.0",
            "strip-ansi": "3.0.1"
          }
        }
      }
    },
    "ansi-escapes": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz",
      "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ=="
    },
    "ansi-html": {
      "version": "0.0.7",
      "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
      "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4="
    },
    "ansi-regex": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
    },
    "ansi-styles": {
      "version": "3.2.0",
      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
      "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
      "requires": {
        "color-convert": "1.9.1"
      }
    },
    "anymatch": {
      "version": "1.3.2",
      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
      "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
      "requires": {
        "micromatch": "2.3.11",
        "normalize-path": "2.1.1"
      }
    },
    "append-transform": {
      "version": "0.4.0",
      "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz",
      "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=",
      "requires": {
        "default-require-extensions": "1.0.0"
      }
    },
    "argparse": {
      "version": "1.0.9",
      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
      "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
      "requires": {
        "sprintf-js": "1.0.3"
      }
    },
    "aria-query": {
      "version": "0.7.0",
      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.7.0.tgz",
      "integrity": "sha512-/r2lHl09V3o74+2MLKEdewoj37YZqiQZnfen1O4iNlrOjUgeKuu1U2yF3iKh6HJxqF+OXkLMfQv65Z/cvxD6vA==",
      "requires": {
        "ast-types-flow": "0.0.7"
      }
    },
    "arr-diff": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
      "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
      "requires": {
        "arr-flatten": "1.1.0"
      }
    },
    "arr-flatten": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
    },
    "array-equal": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
      "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM="
    },
    "array-filter": {
      "version": "0.0.1",
      "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
      "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw="
    },
    "array-find-index": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
      "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E="
    },
    "array-flatten": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz",
      "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY="
    },
    "array-includes": {
      "version": "3.0.3",
      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
      "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
      "requires": {
        "define-properties": "1.1.2",
        "es-abstract": "1.9.0"
      }
    },
    "array-map": {
      "version": "0.0.0",
      "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
      "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI="
    },
    "array-reduce": {
      "version": "0.0.0",
      "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
      "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys="
    },
    "array-union": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
      "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
      "requires": {
        "array-uniq": "1.0.3"
      }
    },
    "array-uniq": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
      "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY="
    },
    "array-unique": {
      "version": "0.2.1",
      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
      "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM="
    },
    "arrify": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
      "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="
    },
    "asap": {
      "version": "2.0.6",
      "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
    },
    "asn1": {
      "version": "0.2.3",
      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
      "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
    },
    "asn1.js": {
      "version": "4.9.2",
      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz",
      "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==",
      "requires": {
        "bn.js": "4.11.8",
        "inherits": "2.0.3",
        "minimalistic-assert": "1.0.0"
      }
    },
    "assert": {
      "version": "1.4.1",
      "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
      "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
      "requires": {
        "util": "0.10.3"
      }
    },
    "assert-plus": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
    },
    "ast-types-flow": {
      "version": "0.0.7",
      "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
      "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0="
    },
    "async": {
      "version": "2.6.0",
      "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
      "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
      "requires": {
        "lodash": "4.17.4"
      }
    },
    "async-each": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
      "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0="
    },
    "asynckit": {
      "version": "0.4.0",
      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
    },
    "autoprefixer": {
      "version": "7.1.6",
      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.1.6.tgz",
      "integrity": "sha512-C9yv/UF3X+eJTi/zvfxuyfxmLibYrntpF3qoJYrMeQwgUJOZrZvpJiMG2FMQ3qnhWtF/be4pYONBBw95ZGe3vA==",
      "requires": {
        "browserslist": "2.8.0",
        "caniuse-lite": "1.0.30000760",
        "normalize-range": "0.1.2",
        "num2fraction": "1.2.2",
        "postcss": "6.0.14",
        "postcss-value-parser": "3.3.0"
      }
    },
    "aws-sign2": {
      "version": "0.7.0",
      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
    },
    "aws4": {
      "version": "1.6.0",
      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
      "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
    },
    "axobject-query": {
      "version": "0.1.0",
      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz",
      "integrity": "sha1-YvWdvFnJ+SQnWco0mWDnov48NsA=",
      "requires": {
        "ast-types-flow": "0.0.7"
      }
    },
    "babel-code-frame": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
      "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
      "requires": {
        "chalk": "1.1.3",
        "esutils": "2.0.2",
        "js-tokens": "3.0.2"
      }
    },
    "babel-core": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz",
      "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=",
      "requires": {
        "babel-code-frame": "6.26.0",
        "babel-generator": "6.26.0",
        "babel-helpers": "6.24.1",
        "babel-messages": "6.23.0",
        "babel-register": "6.26.0",
        "babel-runtime": "6.26.0",
        "babel-template": "6.26.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0",
        "babylon": "6.18.0",
        "convert-source-map": "1.5.0",
        "debug": "2.6.9",
        "json5": "0.5.1",
        "lodash": "4.17.4",
        "minimatch": "3.0.4",
        "path-is-absolute": "1.0.1",
        "private": "0.1.8",
        "slash": "1.0.0",
        "source-map": "0.5.7"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        },
        "source-map": {
          "version": "0.5.7",
          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
        }
      }
    },
    "babel-eslint": {
      "version": "7.2.3",
      "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz",
      "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=",
      "requires": {
        "babel-code-frame": "6.26.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0",
        "babylon": "6.18.0"
      }
    },
    "babel-generator": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz",
      "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=",
      "requires": {
        "babel-messages": "6.23.0",
        "babel-runtime": "6.26.0",
        "babel-types": "6.26.0",
        "detect-indent": "4.0.0",
        "jsesc": "1.3.0",
        "lodash": "4.17.4",
        "source-map": "0.5.7",
        "trim-right": "1.0.1"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        },
        "source-map": {
          "version": "0.5.7",
          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
        }
      }
    },
    "babel-helper-builder-binary-assignment-operator-visitor": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz",
      "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=",
      "requires": {
        "babel-helper-explode-assignable-expression": "6.24.1",
        "babel-runtime": "6.23.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-helper-builder-react-jsx": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz",
      "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=",
      "requires": {
        "babel-runtime": "6.26.0",
        "babel-types": "6.26.0",
        "esutils": "2.0.2"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        }
      }
    },
    "babel-helper-call-delegate": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
      "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
      "requires": {
        "babel-helper-hoist-variables": "6.24.1",
        "babel-runtime": "6.23.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-helper-define-map": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
      "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=",
      "requires": {
        "babel-helper-function-name": "6.24.1",
        "babel-runtime": "6.26.0",
        "babel-types": "6.26.0",
        "lodash": "4.17.4"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        }
      }
    },
    "babel-helper-explode-assignable-expression": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz",
      "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=",
      "requires": {
        "babel-runtime": "6.23.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-helper-function-name": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
      "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
      "requires": {
        "babel-helper-get-function-arity": "6.24.1",
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-helper-get-function-arity": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
      "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
      "requires": {
        "babel-runtime": "6.23.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-helper-hoist-variables": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
      "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
      "requires": {
        "babel-runtime": "6.23.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-helper-optimise-call-expression": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
      "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
      "requires": {
        "babel-runtime": "6.23.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-helper-regex": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz",
      "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=",
      "requires": {
        "babel-runtime": "6.26.0",
        "babel-types": "6.26.0",
        "lodash": "4.17.4"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        }
      }
    },
    "babel-helper-remap-async-to-generator": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz",
      "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=",
      "requires": {
        "babel-helper-function-name": "6.24.1",
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-helper-replace-supers": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
      "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
      "requires": {
        "babel-helper-optimise-call-expression": "6.24.1",
        "babel-messages": "6.23.0",
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-helpers": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
      "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
      "requires": {
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0"
      }
    },
    "babel-jest": {
      "version": "20.0.3",
      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-20.0.3.tgz",
      "integrity": "sha1-5KA7E9wQOJ4UD8ZF0J/8TO0wFnE=",
      "requires": {
        "babel-core": "6.26.0",
        "babel-plugin-istanbul": "4.1.5",
        "babel-preset-jest": "20.0.3"
      }
    },
    "babel-loader": {
      "version": "7.1.2",
      "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.2.tgz",
      "integrity": "sha512-jRwlFbINAeyDStqK6Dd5YuY0k5YuzQUvlz2ZamuXrXmxav3pNqe9vfJ402+2G+OmlJSXxCOpB6Uz0INM7RQe2A==",
      "requires": {
        "find-cache-dir": "1.0.0",
        "loader-utils": "1.1.0",
        "mkdirp": "0.5.1"
      }
    },
    "babel-messages": {
      "version": "6.23.0",
      "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
      "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-check-es2015-constants": {
      "version": "6.22.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
      "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-dynamic-import-node": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-1.1.0.tgz",
      "integrity": "sha512-tTfZbM9Ecwj3GK50mnPrUpinTwA4xXmDiQGCk/aBYbvl1+X8YqldK86wZ1owVJ4u3mrKbRlXMma80J18qwiaTQ==",
      "requires": {
        "babel-plugin-syntax-dynamic-import": "6.18.0",
        "babel-template": "6.26.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-plugin-istanbul": {
      "version": "4.1.5",
      "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.5.tgz",
      "integrity": "sha1-Z2DN2Xf0EdPhdbsGTyvDJ9mbK24=",
      "requires": {
        "find-up": "2.1.0",
        "istanbul-lib-instrument": "1.9.1",
        "test-exclude": "4.1.1"
      }
    },
    "babel-plugin-jest-hoist": {
      "version": "20.0.3",
      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz",
      "integrity": "sha1-r+3IU70/jcNUjqZx++adA8wsF2c="
    },
    "babel-plugin-syntax-async-functions": {
      "version": "6.13.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
      "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU="
    },
    "babel-plugin-syntax-class-properties": {
      "version": "6.13.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
      "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94="
    },
    "babel-plugin-syntax-dynamic-import": {
      "version": "6.18.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
      "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo="
    },
    "babel-plugin-syntax-exponentiation-operator": {
      "version": "6.13.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz",
      "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4="
    },
    "babel-plugin-syntax-flow": {
      "version": "6.18.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
      "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0="
    },
    "babel-plugin-syntax-jsx": {
      "version": "6.18.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
      "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
    },
    "babel-plugin-syntax-object-rest-spread": {
      "version": "6.13.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
      "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U="
    },
    "babel-plugin-syntax-trailing-function-commas": {
      "version": "6.22.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
      "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM="
    },
    "babel-plugin-transform-async-to-generator": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz",
      "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=",
      "requires": {
        "babel-helper-remap-async-to-generator": "6.24.1",
        "babel-plugin-syntax-async-functions": "6.13.0",
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-class-properties": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz",
      "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=",
      "requires": {
        "babel-helper-function-name": "6.24.1",
        "babel-plugin-syntax-class-properties": "6.13.0",
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-arrow-functions": {
      "version": "6.22.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
      "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-es2015-block-scoped-functions": {
      "version": "6.22.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
      "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-es2015-block-scoping": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
      "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=",
      "requires": {
        "babel-runtime": "6.26.0",
        "babel-template": "6.26.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0",
        "lodash": "4.17.4"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        }
      }
    },
    "babel-plugin-transform-es2015-classes": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
      "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
      "requires": {
        "babel-helper-define-map": "6.26.0",
        "babel-helper-function-name": "6.24.1",
        "babel-helper-optimise-call-expression": "6.24.1",
        "babel-helper-replace-supers": "6.24.1",
        "babel-messages": "6.23.0",
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-computed-properties": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
      "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
      "requires": {
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-destructuring": {
      "version": "6.23.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
      "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-es2015-duplicate-keys": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
      "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=",
      "requires": {
        "babel-runtime": "6.23.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-for-of": {
      "version": "6.23.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
      "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-es2015-function-name": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
      "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
      "requires": {
        "babel-helper-function-name": "6.24.1",
        "babel-runtime": "6.23.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-literals": {
      "version": "6.22.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
      "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-es2015-modules-amd": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
      "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=",
      "requires": {
        "babel-plugin-transform-es2015-modules-commonjs": "6.26.0",
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-modules-commonjs": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz",
      "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=",
      "requires": {
        "babel-plugin-transform-strict-mode": "6.24.1",
        "babel-runtime": "6.26.0",
        "babel-template": "6.26.0",
        "babel-types": "6.26.0"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        }
      }
    },
    "babel-plugin-transform-es2015-modules-systemjs": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
      "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=",
      "requires": {
        "babel-helper-hoist-variables": "6.24.1",
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-modules-umd": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
      "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=",
      "requires": {
        "babel-plugin-transform-es2015-modules-amd": "6.24.1",
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-object-super": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
      "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=",
      "requires": {
        "babel-helper-replace-supers": "6.24.1",
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-es2015-parameters": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
      "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
      "requires": {
        "babel-helper-call-delegate": "6.24.1",
        "babel-helper-get-function-arity": "6.24.1",
        "babel-runtime": "6.23.0",
        "babel-template": "6.26.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-shorthand-properties": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
      "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
      "requires": {
        "babel-runtime": "6.23.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-spread": {
      "version": "6.22.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
      "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-es2015-sticky-regex": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
      "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=",
      "requires": {
        "babel-helper-regex": "6.26.0",
        "babel-runtime": "6.23.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-plugin-transform-es2015-template-literals": {
      "version": "6.22.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
      "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-es2015-typeof-symbol": {
      "version": "6.23.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
      "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-es2015-unicode-regex": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
      "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=",
      "requires": {
        "babel-helper-regex": "6.26.0",
        "babel-runtime": "6.23.0",
        "regexpu-core": "2.0.0"
      }
    },
    "babel-plugin-transform-exponentiation-operator": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz",
      "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=",
      "requires": {
        "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1",
        "babel-plugin-syntax-exponentiation-operator": "6.13.0",
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-flow-strip-types": {
      "version": "6.22.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz",
      "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=",
      "requires": {
        "babel-plugin-syntax-flow": "6.18.0",
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-object-rest-spread": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz",
      "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=",
      "requires": {
        "babel-plugin-syntax-object-rest-spread": "6.13.0",
        "babel-runtime": "6.26.0"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        }
      }
    },
    "babel-plugin-transform-react-constant-elements": {
      "version": "6.23.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-constant-elements/-/babel-plugin-transform-react-constant-elements-6.23.0.tgz",
      "integrity": "sha1-LxGb9NLN1F65uqrldAU8YE9hR90=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-react-display-name": {
      "version": "6.25.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz",
      "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-react-jsx": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz",
      "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=",
      "requires": {
        "babel-helper-builder-react-jsx": "6.26.0",
        "babel-plugin-syntax-jsx": "6.18.0",
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-react-jsx-self": {
      "version": "6.22.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz",
      "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=",
      "requires": {
        "babel-plugin-syntax-jsx": "6.18.0",
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-react-jsx-source": {
      "version": "6.22.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz",
      "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=",
      "requires": {
        "babel-plugin-syntax-jsx": "6.18.0",
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-regenerator": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
      "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=",
      "requires": {
        "regenerator-transform": "0.10.1"
      }
    },
    "babel-plugin-transform-runtime": {
      "version": "6.23.0",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz",
      "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=",
      "requires": {
        "babel-runtime": "6.23.0"
      }
    },
    "babel-plugin-transform-strict-mode": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
      "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
      "requires": {
        "babel-runtime": "6.23.0",
        "babel-types": "6.26.0"
      }
    },
    "babel-preset-env": {
      "version": "1.6.1",
      "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz",
      "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==",
      "requires": {
        "babel-plugin-check-es2015-constants": "6.22.0",
        "babel-plugin-syntax-trailing-function-commas": "6.22.0",
        "babel-plugin-transform-async-to-generator": "6.24.1",
        "babel-plugin-transform-es2015-arrow-functions": "6.22.0",
        "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0",
        "babel-plugin-transform-es2015-block-scoping": "6.26.0",
        "babel-plugin-transform-es2015-classes": "6.24.1",
        "babel-plugin-transform-es2015-computed-properties": "6.24.1",
        "babel-plugin-transform-es2015-destructuring": "6.23.0",
        "babel-plugin-transform-es2015-duplicate-keys": "6.24.1",
        "babel-plugin-transform-es2015-for-of": "6.23.0",
        "babel-plugin-transform-es2015-function-name": "6.24.1",
        "babel-plugin-transform-es2015-literals": "6.22.0",
        "babel-plugin-transform-es2015-modules-amd": "6.24.1",
        "babel-plugin-transform-es2015-modules-commonjs": "6.26.0",
        "babel-plugin-transform-es2015-modules-systemjs": "6.24.1",
        "babel-plugin-transform-es2015-modules-umd": "6.24.1",
        "babel-plugin-transform-es2015-object-super": "6.24.1",
        "babel-plugin-transform-es2015-parameters": "6.24.1",
        "babel-plugin-transform-es2015-shorthand-properties": "6.24.1",
        "babel-plugin-transform-es2015-spread": "6.22.0",
        "babel-plugin-transform-es2015-sticky-regex": "6.24.1",
        "babel-plugin-transform-es2015-template-literals": "6.22.0",
        "babel-plugin-transform-es2015-typeof-symbol": "6.23.0",
        "babel-plugin-transform-es2015-unicode-regex": "6.24.1",
        "babel-plugin-transform-exponentiation-operator": "6.24.1",
        "babel-plugin-transform-regenerator": "6.26.0",
        "browserslist": "2.8.0",
        "invariant": "2.2.2",
        "semver": "5.4.1"
      }
    },
    "babel-preset-flow": {
      "version": "6.23.0",
      "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz",
      "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=",
      "requires": {
        "babel-plugin-transform-flow-strip-types": "6.22.0"
      }
    },
    "babel-preset-jest": {
      "version": "20.0.3",
      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz",
      "integrity": "sha1-y6yq3stdaJyh4d4TYOv8ZoYsF4o=",
      "requires": {
        "babel-plugin-jest-hoist": "20.0.3"
      }
    },
    "babel-preset-react": {
      "version": "6.24.1",
      "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz",
      "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=",
      "requires": {
        "babel-plugin-syntax-jsx": "6.18.0",
        "babel-plugin-transform-react-display-name": "6.25.0",
        "babel-plugin-transform-react-jsx": "6.24.1",
        "babel-plugin-transform-react-jsx-self": "6.22.0",
        "babel-plugin-transform-react-jsx-source": "6.22.0",
        "babel-preset-flow": "6.23.0"
      }
    },
    "babel-preset-react-app": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-3.1.0.tgz",
      "integrity": "sha512-jEAeVozxLzftLl0iDZ0d5jrmfbo3yogON/eI4AsEDIs8p6WW+t9mDRUsj5l12bqPOLSiVOElCQ3QyGjMcyBiwA==",
      "requires": {
        "babel-plugin-dynamic-import-node": "1.1.0",
        "babel-plugin-syntax-dynamic-import": "6.18.0",
        "babel-plugin-transform-class-properties": "6.24.1",
        "babel-plugin-transform-object-rest-spread": "6.26.0",
        "babel-plugin-transform-react-constant-elements": "6.23.0",
        "babel-plugin-transform-react-jsx": "6.24.1",
        "babel-plugin-transform-react-jsx-self": "6.22.0",
        "babel-plugin-transform-react-jsx-source": "6.22.0",
        "babel-plugin-transform-regenerator": "6.26.0",
        "babel-plugin-transform-runtime": "6.23.0",
        "babel-preset-env": "1.6.1",
        "babel-preset-react": "6.24.1"
      }
    },
    "babel-register": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
      "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=",
      "requires": {
        "babel-core": "6.26.0",
        "babel-runtime": "6.26.0",
        "core-js": "2.5.1",
        "home-or-tmp": "2.0.0",
        "lodash": "4.17.4",
        "mkdirp": "0.5.1",
        "source-map-support": "0.4.18"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        }
      }
    },
    "babel-runtime": {
      "version": "6.23.0",
      "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz",
      "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=",
      "requires": {
        "core-js": "2.5.0",
        "regenerator-runtime": "0.10.5"
      },
      "dependencies": {
        "core-js": {
          "version": "2.5.0",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.0.tgz",
          "integrity": "sha1-VpwFCRi+ZIazg3VSAorgRmtxcIY="
        }
      }
    },
    "babel-template": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
      "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
      "requires": {
        "babel-runtime": "6.26.0",
        "babel-traverse": "6.26.0",
        "babel-types": "6.26.0",
        "babylon": "6.18.0",
        "lodash": "4.17.4"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        }
      }
    },
    "babel-traverse": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
      "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
      "requires": {
        "babel-code-frame": "6.26.0",
        "babel-messages": "6.23.0",
        "babel-runtime": "6.26.0",
        "babel-types": "6.26.0",
        "babylon": "6.18.0",
        "debug": "2.6.9",
        "globals": "9.18.0",
        "invariant": "2.2.2",
        "lodash": "4.17.4"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        }
      }
    },
    "babel-types": {
      "version": "6.26.0",
      "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
      "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
      "requires": {
        "babel-runtime": "6.26.0",
        "esutils": "2.0.2",
        "lodash": "4.17.4",
        "to-fast-properties": "1.0.3"
      },
      "dependencies": {
        "babel-runtime": {
          "version": "6.26.0",
          "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
          "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
          "requires": {
            "core-js": "2.5.1",
            "regenerator-runtime": "0.11.0"
          }
        },
        "core-js": {
          "version": "2.5.1",
          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
          "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
        },
        "regenerator-runtime": {
          "version": "0.11.0",
          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
          "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
        }
      }
    },
    "babylon": {
      "version": "6.18.0",
      "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
      "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
    },
    "balanced-match": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
    },
    "base64-js": {
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
      "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw=="
    },
    "batch": {
      "version": "0.6.1",
      "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
      "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY="
    },
    "bcrypt-pbkdf": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
      "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
      "optional": true,
      "requires": {
        "tweetnacl": "0.14.5"
      }
    },
    "big.js": {
      "version": "3.2.0",
      "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
      "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q=="
    },
    "binary-extensions": {
      "version": "1.10.0",
      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz",
      "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA="
    },
    "bluebird": {
      "version": "3.5.1",
      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
      "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
    },
    "bn.js": {
      "version": "4.11.8",
      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
      "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
    },
    "body-parser": {
      "version": "1.18.2",
      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
      "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
      "requires": {
        "bytes": "3.0.0",
        "content-type": "1.0.4",
        "debug": "2.6.9",
        "depd": "1.1.1",
        "http-errors": "1.6.2",
        "iconv-lite": "0.4.19",
        "on-finished": "2.3.0",
        "qs": "6.5.1",
        "raw-body": "2.3.2",
        "type-is": "1.6.15"
      },
      "dependencies": {
        "iconv-lite": {
          "version": "0.4.19",
          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
          "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
        }
      }
    },
    "bonjour": {
      "version": "3.5.0",
      "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
      "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
      "requires": {
        "array-flatten": "2.1.1",
        "deep-equal": "1.0.1",
        "dns-equal": "1.0.0",
        "dns-txt": "2.0.2",
        "multicast-dns": "6.1.1",
        "multicast-dns-service-types": "1.1.0"
      }
    },
    "boolbase": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
      "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
    },
    "boom": {
      "version": "4.3.1",
      "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
      "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
      "requires": {
        "hoek": "4.2.0"
      }
    },
    "boxen": {
      "version": "0.6.0",
      "resolved": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz",
      "integrity": "sha1-g2TUJIrDT/DvGy8r9JpsYM4NgbY=",
      "requires": {
        "ansi-align": "1.1.0",
        "camelcase": "2.1.1",
        "chalk": "1.1.3",
        "cli-boxes": "1.0.0",
        "filled-array": "1.1.0",
        "object-assign": "4.1.1",
        "repeating": "2.0.1",
        "string-width": "1.0.2",
        "widest-line": "1.0.0"
      },
      "dependencies": {
        "camelcase": {
          "version": "2.1.1",
          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
        },
        "is-fullwidth-code-point": {
          "version": "1.0.0",
          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
          "requires": {
            "number-is-nan": "1.0.1"
          }
        },
        "string-width": {
          "version": "1.0.2",
          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
          "requires": {
            "code-point-at": "1.1.0",
            "is-fullwidth-code-point": "1.0.0",
            "strip-ansi": "3.0.1"
          }
        }
      }
    },
    "brace-expansion": {
      "version": "1.1.8",
      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
      "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
      "requires": {
        "balanced-match": "1.0.0",
        "concat-map": "0.0.1"
      }
    },
    "braces": {
      "version": "1.8.5",
      "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
      "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
      "requires": {
        "expand-range": "1.8.2",
        "preserve": "0.2.0",
        "repeat-element": "1.1.2"
      }
    },
    "brorand": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
    },
    "browser-resolve": {
      "version": "1.11.2",
      "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz",
      "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=",
      "requires": {
        "resolve": "1.1.7"
      },
      "dependencies": {
        "resolve": {
          "version": "1.1.7",
          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs="
        }
      }
    },
    "browserify-aes": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz",
      "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==",
      "requires": {
        "buffer-xor": "1.0.3",
        "cipher-base": "1.0.4",
        "create-hash": "1.1.3",
        "evp_bytestokey": "1.0.3",
        "inherits": "2.0.3",
        "safe-buffer": "5.1.1"
      }
    },
    "browserify-cipher": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz",
      "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=",
      "requires": {
        "browserify-aes": "1.1.1",
        "browserify-des": "1.0.0",
        "evp_bytestokey": "1.0.3"
      }
    },
    "browserify-des": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz",
      "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=",
      "requires": {
        "cipher-base": "1.0.4",
        "des.js": "1.0.0",
        "inherits": "2.0.3"
      }
    },
    "browserify-rsa": {
      "version": "4.0.1",
      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
      "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
      "requires": {
        "bn.js": "4.11.8",
        "randombytes": "2.0.5"
      }
    },
    "browserify-sign": {
      "version": "4.0.4",
      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
      "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
      "requires": {
        "bn.js": "4.11.8",
        "browserify-rsa": "4.0.1",
        "create-hash": "1.1.3",
        "create-hmac": "1.1.6",
        "elliptic": "6.4.0",
        "inherits": "2.0.3",
        "parse-asn1": "5.1.0"
      }
    },
    "browserify-zlib": {
      "version": "0.1.4",
      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
      "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
      "requires": {
        "pako": "0.2.9"
      }
    },
    "browserslist": {
      "version": "2.8.0",
      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.8.0.tgz",
      "integrity": "sha512-iiWHM1Et6Q4TQpB7Ar6pxuM3TNMXasVJY4Y/oh3q38EwR3Z+IdZ9MyVf7PI4MJFB4xpwMcZgs9bEUnPG2E3TCA==",
      "requires": {
        "caniuse-lite": "1.0.30000760",
        "electron-to-chromium": "1.3.27"
      }
    },
    "bser": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz",
      "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=",
      "requires": {
        "node-int64": "0.4.0"
      }
    },
    "buffer": {
      "version": "4.9.1",
      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
      "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
      "requires": {
        "base64-js": "1.2.1",
        "ieee754": "1.1.8",
        "isarray": "1.0.0"
      }
    },
    "buffer-indexof": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
      "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g=="
    },
    "buffer-xor": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
    },
    "builtin-modules": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
    },
    "builtin-status-codes": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
      "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug="
    },
    "bytes": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
    },
    "caller-path": {
      "version": "0.1.0",
      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
      "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
      "requires": {
        "callsites": "0.2.0"
      }
    },
    "callsites": {
      "version": "0.2.0",
      "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
      "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo="
    },
    "camel-case": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
      "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
      "requires": {
        "no-case": "2.3.2",
        "upper-case": "1.1.3"
      }
    },
    "camelcase": {
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
      "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
    },
    "camelcase-keys": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
      "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
      "requires": {
        "camelcase": "2.1.1",
        "map-obj": "1.0.1"
      },
      "dependencies": {
        "camelcase": {
          "version": "2.1.1",
          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
        }
      }
    },
    "caniuse-api": {
      "version": "1.6.1",
      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz",
      "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=",
      "requires": {
        "browserslist": "1.7.7",
        "caniuse-db": "1.0.30000760",
        "lodash.memoize": "4.1.2",
        "lodash.uniq": "4.5.0"
      },
      "dependencies": {
        "browserslist": {
          "version": "1.7.7",
          "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
          "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
          "requires": {
            "caniuse-db": "1.0.30000760",
            "electron-to-chromium": "1.3.27"
          }
        }
      }
    },
    "caniuse-db": {
      "version": "1.0.30000760",
      "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000760.tgz",
      "integrity": "sha1-PqKUc+t4psywny63Osnh3r/sUo0="
    },
    "caniuse-lite": {
      "version": "1.0.30000760",
      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000760.tgz",
      "integrity": "sha1-7HIDlXQvHH7IlH/W3SYE53qPmP8="
    },
    "capture-stack-trace": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
      "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
    },
    "case-sensitive-paths-webpack-plugin": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.1.1.tgz",
      "integrity": "sha1-PSnO2MHxJL9vU4Rvs/WJRzH9yQk="
    },
    "caseless": {
      "version": "0.12.0",
      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
    },
    "center-align": {
      "version": "0.1.3",
      "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
      "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
      "requires": {
        "align-text": "0.1.4",
        "lazy-cache": "1.0.4"
      }
    },
    "chalk": {
      "version": "1.1.3",
      "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
      "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
      "requires": {
        "ansi-styles": "2.2.1",
        "escape-string-regexp": "1.0.5",
        "has-ansi": "2.0.0",
        "strip-ansi": "3.0.1",
        "supports-color": "2.0.0"
      },
      "dependencies": {
        "ansi-styles": {
          "version": "2.2.1",
          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
        },
        "supports-color": {
          "version": "2.0.0",
          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
        }
      }
    },
    "chokidar": {
      "version": "1.7.0",
      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
      "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
      "requires": {
        "anymatch": "1.3.2",
        "async-each": "1.0.1",
        "glob-parent": "2.0.0",
        "inherits": "2.0.3",
        "is-binary-path": "1.0.1",
        "is-glob": "2.0.1",
        "path-is-absolute": "1.0.1",
        "readdirp": "2.1.0"
      }
    },
    "ci-info": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.1.tgz",
      "integrity": "sha512-vHDDF/bP9RYpTWtUhpJRhCFdvvp3iDWvEbuDbWgvjUrNGV1MXJrE0MPcwGtEled04m61iwdBLUIHZtDgzWS4ZQ=="
    },
    "cipher-base": {
      "version": "1.0.4",
      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
      "requires": {
        "inherits": "2.0.3",
        "safe-buffer": "5.1.1"
      }
    },
    "circular-json": {
      "version": "0.3.3",
      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
      "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A=="
    },
    "clap": {
      "version": "1.2.3",
      "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz",
      "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==",
      "requires": {
        "chalk": "1.1.3"
      }
    },
    "classnames": {
      "version": "2.2.5",
      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz",
      "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0="
    },
    "clean-css": {
      "version": "4.1.9",
      "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.9.tgz",
      "integrity": "sha1-Nc7ornaHpJuYA09w3gDE7dOCYwE=",
      "requires": {
        "source-map": "0.5.7"
      },
      "dependencies": {
        "source-map": {
          "version": "0.5.7",
          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
        }
      }
    },
    "cli-boxes": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
      "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
    },
    "cli-cursor": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
      "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
      "requires": {
        "restore-cursor": "2.0.0"
      }
    },
    "cli-width": {
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
      "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk="
    },
    "cliui": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
      "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
      "requires": {
        "center-align": "0.1.3",
        "right-align": "0.1.3",
        "wordwrap": "0.0.2"
      },
      "dependencies": {
        "wordwrap": {
          "version": "0.0.2",
          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
          "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
        }
      }
    },
    "clone": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz",
      "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8="
    },
    "co": {
      "version": "4.6.0",
      "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
      "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
    },
    "coa": {
      "version": "1.0.4",
      "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz",
      "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=",
      "requires": {
        "q": "1.5.1"
      }
    },
    "code-point-at": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
    },
    "color": {
      "version": "0.11.4",
      "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz",
      "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=",
      "requires": {
        "clone": "1.0.3",
        "color-convert": "1.9.1",
        "color-string": "0.3.0"
      }
    },
    "color-convert": {
      "version": "1.9.1",
      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
      "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
      "requires": {
        "color-name": "1.1.3"
      }
    },
    "color-name": {
      "version": "1.1.3",
      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
    },
    "color-string": {
      "version": "0.3.0",
      "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz",
      "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=",
      "requires": {
        "color-name": "1.1.3"
      }
    },
    "colormin": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz",
      "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=",
      "requires": {
        "color": "0.11.4",
        "css-color-names": "0.0.4",
        "has": "1.0.1"
      }
    },
    "colors": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
      "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM="
    },
    "combined-stream": {
      "version": "1.0.5",
      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
      "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
      "requires": {
        "delayed-stream": "1.0.0"
      }
    },
    "commander": {
      "version": "2.11.0",
      "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
      "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ=="
    },
    "commondir": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
      "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
    },
    "compressible": {
      "version": "2.0.12",
      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.12.tgz",
      "integrity": "sha1-xZpcmdt2dn6YdlAOJx72OzSTvWY=",
      "requires": {
        "mime-db": "1.30.0"
      }
    },
    "compression": {
      "version": "1.7.1",
      "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.1.tgz",
      "integrity": "sha1-7/JgPvwuIs+G810uuTWJ+YdTc9s=",
      "requires": {
        "accepts": "1.3.4",
        "bytes": "3.0.0",
        "compressible": "2.0.12",
        "debug": "2.6.9",
        "on-headers": "1.0.1",
        "safe-buffer": "5.1.1",
        "vary": "1.1.2"
      }
    },
    "concat-map": {
      "version": "0.0.1",
      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
    },
    "concat-stream": {
      "version": "1.6.0",
      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
      "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
      "requires": {
        "inherits": "2.0.3",
        "readable-stream": "2.3.3",
        "typedarray": "0.0.6"
      }
    },
    "configstore": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz",
      "integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=",
      "requires": {
        "dot-prop": "3.0.0",
        "graceful-fs": "4.1.11",
        "mkdirp": "0.5.1",
        "object-assign": "4.1.1",
        "os-tmpdir": "1.0.2",
        "osenv": "0.1.4",
        "uuid": "2.0.3",
        "write-file-atomic": "1.3.4",
        "xdg-basedir": "2.0.0"
      },
      "dependencies": {
        "uuid": {
          "version": "2.0.3",
          "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
          "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho="
        }
      }
    },
    "connect-history-api-fallback": {
      "version": "1.4.0",
      "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.4.0.tgz",
      "integrity": "sha1-PbJPlz9LkjsOgvYZzg3wJBHKYj0="
    },
    "console-browserify": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
      "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
      "requires": {
        "date-now": "0.1.4"
      }
    },
    "constants-browserify": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
      "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U="
    },
    "contains-path": {
      "version": "0.1.0",
      "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
      "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo="
    },
    "content-disposition": {
      "version": "0.5.2",
      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
      "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
    },
    "content-type": {
      "version": "1.0.4",
      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
    },
    "content-type-parser": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz",
      "integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ=="
    },
    "convert-source-map": {
      "version": "1.5.0",
      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz",
      "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU="
    },
    "cookie": {
      "version": "0.3.1",
      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
      "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
    },
    "cookie-signature": {
      "version": "1.0.6",
      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
    },
    "core-js": {
      "version": "1.2.7",
      "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
      "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
    },
    "core-util-is": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
    },
    "cosmiconfig": {
      "version": "2.2.2",
      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz",
      "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==",
      "requires": {
        "is-directory": "0.3.1",
        "js-yaml": "3.7.0",
        "minimist": "1.2.0",
        "object-assign": "4.1.1",
        "os-homedir": "1.0.2",
        "parse-json": "2.2.0",
        "require-from-string": "1.2.1"
      },
      "dependencies": {
        "minimist": {
          "version": "1.2.0",
          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
        }
      }
    },
    "create-ecdh": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz",
      "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=",
      "requires": {
        "bn.js": "4.11.8",
        "elliptic": "6.4.0"
      }
    },
    "create-error-class": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
      "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
      "requires": {
        "capture-stack-trace": "1.0.0"
      }
    },
    "create-hash": {
      "version": "1.1.3",
      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
      "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=",
      "requires": {
        "cipher-base": "1.0.4",
        "inherits": "2.0.3",
        "ripemd160": "2.0.1",
        "sha.js": "2.4.9"
      }
    },
    "create-hmac": {
      "version": "1.1.6",
      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz",
      "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=",
      "requires": {
        "cipher-base": "1.0.4",
        "create-hash": "1.1.3",
        "inherits": "2.0.3",
        "ripemd160": "2.0.1",
        "safe-buffer": "5.1.1",
        "sha.js": "2.4.9"
      }
    },
    "cross-spawn": {
      "version": "5.1.0",
      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
      "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
      "requires": {
        "lru-cache": "4.1.1",
        "shebang-command": "1.2.0",
        "which": "1.3.0"
      }
    },
    "cryptiles": {
      "version": "3.1.2",
      "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
      "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
      "requires": {
        "boom": "5.2.0"
      },
      "dependencies": {
        "boom": {
          "version": "5.2.0",
          "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
          "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
          "requires": {
            "hoek": "4.2.0"
          }
        }
      }
    },
    "crypto-browserify": {
      "version": "3.12.0",
      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
      "requires": {
        "browserify-cipher": "1.0.0",
        "browserify-sign": "4.0.4",
        "create-ecdh": "4.0.0",
        "create-hash": "1.1.3",
        "create-hmac": "1.1.6",
        "diffie-hellman": "5.0.2",
        "inherits": "2.0.3",
        "pbkdf2": "3.0.14",
        "public-encrypt": "4.0.0",
        "randombytes": "2.0.5",
        "randomfill": "1.0.3"
      }
    },
    "css-color-names": {
      "version": "0.0.4",
      "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
      "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA="
    },
    "css-loader": {
      "version": "0.28.7",
      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.7.tgz",
      "integrity": "sha512-GxMpax8a/VgcfRrVy0gXD6yLd5ePYbXX/5zGgTVYp4wXtJklS8Z2VaUArJgc//f6/Dzil7BaJObdSv8eKKCPgg==",
      "requires": {
        "babel-code-frame": "6.26.0",
        "css-selector-tokenizer": "0.7.0",
        "cssnano": "3.10.0",
        "icss-utils": "2.1.0",
        "loader-utils": "1.1.0",
        "lodash.camelcase": "4.3.0",
        "object-assign": "4.1.1",
        "postcss": "5.2.18",
        "postcss-modules-extract-imports": "1.1.0",
        "postcss-modules-local-by-default": "1.2.0",
        "postcss-modules-scope": "1.1.0",
        "postcss-modules-values": "1.3.0",
        "postcss-value-parser": "3.3.0",
        "source-list-map": "2.0.0"
      },
      "dependencies": {
        "has-flag": {
          "version": "1.0.0",
          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo="
        },
        "postcss": {
          "version": "5.2.18",
          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
          "requires": {
            "chalk": "1.1.3",
            "js-base64": "2.3.2",
            "source-map": "0.5.7",
            "supports-color": "3.2.3"
          }
        },
        "source-map": {
          "version": "0.5.7",
          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
        },
        "supports-color": {
          "version": "3.2.3",
          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
          "requires": {
            "has-flag": "1.0.0"
          }
        }
      }
    },
    "css-select": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
      "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
      "requires": {
        "boolbase": "1.0.0",
        "css-what": "2.1.0",
        "domutils": "1.5.1",
        "nth-check": "1.0.1"
      }
    },
    "css-selector-tokenizer": {
      "version": "0.7.0",
      "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz",
      "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=",
      "requires": {
        "cssesc": "0.1.0",
        "fastparse": "1.1.1",
        "regexpu-core": "1.0.0"
      },
      "dependencies": {
        "regexpu-core": {
          "version": "1.0.0",
          "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
          "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
          "requires": {
            "regenerate": "1.3.3",
            "regjsgen": "0.2.0",
            "regjsparser": "0.1.5"
          }
        }
      }
    },
    "css-what": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz",
      "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0="
    },
    "cssesc": {
      "version": "0.1.0",
      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz",
      "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q="
    },
    "cssnano": {
      "version": "3.10.0",
      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz",
      "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=",
      "requires": {
        "autoprefixer": "6.7.7",
        "decamelize": "1.2.0",
        "defined": "1.0.0",
        "has": "1.0.1",
        "object-assign": "4.1.1",
        "postcss": "5.2.18",
        "postcss-calc": "5.3.1",
        "postcss-colormin": "2.2.2",
        "postcss-convert-values": "2.6.1",
        "postcss-discard-comments": "2.0.4",
        "postcss-discard-duplicates": "2.1.0",
        "postcss-discard-empty": "2.1.0",
        "postcss-discard-overridden": "0.1.1",
        "postcss-discard-unused": "2.2.3",
        "postcss-filter-plugins": "2.0.2",
        "postcss-merge-idents": "2.1.7",
        "postcss-merge-longhand": "2.0.2",
        "postcss-merge-rules": "2.1.2",
        "postcss-minify-font-values": "1.0.5",
        "postcss-minify-gradients": "1.0.5",
        "postcss-minify-params": "1.2.2",
        "postcss-minify-selectors": "2.1.1",
        "postcss-normalize-charset": "1.1.1",
        "postcss-normalize-url": "3.0.8",
        "postcss-ordered-values": "2.2.3",
        "postcss-reduce-idents": "2.4.0",
        "postcss-reduce-initial": "1.0.1",
        "postcss-reduce-transforms": "1.0.4",
        "postcss-svgo": "2.1.6",
        "postcss-unique-selectors": "2.0.2",
        "postcss-value-parser": "3.3.0",
        "postcss-zindex": "2.2.0"
      },
      "dependencies": {
        "autoprefixer": {
          "version": "6.7.7",
          "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz",
          "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=",
          "requires": {
            "browserslist": "1.7.7",
            "caniuse-db": "1.0.30000760",
            "normalize-range": "0.1.2",
            "num2fraction": "1.2.2",
            "postcss": "5.2.18",
            "postcss-value-parser": "3.3.0"
          }
        },
        "browserslist": {
          "version": "1.7.7",
          "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
          "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
          "requires": {
            "caniuse-db": "1.0.30000760",
            "electron-to-chromium": "1.3.27"
          }
        },
        "has-flag": {
          "version": "1.0.0",
          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo="
        },
        "postcss": {
          "version": "5.2.18",
          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
          "requires": {
            "chalk": "1.1.3",
            "js-base64": "2.3.2",
            "source-map": "0.5.7",
            "supports-color": "3.2.3"
          }
        },
        "source-map": {
          "version": "0.5.7",
          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
        },
        "supports-color": {
          "version": "3.2.3",
          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
          "requires": {
            "has-flag": "1.0.0"
          }
        }
      }
    },
    "csso": {
      "version": "2.3.2",
      "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz",
      "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=",
      "requires": {
        "clap": "1.2.3",
        "source-map": "0.5.7"
      },
      "dependencies": {
        "source-map": {
          "version": "0.5.7",
          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
        }
      }
    },
    "cssom": {
      "version": "0.3.2",
      "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz",
      "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs="
    },
    "cssstyle": {
      "version": "0.2.37",
      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz",
      "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=",
      "requires": {
        "cssom": "0.3.2"
      }
    },
    "currently-unhandled": {
      "version": "0.4.1",
      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
      "requires": {
        "array-find-index": "1.0.2"
      }
    },
    "d": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
      "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
      "requires": {
        "es5-ext": "0.10.35"
      }
    },
    "damerau-levenshtein": {
      "version": "1.0.4",
      "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz",
      "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ="
    },
    "dashdash": {
      "version": "1.14.1",
      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
      "requires": {
        "assert-plus": "1.0.0"
      }
    },
    "date-now": {
      "version": "0.1.4",
      "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
      "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs="
    },
    "debug": {
      "version": "2.6.9",
      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
      "requires": {
        "ms": "2.0.0"
      }
    },
    "decamelize": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
    },
    "deep-equal": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
      "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
    },
    "deep-extend": {
      "version": "0.4.2",
      "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
      "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8="
    },
    "deep-is": {
      "version": "0.1.3",
      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
    },
    "default-require-extensions": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz",
      "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=",
      "requires": {
        "strip-bom": "2.0.0"
      }
    },
    "define-properties": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
      "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
      "requires": {
        "foreach": "2.0.5",
        "object-keys": "1.0.11"
      }
    },
    "defined": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
      "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM="
    },
    "del": {
      "version": "2.2.2",
      "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
      "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
      "requires": {
        "globby": "5.0.0",
        "is-path-cwd": "1.0.0",
        "is-path-in-cwd": "1.0.0",
        "object-assign": "4.1.1",
        "pify": "2.3.0",
        "pinkie-promise": "2.0.1",
        "rimraf": "2.6.2"
      }
    },
    "delayed-stream": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
    },
    "depd": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
      "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
    },
    "des.js": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
      "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
      "requires": {
        "inherits": "2.0.3",
        "minimalistic-assert": "1.0.0"
      }
    },
    "destroy": {
      "version": "1.0.4",
      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
    },
    "detect-indent": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
      "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
      "requires": {
        "repeating": "2.0.1"
      }
    },
    "detect-node": {
      "version": "2.0.3",
      "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz",
      "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc="
    },
    "detect-port-alt": {
      "version": "1.1.3",
      "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.3.tgz",
      "integrity": "sha1-pNLwYddXoDTs83xRQmCph1DysTE=",
      "requires": {
        "address": "1.0.3",
        "debug": "2.6.9"
      }
    },
    "diff": {
      "version": "3.4.0",
      "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz",
      "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA=="
    },
    "diffie-hellman": {
      "version": "5.0.2",
      "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz",
      "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=",
      "requires": {
        "bn.js": "4.11.8",
        "miller-rabin": "4.0.1",
        "randombytes": "2.0.5"
      }
    },
    "dns-equal": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
      "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0="
    },
    "dns-packet": {
      "version": "1.2.2",
      "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.2.2.tgz",
      "integrity": "sha512-kN+DjfGF7dJGUL7nWRktL9Z18t1rWP3aQlyZdY8XlpvU3Nc6GeFTQApftcjtWKxAZfiggZSGrCEoszNgvnpwDg==",
      "requires": {
        "ip": "1.1.5",
        "safe-buffer": "5.1.1"
      }
    },
    "dns-txt": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
      "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
      "requires": {
        "buffer-indexof": "1.1.1"
      }
    },
    "doctrine": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
      "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=",
      "requires": {
        "esutils": "2.0.2",
        "isarray": "1.0.0"
      }
    },
    "dom-converter": {
      "version": "0.1.4",
      "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.1.4.tgz",
      "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=",
      "requires": {
        "utila": "0.3.3"
      },
      "dependencies": {
        "utila": {
          "version": "0.3.3",
          "resolved": "https://registry.npmjs.org/utila/-/utila-0.3.3.tgz",
          "integrity": "sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY="
        }
      }
    },
    "dom-serializer": {
      "version": "0.1.0",
      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
      "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=",
      "requires": {
        "domelementtype": "1.1.3",
        "entities": "1.1.1"
      },
      "dependencies": {
        "domelementtype": {
          "version": "1.1.3",
          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz",
          "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs="
        }
      }
    },
    "dom-urls": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/dom-urls/-/dom-urls-1.1.0.tgz",
      "integrity": "sha1-AB3fgWKM0ecGElxxdvU8zsVdkY4=",
      "requires": {
        "urijs": "1.19.0"
      }
    },
    "domain-browser": {
      "version": "1.1.7",
      "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz",
      "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw="
    },
    "domelementtype": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz",
      "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI="
    },
    "domhandler": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz",
      "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=",
      "requires": {
        "domelementtype": "1.3.0"
      }
    },
    "domutils": {
      "version": "1.5.1",
      "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
      "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
      "requires": {
        "dom-serializer": "0.1.0",
        "domelementtype": "1.3.0"
      }
    },
    "dot-prop": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz",
      "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=",
      "requires": {
        "is-obj": "1.0.1"
      }
    },
    "dotenv": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz",
      "integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0="
    },
    "duplexer": {
      "version": "0.1.1",
      "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
      "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E="
    },
    "duplexer2": {
      "version": "0.1.4",
      "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
      "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
      "requires": {
        "readable-stream": "2.3.3"
      }
    },
    "ecc-jsbn": {
      "version": "0.1.1",
      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
      "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
      "optional": true,
      "requires": {
        "jsbn": "0.1.1"
      }
    },
    "ee-first": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
    },
    "electron-to-chromium": {
      "version": "1.3.27",
      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz",
      "integrity": "sha1-eOy4o5kGYYe7N07t412ccFZagD0="
    },
    "elliptic": {
      "version": "6.4.0",
      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
      "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
      "requires": {
        "bn.js": "4.11.8",
        "brorand": "1.1.0",
        "hash.js": "1.1.3",
        "hmac-drbg": "1.0.1",
        "inherits": "2.0.3",
        "minimalistic-assert": "1.0.0",
        "minimalistic-crypto-utils": "1.0.1"
      }
    },
    "emoji-regex": {
      "version": "6.5.1",
      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz",
      "integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ=="
    },
    "emojis-list": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
      "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k="
    },
    "encodeurl": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
      "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA="
    },
    "encoding": {
      "version": "0.1.12",
      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
      "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
      "requires": {
        "iconv-lite": "0.4.18"
      }
    },
    "enhanced-resolve": {
      "version": "3.4.1",
      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz",
      "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=",
      "requires": {
        "graceful-fs": "4.1.11",
        "memory-fs": "0.4.1",
        "object-assign": "4.1.1",
        "tapable": "0.2.8"
      }
    },
    "entities": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
      "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
    },
    "errno": {
      "version": "0.1.4",
      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz",
      "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=",
      "requires": {
        "prr": "0.0.0"
      }
    },
    "error-ex": {
      "version": "1.3.1",
      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
      "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
      "requires": {
        "is-arrayish": "0.2.1"
      }
    },
    "es-abstract": {
      "version": "1.9.0",
      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.9.0.tgz",
      "integrity": "sha512-kk3IJoKo7A3pWJc0OV8yZ/VEX2oSUytfekrJiqoxBlKJMFAJVJVpGdHClCCTdv+Fn2zHfpDHHIelMFhZVfef3Q==",
      "requires": {
        "es-to-primitive": "1.1.1",
        "function-bind": "1.1.1",
        "has": "1.0.1",
        "is-callable": "1.1.3",
        "is-regex": "1.0.4"
      }
    },
    "es-to-primitive": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
      "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
      "requires": {
        "is-callable": "1.1.3",
        "is-date-object": "1.0.1",
        "is-symbol": "1.0.1"
      }
    },
    "es5-ext": {
      "version": "0.10.35",
      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.35.tgz",
      "integrity": "sha1-GO6FjOajxFx9eekcFfzKnsVoSU8=",
      "requires": {
        "es6-iterator": "2.0.3",
        "es6-symbol": "3.1.1"
      }
    },
    "es6-iterator": {
      "version": "2.0.3",
      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
      "requires": {
        "d": "1.0.0",
        "es5-ext": "0.10.35",
        "es6-symbol": "3.1.1"
      }
    },
    "es6-map": {
      "version": "0.1.5",
      "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
      "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
      "requires": {
        "d": "1.0.0",
        "es5-ext": "0.10.35",
        "es6-iterator": "2.0.3",
        "es6-set": "0.1.5",
        "es6-symbol": "3.1.1",
        "event-emitter": "0.3.5"
      }
    },
    "es6-promise": {
      "version": "4.1.1",
      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz",
      "integrity": "sha512-OaU1hHjgJf+b0NzsxCg7NdIYERD6Hy/PEmFLTjw+b65scuisG3Kt4QoTvJ66BBkPZ581gr0kpoVzKnxniM8nng=="
    },
    "es6-set": {
      "version": "0.1.5",
      "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
      "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
      "requires": {
        "d": "1.0.0",
        "es5-ext": "0.10.35",
        "es6-iterator": "2.0.3",
        "es6-symbol": "3.1.1",
        "event-emitter": "0.3.5"
      }
    },
    "es6-symbol": {
      "version": "3.1.1",
      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
      "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
      "requires": {
        "d": "1.0.0",
        "es5-ext": "0.10.35"
      }
    },
    "es6-weak-map": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
      "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
      "requires": {
        "d": "1.0.0",
        "es5-ext": "0.10.35",
        "es6-iterator": "2.0.3",
        "es6-symbol": "3.1.1"
      }
    },
    "escape-html": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
    },
    "escape-string-regexp": {
      "version": "1.0.5",
      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
    },
    "escodegen": {
      "version": "1.9.0",
      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.0.tgz",
      "integrity": "sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw==",
      "requires": {
        "esprima": "3.1.3",
        "estraverse": "4.2.0",
        "esutils": "2.0.2",
        "optionator": "0.8.2",
        "source-map": "0.5.7"
      },
      "dependencies": {
        "esprima": {
          "version": "3.1.3",
          "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
          "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
        },
        "source-map": {
          "version": "0.5.7",
          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
          "optional": true
        }
      }
    },
    "escope": {
      "version": "3.6.0",
      "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz",
      "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=",
      "requires": {
        "es6-map": "0.1.5",
        "es6-weak-map": "2.0.2",
        "esrecurse": "4.2.0",
        "estraverse": "4.2.0"
      }
    },
    "eslint": {
      "version": "4.10.0",
      "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.10.0.tgz",
      "integrity": "sha512-MMVl8P/dYUFZEvolL8PYt7qc5LNdS2lwheq9BYa5Y07FblhcZqFyaUqlS8TW5QITGex21tV4Lk0a3fK8lsJIkA==",
      "requires": {
        "ajv": "5.3.0",
        "babel-code-frame": "6.26.0",
        "chalk": "2.3.0",
        "concat-stream": "1.6.0",
        "cross-spawn": "5.1.0",
        "debug": "3.1.0",
        "doctrine": "2.0.0",
        "eslint-scope": "3.7.1",
        "espree": "3.5.1",
        "esquery": "1.0.0",
        "estraverse": "4.2.0",
        "esutils": "2.0.2",
        "file-entry-cache": "2.0.0",
        "functional-red-black-tree": "1.0.1",
        "glob": "7.1.2",
        "globals": "9.18.0",
        "ignore": "3.3.7",
        "imurmurhash": "0.1.4",
        "inquirer": "3.3.0",
        "is-resolvable": "1.0.0",
        "js-yaml": "3.10.0",
        "json-stable-stringify": "1.0.1",
        "levn": "0.3.0",
        "lodash": "4.17.4",
        "minimatch": "3.0.4",
        "mkdirp": "0.5.1",
        "natural-compare": "1.4.0",
        "optionator": "0.8.2",
        "path-is-inside": "1.0.2",
        "pluralize": "7.0.0",
        "progress": "2.0.0",
        "require-uncached": "1.0.3",
        "semver": "5.4.1",
        "strip-ansi": "4.0.0",
        "strip-json-comments": "2.0.1",
        "table": "4.0.2",
        "text-table": "0.2.0"
      },
      "dependencies": {
        "ansi-regex": {
          "version": "3.0.0",
          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
        },
        "chalk": {
          "version": "2.3.0",
          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
          "integrity":