├── server ├── public ├── app │ ├── service │ │ ├── __init__.py │ │ ├── task.py │ │ └── github.py │ ├── __init__.py │ ├── views │ │ ├── tasks.py │ │ ├── auth.py │ │ └── __init__.py │ ├── config.py │ └── models.py ├── requirements │ ├── docker.txt │ ├── general.txt │ ├── dev.txt │ └── constraints.txt ├── .gitignore ├── gunicorn_entrypoint.py ├── templates │ ├── index.html │ ├── login.html │ └── base.html ├── tests │ └── __init__.py ├── Dockerfile └── manage.py ├── anim.gif ├── front ├── img │ ├── kobin.png │ └── favicon.ico ├── ts │ ├── task.model.ts │ ├── main.ts │ ├── task.component.ts │ ├── task.service.ts │ ├── task-detail.component.ts │ └── task-list.component.ts ├── .gitignore ├── README.md ├── tsconfig.json ├── Dockerfile ├── package.json ├── stylus │ └── style.styl └── yarn.lock ├── .gitignore ├── envrc ├── LICENSE ├── docker-compose.yml └── README.md /server/public: -------------------------------------------------------------------------------- 1 | ../front/public -------------------------------------------------------------------------------- /server/app/service/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kobinpy/kobin-todo/HEAD/anim.gif -------------------------------------------------------------------------------- /front/img/kobin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kobinpy/kobin-todo/HEAD/front/img/kobin.png -------------------------------------------------------------------------------- /front/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kobinpy/kobin-todo/HEAD/front/img/favicon.ico -------------------------------------------------------------------------------- /server/requirements/docker.txt: -------------------------------------------------------------------------------- 1 | -r ./general.txt 2 | click 3 | gunicorn 4 | wsgi-static-middleware 5 | -------------------------------------------------------------------------------- /server/requirements/general.txt: -------------------------------------------------------------------------------- 1 | kobin 2 | jinja2 3 | requests 4 | redis 5 | SQLAlchemy 6 | psycopg2 7 | -------------------------------------------------------------------------------- /server/requirements/dev.txt: -------------------------------------------------------------------------------- 1 | flake8 2 | coverage 3 | click 4 | wsgicli 5 | wsgi_static_middleware 6 | ipython 7 | -------------------------------------------------------------------------------- /front/ts/task.model.ts: -------------------------------------------------------------------------------- 1 | export class Task { 2 | id: number; 3 | title: string; 4 | detail: string; 5 | done: boolean; 6 | } 7 | -------------------------------------------------------------------------------- /front/.gitignore: -------------------------------------------------------------------------------- 1 | ### node 2 | node_modules/ 3 | public/ 4 | ts/*.js 5 | *.js.map 6 | npm-debug.log 7 | 8 | ### Scss 9 | .sass-cache 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### IntelliJ IDEA 2 | .idea/ 3 | 4 | ### DB 5 | db.sqlite3 6 | db.sqlite3.* 7 | 8 | ### sandbox 9 | sandbox/ 10 | 11 | ### direnv 12 | .envrc 13 | -------------------------------------------------------------------------------- /server/app/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from kobin import Kobin, load_config_from_pyfile 3 | from . import views 4 | 5 | config = load_config_from_pyfile(os.environ.get('KOBIN_SETTINGS_FILE', 'app/config.py')) 6 | app = Kobin(config=config) 7 | views.setup_routing(app) 8 | -------------------------------------------------------------------------------- /front/ts/main.ts: -------------------------------------------------------------------------------- 1 | import "es6-shim"; 2 | import "reflect-metadata"; 3 | import "rxjs/Rx"; 4 | import "zone.js/dist/zone"; 5 | 6 | import {bootstrap} from "angular2/platform/browser"; 7 | import {TaskComponent} from './task.component' 8 | 9 | bootstrap(TaskComponent); 10 | -------------------------------------------------------------------------------- /envrc: -------------------------------------------------------------------------------- 1 | export KOBIN_TODO_ENV=develop 2 | export REDIS_PASSWORD=password 3 | export POSTGRES_PASSWORD=password 4 | export SECRET_KEY=secret 5 | 6 | export KOBIN_TODO_GITHUB_CLIENT_ID=xxxxxxxxxxxxxxxxxxxx 7 | export KOBIN_TODO_GITHUB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 8 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | ### Python template 2 | 3 | __pycache__/ 4 | venv/ 5 | 6 | # Installer logs 7 | pip-log.txt 8 | pip-delete-this-directory.txt 9 | 10 | # Unit test / coverage reports 11 | htmlcov/ 12 | .tox/ 13 | .coverage 14 | .coverage.* 15 | .cache 16 | nosetests.xml 17 | coverage.xml 18 | *,cover 19 | -------------------------------------------------------------------------------- /server/gunicorn_entrypoint.py: -------------------------------------------------------------------------------- 1 | from app import app 2 | from os import path 3 | from wsgi_static_middleware import StaticMiddleware 4 | 5 | # Enable wsgi static middleware 6 | BASE_DIR = path.dirname(path.abspath(__name__)) 7 | PUBLIC_DIR = path.join(BASE_DIR, 'public') 8 | 9 | app = StaticMiddleware(app, static_root='/static', static_dirs=[PUBLIC_DIR]) 10 | -------------------------------------------------------------------------------- /front/README.md: -------------------------------------------------------------------------------- 1 | # kobin todo front 2 | 3 | Frontend for Kobin TODO. 4 | 5 | ## Requirements 6 | 7 | - node 7.3 8 | - yarn 9 | 10 | ## How to build without Docker 11 | 12 | ```console 13 | $ npm install 14 | $ npm run build 15 | ``` 16 | 17 | ## Update yarn.lock and package.json 18 | 19 | ```console 20 | $ npm install -g npm-check-updates 21 | $ ncu 22 | $ npm update 23 | ``` 24 | -------------------------------------------------------------------------------- /front/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "noImplicitAny": false, 6 | "sourceMap": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true 10 | }, 11 | "exclude": [ 12 | "node_modules" 13 | ], 14 | "files": [ 15 | "./ts/main.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /front/ts/task.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'angular2/core' 2 | import {HTTP_PROVIDERS} from 'angular2/http' 3 | 4 | import {TaskListComponent} from './task-list.component' 5 | 6 | @Component({ 7 | selector: 'app-task', 8 | template: ` 9 | Now loading... 10 | `, 11 | providers: [HTTP_PROVIDERS], 12 | directives: [TaskListComponent] 13 | }) 14 | export class TaskComponent { 15 | } 16 | -------------------------------------------------------------------------------- /server/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Kobin TODO{% endblock %} 4 | 5 | {% block head %} 6 | {{ super() }} 7 | 8 | 9 | {% endblock %} 10 | 11 | {% block body %} 12 | Loading... 13 | {% endblock %} 14 | 15 | {% block extrafooter %} 16 | {{ super() }} 17 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /front/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:7.3 2 | 3 | RUN mkdir -p /usr/src/public 4 | WORKDIR /usr/src 5 | 6 | ADD ./package.json /usr/src/package.json 7 | ADD ./yarn.lock /usr/src/yarn.lock 8 | 9 | RUN npm install -g yarn && \ 10 | npm install 11 | 12 | ADD ./img /usr/src/img 13 | ADD ./stylus /usr/src/stylus 14 | ADD ./ts /usr/src/ts 15 | ADD ./tsconfig.json /usr/src/tsconfig.json 16 | 17 | RUN npm run build 18 | 19 | # Shared folder should enable after npm build. 20 | VOLUME /usr/src/public 21 | -------------------------------------------------------------------------------- /server/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Kobin TODO{% endblock %} 4 | 5 | {% block head %} 6 | {{ super() }} 7 | 8 | 9 | {% endblock %} 10 | 11 | {% block body %} 12 |
13 | 14 | Login with Github 15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /server/tests/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | import coverage 5 | 6 | 7 | def run(): 8 | os.environ['KOBIN_SETTINGS_FILE'] = 'config/tests.py' 9 | 10 | # start coverage engine 11 | cov = coverage.Coverage(branch=True) 12 | cov.start() 13 | 14 | # run tests 15 | tests = unittest.TestLoader().discover('.') 16 | ok = unittest.TextTestRunner(verbosity=2).run(tests).wasSuccessful() 17 | 18 | # print coverage report 19 | cov.stop() 20 | print('') 21 | cov.report(omit=['manage.py', 'tests/*', 'venv/*', 'config/*']) 22 | 23 | sys.exit(0 if ok else 1) 24 | -------------------------------------------------------------------------------- /server/requirements/constraints.txt: -------------------------------------------------------------------------------- 1 | appnope==0.1.0 2 | click==6.6 3 | coverage==4.2 4 | decorator==4.0.10 5 | flake8==3.2.1 6 | ipython==5.1.0 7 | ipython-genutils==0.1.0 8 | Jinja2==2.8 9 | kobin==0.1.3 10 | MarkupSafe==0.23 11 | mccabe==0.5.3 12 | pexpect==4.2.1 13 | pickleshare==0.7.4 14 | prompt-toolkit==1.0.9 15 | psycopg2==2.6.2 16 | ptyprocess==0.5.1 17 | pycodestyle==2.2.0 18 | pyflakes==1.3.0 19 | Pygments==2.1.3 20 | redis==2.10.5 21 | requests==2.12.4 22 | simplegeneric==0.8.1 23 | six==1.10.0 24 | SQLAlchemy==1.1.4 25 | traitlets==4.3.1 26 | wcwidth==0.1.7 27 | wsgi-lineprof==0.2.0 28 | wsgi-static-middleware==0.1.1 29 | wsgicli==0.4.0 30 | -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-slim 2 | MAINTAINER Masashi Shibata 3 | 4 | RUN apt-get update && apt-get install -y --no-install-recommends git gcc libpq-dev && \ 5 | rm -rf /var/lib/apt/lists/* 6 | 7 | RUN pip install --upgrade pip 8 | ADD requirements /usr/src/requirements 9 | RUN pip install \ 10 | -c /usr/src/requirements/constraints.txt \ 11 | -r /usr/src/requirements/docker.txt 12 | 13 | ADD ./app /usr/src/app 14 | ADD ./templates /usr/src/templates 15 | ADD ./manage.py /usr/src/manage.py 16 | ADD ./gunicorn_entrypoint.py /usr/src/gunicorn_entrypoint.py 17 | 18 | WORKDIR /usr/src 19 | EXPOSE 80 20 | CMD ["gunicorn", "-w", "1", "-b", ":80", "gunicorn_entrypoint:app"] 21 | -------------------------------------------------------------------------------- /server/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block head %} 6 | {% block title %}{% endblock %} - Kobin 7 | 8 | 9 | {% endblock %} 10 | 11 | 12 | {% block body %} 13 | {% endblock %} 14 | {% block extrafooter %} 15 | 16 | {% endblock %} 17 | 18 | 19 | -------------------------------------------------------------------------------- /server/app/views/tasks.py: -------------------------------------------------------------------------------- 1 | from kobin import request, Response, JSONResponse, HTTPError 2 | 3 | from ..service import task as task_service 4 | 5 | 6 | def task_list(): 7 | tasks = [t.serialize for t in task_service.retrieve_tasks()] 8 | return JSONResponse({'tasks': tasks}) 9 | 10 | 11 | def create_task(): 12 | new_task = task_service.create_task(title=request.json['title']) 13 | return JSONResponse(new_task.serialize) 14 | 15 | 16 | def get_task(task_id: int): 17 | task = task_service.retrieve_task(task_id) 18 | if task is None: 19 | raise HTTPError(f"Not found: {request.path}", 404) 20 | return JSONResponse(task.serialize) 21 | 22 | 23 | def update_task(task_id: int): 24 | updated_task = task_service.update_task(task_id, request.json['task']) 25 | return JSONResponse(updated_task.serialize) 26 | 27 | 28 | def delete_task(task_id: int): 29 | if not task_service.delete_task(task_id): 30 | raise HTTPError("Task is not found.", 404) 31 | return Response('', status=204) 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 MASASHI Shibata 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /front/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kobin-todo", 3 | "version": "0.0.1", 4 | "description": "Todo application in Kobin python web-framework.", 5 | "scripts": { 6 | "dir": "mkdir -p ./public/img ./public/js ./public/css", 7 | "tsc": "tsc -p .", 8 | "browserify": "browserify ./ts/main.js -o ./public/js/bundle.js", 9 | "stylus": "stylus ./stylus/style.styl -o ./public/css/style.css", 10 | "img": "cp -r ./img/ ./public/img", 11 | "build": "npm run dir && npm run tsc && npm run browserify && npm run stylus && npm run img" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/c-bata/kobin-todo.git" 16 | }, 17 | "keywords": [], 18 | "author": "c-bata", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/c-bata/kobin-example/issues" 22 | }, 23 | "homepage": "https://github.com/c-bata/kobin-example#readme", 24 | "dependencies": { 25 | "angular2": "^2.0.0-beta.15", 26 | "es6-shim": "^0.35.0", 27 | "reflect-metadata": "^0.1.2", 28 | "rxjs": "^5.0.0-beta.2", 29 | "typescript": "^2.1.4", 30 | "zone.js": "^0.7.4" 31 | }, 32 | "devDependencies": { 33 | "browserify": "^13.0.0", 34 | "stylus": "^0.54.5" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/app/service/task.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, List 2 | from kobin import HTTPError 3 | from .. import app, models 4 | from ..service import github as github_service 5 | 6 | 7 | def retrieve_tasks() -> List[models.Task]: 8 | session = app.config["SESSION"] 9 | return session.query(models.Task).all() 10 | 11 | 12 | def create_task(title: str=None) -> models.Task: 13 | user = github_service.current_user() 14 | if user is None: 15 | raise HTTPError(body='You are not logged in.', status=401) 16 | 17 | new_task = models.Task(title=title, user_id=user.id) 18 | session = app.config["SESSION"] 19 | session.add(new_task) 20 | session.commit() 21 | return new_task 22 | 23 | 24 | def retrieve_task(task_id) -> models.Task: 25 | session = app.config["SESSION"] 26 | return session.query(models.Task).get(task_id) 27 | 28 | 29 | def update_task(task_id: int, new_task: Dict[str, Any]) -> models.Task: 30 | task = retrieve_task(task_id) 31 | task.update(**new_task) 32 | session = app.config["SESSION"] 33 | session.add(task) 34 | session.commit() 35 | return task 36 | 37 | 38 | def delete_task(task_id: int) -> bool: 39 | task = retrieve_task(task_id) 40 | if task is None: 41 | return False 42 | session = app.config["SESSION"] 43 | session.delete(task) 44 | session.commit() 45 | return True 46 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | server: 4 | container_name: kobin_server 5 | build: 6 | context: ./server 7 | environment: 8 | - REDIS_HOST=redis 9 | - REDIS_PORT=6379 10 | - REDIS_DB=0 11 | - POSTGRES_HOST=postgres 12 | - POSTGRES_USER=kobin 13 | - POSTGRES_NAME=kobintodo 14 | 15 | - SECRET_KEY 16 | - REDIS_PASSWORD 17 | - POSTGRES_PASSWORD 18 | - KOBIN_TODO_GITHUB_CLIENT_ID 19 | - KOBIN_TODO_GITHUB_CLIENT_SECRET 20 | links: 21 | - postgres 22 | - redis 23 | ports: 24 | - "8080:80" 25 | volumes_from: 26 | - front 27 | command: gunicorn -w 1 -b :80 --reload --log-level debug gunicorn_entrypoint:app 28 | 29 | front: 30 | container_name: kobin_front 31 | build: 32 | context: ./front 33 | volumes: 34 | - ./front/public:/usr/src/public 35 | - /usr/src/public 36 | 37 | postgres: 38 | image: postgres:latest 39 | container_name: kobin_postgres 40 | environment: 41 | - POSTGRES_USER=kobin 42 | - POSTGRES_DB=kobintodo 43 | - POSTGRES_PASSWORD 44 | ports: 45 | - "5432:5432" 46 | 47 | redis: 48 | image: redis:latest 49 | container_name: kobin_redis 50 | environment: 51 | - REDIS_PASSWORD 52 | ports: 53 | - "6379:6379" 54 | command: redis-server --requirepass ${REDIS_PASSWORD} 55 | -------------------------------------------------------------------------------- /server/manage.py: -------------------------------------------------------------------------------- 1 | import click 2 | import subprocess 3 | import sys 4 | 5 | from app import app 6 | 7 | 8 | @click.group() 9 | def cli(): 10 | """This is a management script for the kobin-todo application.""" 11 | pass 12 | 13 | 14 | @cli.command() 15 | def migrate(): 16 | """Runs database migrations.""" 17 | from app.models import Base 18 | metadata = Base.metadata 19 | engine = app.config.get("SQLALCHEMY_ENGINE") 20 | metadata.create_all(engine) 21 | 22 | 23 | @cli.command() 24 | def run(): 25 | """Runs server.""" 26 | subprocess.call(['wsgicli', 'run', 'app/__init__.py', 'app', 27 | '-p', '8080', '--reload', 28 | '--static', '--static-root', '/static/', 29 | '--static-dirs', './public/']) 30 | 31 | 32 | @cli.command() 33 | def shell(): 34 | """Run shell""" 35 | subprocess.call(['wsgicli', 'shell', 'manage.py', 'app', '-i', '--models', 'ipython']) 36 | 37 | 38 | @cli.command() 39 | def lint(): 40 | """Runs code linter.""" 41 | lint = subprocess.call(['flake8', '--ignore=E501', 'app/', 'manage.py']) 42 | if lint: 43 | print("OK") 44 | sys.exit(lint) 45 | 46 | 47 | @cli.command() 48 | def test(): 49 | """Runs unit tests.""" 50 | tests = subprocess.call(['python', '-c', 'import tests; tests.run()']) 51 | sys.exit(tests) 52 | 53 | if __name__ == '__main__': 54 | cli() 55 | -------------------------------------------------------------------------------- /server/app/views/auth.py: -------------------------------------------------------------------------------- 1 | from kobin import request, RedirectResponse 2 | from datetime import timedelta 3 | 4 | from .. import app 5 | from ..service import github as github_service 6 | 7 | 8 | def github_oauth_callback(): 9 | code = request.query['code'] 10 | payload = { 11 | "client_id": app.config.get('GITHUB_CLIENT_ID'), 12 | "client_secret": app.config.get('GITHUB_CLIENT_SECRET'), 13 | "code": code, 14 | } 15 | access_token = github_service.get_access_token(payload) 16 | user_info = github_service.get_github_user_info(access_token) 17 | user = github_service.get_or_create_user( 18 | nickname=user_info['login'], 19 | name=user_info['name'], 20 | avatar_url=user_info['avatar_url'], 21 | auth_service='github', 22 | auth_service_id=user_info['id'], 23 | email=user_info['email'], 24 | ) 25 | 26 | r = app.config.get('REDIS') 27 | r.set('access_token_{id}'.format(id=user.id), access_token) 28 | response = RedirectResponse(app.router.reverse('top')) 29 | response.set_cookie("user_id", f"access_token_{user.id}", 30 | max_age=timedelta(days=1), path='/', 31 | secret=app.config['SECRET_KEY']) 32 | return response 33 | 34 | 35 | def logout(): 36 | response = RedirectResponse(app.router.reverse('top')) 37 | response.delete_cookie('user_id') 38 | return response 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kobin-todo 2 | 3 | Todo application using Kobin. 4 | 5 | ![animation](./anim.gif) 6 | 7 | ## Running Kobin TODO by Docker 8 | 9 | Set Environment Variables. 10 | 11 | ```sh 12 | export KOBIN_TODO_GITHUB_CLIENT_ID=xxxxxxxxxxxxxxxxxxxx 13 | export KOBIN_TODO_GITHUB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 14 | ``` 15 | 16 | Run with Docker. 17 | 18 | ```console 19 | $ docker-compose build 20 | $ docker-compose up -d 21 | $ docker-compose run server python manage.py migrate 22 | ``` 23 | 24 | Other: 25 | 26 | - bash: `docker-compose exec server /bin/bash` 27 | - logs: `docker-compose logs -f server` 28 | - psql: `psql -h localhost --user kobin kobintodo` 29 | 30 | 31 | ## How to build 32 | 33 | Set Environment Variables 34 | 35 | ```sh 36 | export KOBIN_TODO_ENV=develop 37 | export KOBIN_TODO_GITHUB_CLIENT_ID=xxxxxxxxxxxxxxxxxxxx 38 | export KOBIN_TODO_GITHUB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 39 | ``` 40 | 41 | Compile TypeScript and Stylus 42 | 43 | ```console 44 | $ cd front 45 | $ npm install 46 | $ npm run build 47 | ``` 48 | 49 | Run redis and postgresql 50 | 51 | ```console 52 | $ docker-compose up -d redis postgres 53 | ``` 54 | 55 | Setup python interpreter 56 | 57 | ```console 58 | $ python3.6 -m venv venv 59 | $ source venv/bin/activate 60 | $ pip install -c requirements/constraints.txt -r requirements/general.txt -r requirements.dev.txt 61 | $ python manage.py migrate 62 | ``` 63 | 64 | Run 65 | 66 | ```console 67 | $ python manage.py run 68 | ``` 69 | -------------------------------------------------------------------------------- /server/app/views/__init__.py: -------------------------------------------------------------------------------- 1 | def setup_routing(app): 2 | from kobin import TemplateResponse, request, HTTPError 3 | from . import tasks 4 | from . import auth 5 | 6 | # tasks 7 | app.route('/api/tasks', 'GET', 'task-list', tasks.task_list) 8 | app.route('/api/tasks', 'POST', 'task-create', tasks.create_task) 9 | app.route('/api/tasks/{task_id}', 'GET', 'task-detail', tasks.get_task) 10 | app.route('/api/tasks/{task_id}', 'PATCH', 'task-task-update', tasks.update_task) 11 | app.route('/api/tasks/{task_id}', 'DELETE', 'task-delete', tasks.delete_task) 12 | 13 | # auth 14 | app.route('/auth/github/callback', 'GET', 'github-callback', auth.github_oauth_callback) 15 | app.route('/logout', 'GET', '/', auth.logout) 16 | 17 | @app.route('/', method='GET', name='top') 18 | def index(): 19 | if request.environ.get('kobin.user'): 20 | return TemplateResponse('index.html') 21 | return TemplateResponse('login.html', client_id=app.config['GITHUB_CLIENT_ID']) 22 | 23 | @app.before_request 24 | def before(): 25 | redis_access_token_key = request.get_cookie("user_id", default=None, 26 | secret=app.config['SECRET_KEY']) 27 | request.environ['kobin.user'] = redis_access_token_key 28 | 29 | @app.after_request 30 | def after(response): 31 | if isinstance(response, HTTPError): 32 | s = app.config['DB']['SESSION'] 33 | s.rollback() 34 | return response 35 | -------------------------------------------------------------------------------- /front/stylus/style.styl: -------------------------------------------------------------------------------- 1 | .sidebar 2 | position: fixed; 3 | width: 300px; 4 | top: 0; 5 | left: 0; 6 | height: 100%; 7 | 8 | color: #eee; 9 | background-color: #389adc; 10 | 11 | .task-titles 12 | height: calc(100% - 170px - 100px); 13 | overflow-y: auto; 14 | margin-top: 170px; 15 | padding: 10px; 16 | 17 | .sidebar-top 18 | position: absolute; 19 | left: 0; 20 | top: 0; 21 | width: 100%; 22 | 23 | .logo 24 | width: 100%; 25 | padding: 30px; 26 | 27 | footer 28 | position: absolute; 29 | left: 0; 30 | bottom: 0; 31 | width: 100%; 32 | height: 100px; 33 | 34 | display: flex; 35 | display: -webkit-flex; 36 | flex-direction: column; 37 | -webkit-flex-direction: column; 38 | align-items: center; 39 | -webkit-align-items: center; 40 | 41 | a 42 | color: #ddd; 43 | 44 | html, body 45 | height: 100%; 46 | 47 | .top-container 48 | width: 100%; 49 | height: 100%; 50 | background: #4990E2; 51 | 52 | display: flex; 53 | display: -webkit-flex; 54 | 55 | flex-direction: column; 56 | -webkit-flex-direction: column; 57 | justify-content: center; 58 | -webkit-justify-content: center; 59 | align-items: center; 60 | -webkit-align-items: center; 61 | 62 | .top-logo 63 | width: 300px; 64 | margin: 50px auto; 65 | 66 | .top-oauth-link 67 | font-size: 20px; 68 | color: #EFEFEF; 69 | font-weight: lighter; 70 | 71 | main 72 | padding: 20px; 73 | margin-left: 300px; 74 | -------------------------------------------------------------------------------- /server/app/service/github.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | from typing import Dict 4 | from kobin import request, HTTPError 5 | 6 | from .. import app, models 7 | 8 | 9 | def get_or_create_user(nickname: str, name: str, auth_service: str, avatar_url: str, 10 | auth_service_id: int, email: str) -> models.User: 11 | session = app.config["SESSION"] 12 | user = session.query(models.User).filter_by( 13 | auth_service=auth_service, auth_service_id=auth_service_id 14 | ).first() 15 | 16 | if user is None: 17 | user = models.User( 18 | nickname=nickname, 19 | name=name, 20 | avatar_url=avatar_url, 21 | auth_service=auth_service, 22 | auth_service_id=auth_service_id, 23 | email=email, 24 | ) 25 | session.add(user) 26 | session.commit() 27 | return user 28 | 29 | 30 | def current_user() -> models.User: 31 | user_id = int(request.environ['kobin.user'].split('_')[-1]) 32 | session = app.config["SESSION"] 33 | return session.query(models.User).get(user_id) 34 | 35 | 36 | def get_github_user_info(access_token: str) -> Dict: 37 | url = "https://api.github.com/user?access_token={token}".format(token=access_token) 38 | res = requests.get(url) 39 | return res.json() 40 | 41 | 42 | def get_access_token(payload: Dict[str, str]): 43 | headers = {'Content-Type': 'application/json'} 44 | res = requests.post('https://github.com/login/oauth/access_token', 45 | data=json.dumps(payload), headers=headers) 46 | if res.status_code != 200: 47 | raise HTTPError("Github authentication error", 500) 48 | res_params = {k: v for k, v in 49 | [p.split('=') for p in res.text.split('&')]} 50 | return res_params['access_token'] 51 | -------------------------------------------------------------------------------- /server/app/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import redis 3 | from sqlalchemy.orm import sessionmaker 4 | from sqlalchemy import create_engine 5 | 6 | ENV = os.getenv('KOBIN_TODO_ENV') 7 | 8 | BASE_DIR = os.path.dirname(os.path.abspath(__name__)) 9 | TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')] 10 | 11 | # Github 12 | # ====== 13 | GITHUB_CLIENT_ID = os.environ.get('KOBIN_TODO_GITHUB_CLIENT_ID') 14 | GITHUB_CLIENT_SECRET = os.environ.get('KOBIN_TODO_GITHUB_CLIENT_SECRET') 15 | 16 | 17 | # Database 18 | # ======== 19 | if ENV == 'develop': 20 | REDIS_HOST = 'localhost' 21 | REDIS_PORT = 6379 22 | REDIS_DB = 0 23 | REDIS_PASSWORD = 'password' 24 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://kobin:password@localhost/kobintodo' 25 | DEBUG = True 26 | SECRET_KEY = b'secretkey' 27 | elif ENV == 'test': 28 | REDIS_HOST = 'localhost' 29 | REDIS_PORT = 6379 30 | REDIS_DB = 0 31 | REDIS_PASSWORD = 'password' 32 | REDIS_PASSWORD = None 33 | SQLALCHEMY_DATABASE_URI = 'sqlite://' 34 | DEBUG = True 35 | SECRET_KEY = b'secretkey' 36 | else: 37 | DEBUG = False 38 | SECRET_KEY = os.environ.get('SECRET_KEY').encode('utf-8') 39 | 40 | REDIS_HOST = os.environ.get('REDIS_HOST') 41 | REDIS_PORT = os.environ.get('REDIS_PORT') 42 | REDIS_PASSWORD = os.environ.get('REDIS_PASSWORD') 43 | REDIS_DB = os.environ.get('REDIS_DB') 44 | 45 | host = os.environ.get('POSTGRES_HOST') 46 | user = os.environ.get('POSTGRES_USER') 47 | password = os.environ.get('POSTGRES_PASSWORD') 48 | db = os.environ.get('POSTGRES_NAME') 49 | SQLALCHEMY_DATABASE_URI = f'postgresql+psycopg2://{user}:{password}@{host}/{db}' 50 | 51 | # SQLAlchemy 52 | Session = sessionmaker() 53 | SQLALCHEMY_ENGINE = create_engine(SQLALCHEMY_DATABASE_URI, echo=True) 54 | Session.configure(bind=SQLALCHEMY_ENGINE) 55 | SQLALCHEMY_SESSION = Session() 56 | 57 | # for Redis 58 | REDIS = redis.StrictRedis( 59 | host=REDIS_HOST, 60 | port=REDIS_PORT, 61 | db=REDIS_DB, 62 | password=REDIS_PASSWORD, 63 | ) 64 | -------------------------------------------------------------------------------- /front/ts/task.service.ts: -------------------------------------------------------------------------------- 1 | import {Task} from './task.model' 2 | import {Injectable} from 'angular2/core' 3 | import {Http, Response, Headers, RequestOptions} from 'angular2/http'; 4 | import {Observable} from "rxjs/Observable"; 5 | 6 | @Injectable() 7 | export class TaskService { 8 | constructor( 9 | private http: Http 10 | ) { } 11 | 12 | private _tasksUrl = 'api/tasks'; 13 | private defaultRequestOptions: RequestOptions = new RequestOptions({ 14 | headers: new Headers({ 'Content-Type': 'application/json' }), 15 | }); 16 | 17 | getTasks(): Observable { 18 | return this.http.get(this._tasksUrl) 19 | .map(this.extractTasks) 20 | .catch(this.handleError); 21 | } 22 | 23 | addTask(title: string): Observable { 24 | let body = JSON.stringify({ title }); 25 | return this.http.post(this._tasksUrl, body, this.defaultRequestOptions) 26 | .map(this.extractTask) 27 | .catch(this.handleError); 28 | } 29 | 30 | updateTask(task: Task): Observable { 31 | let body = JSON.stringify({ task }); 32 | return this.http.patch(`api/tasks/${task.id}`, body, this.defaultRequestOptions) 33 | .map(this.extractTask) 34 | .catch(this.handleError) 35 | } 36 | 37 | deleteTask(task: Task): Observable { 38 | return this.http.delete(`api/tasks/${task.id}`, this.defaultRequestOptions) 39 | .map(res => res) 40 | .catch(this.handleError) 41 | } 42 | 43 | private extractTasks(res: Response) { 44 | if (res.status < 200 || res.status >= 300) { 45 | throw new Error('Bad response status: ' + res.status); 46 | } 47 | let body = res.json(); 48 | return body.tasks || {}; 49 | } 50 | 51 | private extractTask(res: Response) { 52 | if (res.status < 200 || res.status >= 300) { 53 | throw new Error('Bad response status: ' + res.status); 54 | } 55 | let body = res.json(); 56 | return body || {}; 57 | } 58 | 59 | private handleError(error: any) { 60 | let errMsg = error.message || 'Server error'; 61 | console.error(errMsg); 62 | return Observable.throw(errMsg); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /front/ts/task-detail.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, Input} from 'angular2/core' 2 | import {Task} from './task.model' 3 | import {TaskService} from './task.service' 4 | 5 | @Component({ 6 | selector: 'app-task-detail', 7 | template: ` 8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | This task is done. 20 |
21 |
22 | 23 | 24 |
25 | 26 | 31 |
32 | `, 33 | }) 34 | export class TaskDetailComponent { 35 | @Input() task: Task; 36 | @Input() tasks: Task[]; 37 | errorMessage: string; 38 | 39 | constructor( 40 | private _taskService: TaskService 41 | ) {} 42 | 43 | updateTask(task: Task) { 44 | this._taskService.updateTask(task) 45 | .subscribe( 46 | task => this.task = task, 47 | error => this.errorMessage = error 48 | ); 49 | } 50 | 51 | deleteTask(task: Task) { 52 | let index = this.tasks.indexOf(task); 53 | this._taskService.deleteTask(task) 54 | .subscribe( 55 | res => { 56 | this.tasks.splice(index, 1); 57 | this.task = null; 58 | }, 59 | error => this.errorMessage = error 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /server/app/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext import declarative 2 | from datetime import datetime 3 | from sqlalchemy import ( 4 | Column, Integer, Unicode, UnicodeText, Boolean, DateTime, ForeignKey 5 | ) 6 | 7 | Base = declarative.declarative_base() 8 | 9 | 10 | class User(Base): 11 | __tablename__ = 'users' 12 | id = Column(Integer, primary_key=True) 13 | name = Column(Unicode(255), nullable=False) 14 | nickname = Column(Unicode(255), nullable=False) 15 | email = Column(Unicode(255), nullable=False) 16 | avatar_url = Column(Unicode(512), nullable=False) 17 | auth_service = Column(Unicode(16)) # Now, Support only github. 18 | auth_service_id = Column(Integer, nullable=False) 19 | updated_at = Column(DateTime, default=datetime.now(), nullable=False) 20 | created_at = Column(DateTime, default=datetime.now(), nullable=False) 21 | 22 | def __repr__(self): 23 | return f"" 24 | 25 | @property 26 | def serialize(self): 27 | return { 28 | 'id': self.id, 29 | 'name': self.name, 30 | 'nickname': self.name, 31 | 'email': self.email, 32 | 'avatar_url': self.avatar_url, 33 | 'updated_at': self.updated_at.strftime('%Y-%m-%d'), 34 | 'created_at': self.created_at.strftime('%Y-%m-%d'), 35 | } 36 | 37 | 38 | class Task(Base): 39 | __tablename__ = 'tasks' 40 | id = Column(Integer, primary_key=True) 41 | title = Column(Unicode(255), nullable=False) 42 | detail = Column(UnicodeText) 43 | done = Column(Boolean, nullable=False, default=False) 44 | user_id = Column(Integer, ForeignKey("users.id"), nullable=False) 45 | updated_at = Column(DateTime, default=datetime.now(), nullable=False) 46 | created_at = Column(DateTime, default=datetime.now(), nullable=False) 47 | 48 | def __repr__(self): 49 | return f"" 50 | 51 | def update(self, title=None, detail=None, done=None, **kwargs): 52 | if title is not None: 53 | self.title = title 54 | if detail is not None: 55 | self.detail = detail 56 | if done is not None: 57 | self.done = done 58 | self.updated_at = datetime.now() 59 | 60 | @property 61 | def serialize(self): 62 | return { 63 | 'id': self.id, 64 | 'title': self.title, 65 | 'detail': self.detail, 66 | 'done': self.done, 67 | 'user_id': self.user_id, 68 | 'updated_at': self.updated_at.strftime('%Y-%m-%d'), 69 | 'created_at': self.created_at.strftime('%Y-%m-%d'), 70 | } -------------------------------------------------------------------------------- /front/ts/task-list.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from 'angular2/core'; 2 | 3 | import {Task} from './task.model' 4 | import {TaskService} from "./task.service"; 5 | import {TaskDetailComponent} from './task-detail.component' 6 | 7 | @Component({ 8 | selector: 'app-task-list', 9 | template: ` 10 | 40 | 41 |
42 | 43 |
44 | `, 45 | providers: [TaskService], 46 | directives: [TaskDetailComponent], 47 | styles: [` 48 | li { list-style: none; padding: 10px 0 10px 20px; } 49 | ul { padding-left: 0px; } 50 | .selected { background-color: #2980b9; } 51 | .task-add { padding: 10px 20px 10px 20px; } 52 | .task-add { 53 | display: flex; 54 | display: -webkit-flex; 55 | flex-flow: row nowrap; 56 | } 57 | .task-add-input { 58 | background: rgba(0, 0, 0, 0.2); 59 | border-color: #ddd; 60 | border-width: 2px; 61 | color: #fff; 62 | border-radius: 0px; 63 | flex-grow: 3; 64 | margin-right: 4px; 65 | } 66 | .task-add-btn { 67 | background: #eee; 68 | color: rgba(0, 0, 0, 0.7); 69 | border-radius: 0px; 70 | } 71 | `], 72 | }) 73 | export class TaskListComponent implements OnInit { 74 | tasks: Task[] = []; 75 | selectedTask: Task; 76 | errorMessage: string; 77 | 78 | constructor( 79 | private _taskService: TaskService 80 | ) {} 81 | 82 | getTasks() { 83 | this._taskService.getTasks() 84 | .subscribe( 85 | tasks => this.tasks = tasks, 86 | error => this.errorMessage = error 87 | ); 88 | } 89 | 90 | addTask(title: string) { 91 | if (!title) { 92 | return; 93 | } 94 | this._taskService.addTask(title) 95 | .subscribe( 96 | task => { 97 | this.tasks.push(task); 98 | this.selectedTask = task; 99 | }, 100 | error => this.errorMessage = error 101 | ); 102 | } 103 | 104 | doneTaskLength() { 105 | return this.tasks 106 | .filter(task => task.done) 107 | .length 108 | } 109 | 110 | toggleDone(task: Task) { 111 | task.done = !task.done; 112 | this._taskService.updateTask(task) 113 | .subscribe( 114 | task => { 115 | this.tasks.filter(t => t.id === task.id) 116 | .map(t => t.done = task.done) 117 | }, 118 | error => this.errorMessage = error 119 | ); 120 | } 121 | 122 | ngOnInit() { 123 | this.getTasks(); 124 | } 125 | 126 | onSelect(task: Task) { 127 | this.selectedTask = task; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /front/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | JSONStream@^1.0.3: 6 | version "1.3.0" 7 | resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.0.tgz#680ab9ac6572a8a1a207e0b38721db1c77b215e5" 8 | dependencies: 9 | jsonparse "^1.2.0" 10 | through ">=2.2.7 <3" 11 | 12 | acorn@^1.0.3: 13 | version "1.2.2" 14 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-1.2.2.tgz#c8ce27de0acc76d896d2b1fad3df588d9e82f014" 15 | 16 | acorn@^2.7.0: 17 | version "2.7.0" 18 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" 19 | 20 | acorn@^3.1.0: 21 | version "3.3.0" 22 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" 23 | 24 | amdefine@>=0.0.4: 25 | version "1.0.1" 26 | resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" 27 | 28 | angular2@^2.0.0-beta.15: 29 | version "2.0.0-beta.21" 30 | resolved "https://registry.yarnpkg.com/angular2/-/angular2-2.0.0-beta.21.tgz#78643e590641526a2596dcd1d5cca3a5509ed76d" 31 | 32 | array-filter@~0.0.0: 33 | version "0.0.1" 34 | resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" 35 | 36 | array-map@~0.0.0: 37 | version "0.0.0" 38 | resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" 39 | 40 | array-reduce@~0.0.0: 41 | version "0.0.0" 42 | resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" 43 | 44 | asn1.js@^4.0.0: 45 | version "4.9.0" 46 | resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.0.tgz#f71a1243f3e79d46d7b07d7fbf4824ee73af054a" 47 | dependencies: 48 | bn.js "^4.0.0" 49 | inherits "^2.0.1" 50 | minimalistic-assert "^1.0.0" 51 | 52 | assert@~1.3.0: 53 | version "1.3.0" 54 | resolved "https://registry.yarnpkg.com/assert/-/assert-1.3.0.tgz#03939a622582a812cc202320a0b9a56c9b815849" 55 | dependencies: 56 | util "0.10.3" 57 | 58 | astw@^2.0.0: 59 | version "2.0.0" 60 | resolved "https://registry.yarnpkg.com/astw/-/astw-2.0.0.tgz#08121ac8288d35611c0ceec663f6cd545604897d" 61 | dependencies: 62 | acorn "^1.0.3" 63 | 64 | balanced-match@^0.4.1: 65 | version "0.4.2" 66 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 67 | 68 | base64-js@^1.0.2: 69 | version "1.2.0" 70 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" 71 | 72 | bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: 73 | version "4.11.6" 74 | resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" 75 | 76 | brace-expansion@^1.0.0: 77 | version "1.1.6" 78 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" 79 | dependencies: 80 | balanced-match "^0.4.1" 81 | concat-map "0.0.1" 82 | 83 | brorand@^1.0.1: 84 | version "1.0.6" 85 | resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.6.tgz#4028706b915f91f7b349a2e0bf3c376039d216e5" 86 | 87 | browser-pack@^6.0.1: 88 | version "6.0.2" 89 | resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.2.tgz#f86cd6cef4f5300c8e63e07a4d512f65fbff4531" 90 | dependencies: 91 | JSONStream "^1.0.3" 92 | combine-source-map "~0.7.1" 93 | defined "^1.0.0" 94 | through2 "^2.0.0" 95 | umd "^3.0.0" 96 | 97 | browser-resolve@^1.11.0, browser-resolve@^1.7.0: 98 | version "1.11.2" 99 | resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" 100 | dependencies: 101 | resolve "1.1.7" 102 | 103 | browserify-aes@^1.0.0, browserify-aes@^1.0.4: 104 | version "1.0.6" 105 | resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" 106 | dependencies: 107 | buffer-xor "^1.0.2" 108 | cipher-base "^1.0.0" 109 | create-hash "^1.1.0" 110 | evp_bytestokey "^1.0.0" 111 | inherits "^2.0.1" 112 | 113 | browserify-cipher@^1.0.0: 114 | version "1.0.0" 115 | resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" 116 | dependencies: 117 | browserify-aes "^1.0.4" 118 | browserify-des "^1.0.0" 119 | evp_bytestokey "^1.0.0" 120 | 121 | browserify-des@^1.0.0: 122 | version "1.0.0" 123 | resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" 124 | dependencies: 125 | cipher-base "^1.0.1" 126 | des.js "^1.0.0" 127 | inherits "^2.0.1" 128 | 129 | browserify-rsa@^4.0.0: 130 | version "4.0.1" 131 | resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" 132 | dependencies: 133 | bn.js "^4.1.0" 134 | randombytes "^2.0.1" 135 | 136 | browserify-sign@^4.0.0: 137 | version "4.0.0" 138 | resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.0.tgz#10773910c3c206d5420a46aad8694f820b85968f" 139 | dependencies: 140 | bn.js "^4.1.1" 141 | browserify-rsa "^4.0.0" 142 | create-hash "^1.1.0" 143 | create-hmac "^1.1.2" 144 | elliptic "^6.0.0" 145 | inherits "^2.0.1" 146 | parse-asn1 "^5.0.0" 147 | 148 | browserify-zlib@~0.1.2: 149 | version "0.1.4" 150 | resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" 151 | dependencies: 152 | pako "~0.2.0" 153 | 154 | browserify@^13.0.0: 155 | version "13.1.1" 156 | resolved "https://registry.yarnpkg.com/browserify/-/browserify-13.1.1.tgz#72a2310e2f706ed87db929cf0ee73a5e195d9bb0" 157 | dependencies: 158 | JSONStream "^1.0.3" 159 | assert "~1.3.0" 160 | browser-pack "^6.0.1" 161 | browser-resolve "^1.11.0" 162 | browserify-zlib "~0.1.2" 163 | buffer "^4.1.0" 164 | cached-path-relative "^1.0.0" 165 | concat-stream "~1.5.1" 166 | console-browserify "^1.1.0" 167 | constants-browserify "~1.0.0" 168 | crypto-browserify "^3.0.0" 169 | defined "^1.0.0" 170 | deps-sort "^2.0.0" 171 | domain-browser "~1.1.0" 172 | duplexer2 "~0.1.2" 173 | events "~1.1.0" 174 | glob "^5.0.15" 175 | has "^1.0.0" 176 | htmlescape "^1.1.0" 177 | https-browserify "~0.0.0" 178 | inherits "~2.0.1" 179 | insert-module-globals "^7.0.0" 180 | labeled-stream-splicer "^2.0.0" 181 | module-deps "^4.0.8" 182 | os-browserify "~0.1.1" 183 | parents "^1.0.1" 184 | path-browserify "~0.0.0" 185 | process "~0.11.0" 186 | punycode "^1.3.2" 187 | querystring-es3 "~0.2.0" 188 | read-only-stream "^2.0.0" 189 | readable-stream "^2.0.2" 190 | resolve "^1.1.4" 191 | shasum "^1.0.0" 192 | shell-quote "^1.4.3" 193 | stream-browserify "^2.0.0" 194 | stream-http "^2.0.0" 195 | string_decoder "~0.10.0" 196 | subarg "^1.0.0" 197 | syntax-error "^1.1.1" 198 | through2 "^2.0.0" 199 | timers-browserify "^1.0.1" 200 | tty-browserify "~0.0.0" 201 | url "~0.11.0" 202 | util "~0.10.1" 203 | vm-browserify "~0.0.1" 204 | xtend "^4.0.0" 205 | 206 | buffer-shims@^1.0.0: 207 | version "1.0.0" 208 | resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" 209 | 210 | buffer-xor@^1.0.2: 211 | version "1.0.3" 212 | resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" 213 | 214 | buffer@^4.1.0: 215 | version "4.9.1" 216 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" 217 | dependencies: 218 | base64-js "^1.0.2" 219 | ieee754 "^1.1.4" 220 | isarray "^1.0.0" 221 | 222 | builtin-status-codes@^2.0.0: 223 | version "2.0.0" 224 | resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-2.0.0.tgz#6f22003baacf003ccd287afe6872151fddc58579" 225 | 226 | cached-path-relative@^1.0.0: 227 | version "1.0.0" 228 | resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.0.tgz#d1094c577fbd9a8b8bd43c96af6188aa205d05f4" 229 | 230 | cipher-base@^1.0.0, cipher-base@^1.0.1: 231 | version "1.0.3" 232 | resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" 233 | dependencies: 234 | inherits "^2.0.1" 235 | 236 | combine-source-map@~0.7.1: 237 | version "0.7.2" 238 | resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e" 239 | dependencies: 240 | convert-source-map "~1.1.0" 241 | inline-source-map "~0.6.0" 242 | lodash.memoize "~3.0.3" 243 | source-map "~0.5.3" 244 | 245 | concat-map@0.0.1: 246 | version "0.0.1" 247 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 248 | 249 | concat-stream@~1.5.0, concat-stream@~1.5.1: 250 | version "1.5.2" 251 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" 252 | dependencies: 253 | inherits "~2.0.1" 254 | readable-stream "~2.0.0" 255 | typedarray "~0.0.5" 256 | 257 | console-browserify@^1.1.0: 258 | version "1.1.0" 259 | resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" 260 | dependencies: 261 | date-now "^0.1.4" 262 | 263 | constants-browserify@~1.0.0: 264 | version "1.0.0" 265 | resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" 266 | 267 | convert-source-map@~1.1.0: 268 | version "1.1.3" 269 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" 270 | 271 | core-util-is@~1.0.0: 272 | version "1.0.2" 273 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 274 | 275 | create-ecdh@^4.0.0: 276 | version "4.0.0" 277 | resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" 278 | dependencies: 279 | bn.js "^4.1.0" 280 | elliptic "^6.0.0" 281 | 282 | create-hash@^1.1.0, create-hash@^1.1.1: 283 | version "1.1.2" 284 | resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad" 285 | dependencies: 286 | cipher-base "^1.0.1" 287 | inherits "^2.0.1" 288 | ripemd160 "^1.0.0" 289 | sha.js "^2.3.6" 290 | 291 | create-hmac@^1.1.0, create-hmac@^1.1.2: 292 | version "1.1.4" 293 | resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" 294 | dependencies: 295 | create-hash "^1.1.0" 296 | inherits "^2.0.1" 297 | 298 | crypto-browserify@^3.0.0: 299 | version "3.11.0" 300 | resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522" 301 | dependencies: 302 | browserify-cipher "^1.0.0" 303 | browserify-sign "^4.0.0" 304 | create-ecdh "^4.0.0" 305 | create-hash "^1.1.0" 306 | create-hmac "^1.1.0" 307 | diffie-hellman "^5.0.0" 308 | inherits "^2.0.1" 309 | pbkdf2 "^3.0.3" 310 | public-encrypt "^4.0.0" 311 | randombytes "^2.0.0" 312 | 313 | css-parse@1.7.x: 314 | version "1.7.0" 315 | resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-1.7.0.tgz#321f6cf73782a6ff751111390fc05e2c657d8c9b" 316 | 317 | date-now@^0.1.4: 318 | version "0.1.4" 319 | resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" 320 | 321 | debug@*: 322 | version "2.5.2" 323 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.5.2.tgz#50c295a53dbf1657146e0c1b21307275e90d49cb" 324 | dependencies: 325 | ms "0.7.2" 326 | 327 | defined@^1.0.0: 328 | version "1.0.0" 329 | resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" 330 | 331 | deps-sort@^2.0.0: 332 | version "2.0.0" 333 | resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb5" 334 | dependencies: 335 | JSONStream "^1.0.3" 336 | shasum "^1.0.0" 337 | subarg "^1.0.0" 338 | through2 "^2.0.0" 339 | 340 | des.js@^1.0.0: 341 | version "1.0.0" 342 | resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" 343 | dependencies: 344 | inherits "^2.0.1" 345 | minimalistic-assert "^1.0.0" 346 | 347 | detective@^4.0.0: 348 | version "4.3.2" 349 | resolved "https://registry.yarnpkg.com/detective/-/detective-4.3.2.tgz#77697e2e7947ac3fe7c8e26a6d6f115235afa91c" 350 | dependencies: 351 | acorn "^3.1.0" 352 | defined "^1.0.0" 353 | 354 | diffie-hellman@^5.0.0: 355 | version "5.0.2" 356 | resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" 357 | dependencies: 358 | bn.js "^4.1.0" 359 | miller-rabin "^4.0.0" 360 | randombytes "^2.0.0" 361 | 362 | domain-browser@~1.1.0: 363 | version "1.1.7" 364 | resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" 365 | 366 | duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: 367 | version "0.1.4" 368 | resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" 369 | dependencies: 370 | readable-stream "^2.0.2" 371 | 372 | elliptic@^6.0.0: 373 | version "6.3.2" 374 | resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.2.tgz#e4c81e0829cf0a65ab70e998b8232723b5c1bc48" 375 | dependencies: 376 | bn.js "^4.4.0" 377 | brorand "^1.0.1" 378 | hash.js "^1.0.0" 379 | inherits "^2.0.1" 380 | 381 | es6-shim@^0.35.0: 382 | version "0.35.2" 383 | resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.2.tgz#45c5b3eb2f792ed28f130d826239be50affb897f" 384 | 385 | events@~1.1.0: 386 | version "1.1.1" 387 | resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" 388 | 389 | evp_bytestokey@^1.0.0: 390 | version "1.0.0" 391 | resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53" 392 | dependencies: 393 | create-hash "^1.1.1" 394 | 395 | fs.realpath@^1.0.0: 396 | version "1.0.0" 397 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 398 | 399 | function-bind@^1.0.2: 400 | version "1.1.0" 401 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" 402 | 403 | glob@7.0.x: 404 | version "7.0.6" 405 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" 406 | dependencies: 407 | fs.realpath "^1.0.0" 408 | inflight "^1.0.4" 409 | inherits "2" 410 | minimatch "^3.0.2" 411 | once "^1.3.0" 412 | path-is-absolute "^1.0.0" 413 | 414 | glob@^5.0.15: 415 | version "5.0.15" 416 | resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" 417 | dependencies: 418 | inflight "^1.0.4" 419 | inherits "2" 420 | minimatch "2 || 3" 421 | once "^1.3.0" 422 | path-is-absolute "^1.0.0" 423 | 424 | has@^1.0.0: 425 | version "1.0.1" 426 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" 427 | dependencies: 428 | function-bind "^1.0.2" 429 | 430 | hash.js@^1.0.0: 431 | version "1.0.3" 432 | resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573" 433 | dependencies: 434 | inherits "^2.0.1" 435 | 436 | htmlescape@^1.1.0: 437 | version "1.1.1" 438 | resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" 439 | 440 | https-browserify@~0.0.0: 441 | version "0.0.1" 442 | resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" 443 | 444 | ieee754@^1.1.4: 445 | version "1.1.8" 446 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" 447 | 448 | indexof@0.0.1: 449 | version "0.0.1" 450 | resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" 451 | 452 | inflight@^1.0.4: 453 | version "1.0.6" 454 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 455 | dependencies: 456 | once "^1.3.0" 457 | wrappy "1" 458 | 459 | inherits@2, inherits@^2.0.1, inherits@~2.0.1: 460 | version "2.0.3" 461 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 462 | 463 | inherits@2.0.1: 464 | version "2.0.1" 465 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" 466 | 467 | inline-source-map@~0.6.0: 468 | version "0.6.2" 469 | resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5" 470 | dependencies: 471 | source-map "~0.5.3" 472 | 473 | insert-module-globals@^7.0.0: 474 | version "7.0.1" 475 | resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.1.tgz#c03bf4e01cb086d5b5e5ace8ad0afe7889d638c3" 476 | dependencies: 477 | JSONStream "^1.0.3" 478 | combine-source-map "~0.7.1" 479 | concat-stream "~1.5.1" 480 | is-buffer "^1.1.0" 481 | lexical-scope "^1.2.0" 482 | process "~0.11.0" 483 | through2 "^2.0.0" 484 | xtend "^4.0.0" 485 | 486 | is-buffer@^1.1.0: 487 | version "1.1.4" 488 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" 489 | 490 | isarray@^1.0.0, isarray@~1.0.0: 491 | version "1.0.0" 492 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 493 | 494 | isarray@~0.0.1: 495 | version "0.0.1" 496 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 497 | 498 | json-stable-stringify@~0.0.0: 499 | version "0.0.1" 500 | resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45" 501 | dependencies: 502 | jsonify "~0.0.0" 503 | 504 | jsonify@~0.0.0: 505 | version "0.0.0" 506 | resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" 507 | 508 | jsonparse@^1.2.0: 509 | version "1.2.0" 510 | resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.2.0.tgz#5c0c5685107160e72fe7489bddea0b44c2bc67bd" 511 | 512 | labeled-stream-splicer@^2.0.0: 513 | version "2.0.0" 514 | resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59" 515 | dependencies: 516 | inherits "^2.0.1" 517 | isarray "~0.0.1" 518 | stream-splicer "^2.0.0" 519 | 520 | lexical-scope@^1.2.0: 521 | version "1.2.0" 522 | resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0afaf22df4" 523 | dependencies: 524 | astw "^2.0.0" 525 | 526 | lodash.memoize@~3.0.3: 527 | version "3.0.4" 528 | resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" 529 | 530 | miller-rabin@^4.0.0: 531 | version "4.0.0" 532 | resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" 533 | dependencies: 534 | bn.js "^4.0.0" 535 | brorand "^1.0.1" 536 | 537 | minimalistic-assert@^1.0.0: 538 | version "1.0.0" 539 | resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" 540 | 541 | "minimatch@2 || 3", minimatch@^3.0.2: 542 | version "3.0.3" 543 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" 544 | dependencies: 545 | brace-expansion "^1.0.0" 546 | 547 | minimist@0.0.8: 548 | version "0.0.8" 549 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 550 | 551 | minimist@^1.1.0: 552 | version "1.2.0" 553 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 554 | 555 | mkdirp@0.5.x: 556 | version "0.5.1" 557 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 558 | dependencies: 559 | minimist "0.0.8" 560 | 561 | module-deps@^4.0.8: 562 | version "4.0.8" 563 | resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.0.8.tgz#55fd70623399706c3288bef7a609ff1e8c0ed2bb" 564 | dependencies: 565 | JSONStream "^1.0.3" 566 | browser-resolve "^1.7.0" 567 | cached-path-relative "^1.0.0" 568 | concat-stream "~1.5.0" 569 | defined "^1.0.0" 570 | detective "^4.0.0" 571 | duplexer2 "^0.1.2" 572 | inherits "^2.0.1" 573 | parents "^1.0.0" 574 | readable-stream "^2.0.2" 575 | resolve "^1.1.3" 576 | stream-combiner2 "^1.1.1" 577 | subarg "^1.0.0" 578 | through2 "^2.0.0" 579 | xtend "^4.0.0" 580 | 581 | ms@0.7.2: 582 | version "0.7.2" 583 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" 584 | 585 | once@^1.3.0: 586 | version "1.4.0" 587 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 588 | dependencies: 589 | wrappy "1" 590 | 591 | os-browserify@~0.1.1: 592 | version "0.1.2" 593 | resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.1.2.tgz#49ca0293e0b19590a5f5de10c7f265a617d8fe54" 594 | 595 | pako@~0.2.0: 596 | version "0.2.9" 597 | resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" 598 | 599 | parents@^1.0.0, parents@^1.0.1: 600 | version "1.0.1" 601 | resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" 602 | dependencies: 603 | path-platform "~0.11.15" 604 | 605 | parse-asn1@^5.0.0: 606 | version "5.0.0" 607 | resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23" 608 | dependencies: 609 | asn1.js "^4.0.0" 610 | browserify-aes "^1.0.0" 611 | create-hash "^1.1.0" 612 | evp_bytestokey "^1.0.0" 613 | pbkdf2 "^3.0.3" 614 | 615 | path-browserify@~0.0.0: 616 | version "0.0.0" 617 | resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" 618 | 619 | path-is-absolute@^1.0.0: 620 | version "1.0.1" 621 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 622 | 623 | path-platform@~0.11.15: 624 | version "0.11.15" 625 | resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" 626 | 627 | pbkdf2@^3.0.3: 628 | version "3.0.9" 629 | resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693" 630 | dependencies: 631 | create-hmac "^1.1.2" 632 | 633 | process-nextick-args@~1.0.6: 634 | version "1.0.7" 635 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 636 | 637 | process@~0.11.0: 638 | version "0.11.9" 639 | resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1" 640 | 641 | public-encrypt@^4.0.0: 642 | version "4.0.0" 643 | resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" 644 | dependencies: 645 | bn.js "^4.1.0" 646 | browserify-rsa "^4.0.0" 647 | create-hash "^1.1.0" 648 | parse-asn1 "^5.0.0" 649 | randombytes "^2.0.1" 650 | 651 | punycode@1.3.2: 652 | version "1.3.2" 653 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" 654 | 655 | punycode@^1.3.2: 656 | version "1.4.1" 657 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" 658 | 659 | querystring-es3@~0.2.0: 660 | version "0.2.1" 661 | resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" 662 | 663 | querystring@0.2.0: 664 | version "0.2.0" 665 | resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" 666 | 667 | randombytes@^2.0.0, randombytes@^2.0.1: 668 | version "2.0.3" 669 | resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" 670 | 671 | read-only-stream@^2.0.0: 672 | version "2.0.0" 673 | resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" 674 | dependencies: 675 | readable-stream "^2.0.2" 676 | 677 | readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@^2.1.5: 678 | version "2.2.2" 679 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" 680 | dependencies: 681 | buffer-shims "^1.0.0" 682 | core-util-is "~1.0.0" 683 | inherits "~2.0.1" 684 | isarray "~1.0.0" 685 | process-nextick-args "~1.0.6" 686 | string_decoder "~0.10.x" 687 | util-deprecate "~1.0.1" 688 | 689 | readable-stream@~2.0.0: 690 | version "2.0.6" 691 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" 692 | dependencies: 693 | core-util-is "~1.0.0" 694 | inherits "~2.0.1" 695 | isarray "~1.0.0" 696 | process-nextick-args "~1.0.6" 697 | string_decoder "~0.10.x" 698 | util-deprecate "~1.0.1" 699 | 700 | reflect-metadata@^0.1.2: 701 | version "0.1.9" 702 | resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.9.tgz#987238dc87a516895fe457f130435ffbd763a4d4" 703 | 704 | resolve@1.1.7: 705 | version "1.1.7" 706 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" 707 | 708 | resolve@^1.1.3, resolve@^1.1.4: 709 | version "1.2.0" 710 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" 711 | 712 | ripemd160@^1.0.0: 713 | version "1.0.1" 714 | resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" 715 | 716 | rxjs@^5.0.0-beta.2: 717 | version "5.0.2" 718 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.0.2.tgz#cc6513756daa93cab4085c1b5a19a3e28fb6c6bf" 719 | dependencies: 720 | symbol-observable "^1.0.1" 721 | 722 | sax@0.5.x: 723 | version "0.5.8" 724 | resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" 725 | 726 | sha.js@^2.3.6, sha.js@~2.4.4: 727 | version "2.4.8" 728 | resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" 729 | dependencies: 730 | inherits "^2.0.1" 731 | 732 | shasum@^1.0.0: 733 | version "1.0.2" 734 | resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" 735 | dependencies: 736 | json-stable-stringify "~0.0.0" 737 | sha.js "~2.4.4" 738 | 739 | shell-quote@^1.4.3: 740 | version "1.6.1" 741 | resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" 742 | dependencies: 743 | array-filter "~0.0.0" 744 | array-map "~0.0.0" 745 | array-reduce "~0.0.0" 746 | jsonify "~0.0.0" 747 | 748 | source-map@0.1.x: 749 | version "0.1.43" 750 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" 751 | dependencies: 752 | amdefine ">=0.0.4" 753 | 754 | source-map@~0.5.3: 755 | version "0.5.6" 756 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" 757 | 758 | stream-browserify@^2.0.0: 759 | version "2.0.1" 760 | resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" 761 | dependencies: 762 | inherits "~2.0.1" 763 | readable-stream "^2.0.2" 764 | 765 | stream-combiner2@^1.1.1: 766 | version "1.1.1" 767 | resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" 768 | dependencies: 769 | duplexer2 "~0.1.0" 770 | readable-stream "^2.0.2" 771 | 772 | stream-http@^2.0.0: 773 | version "2.5.0" 774 | resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.5.0.tgz#585eee513217ed98fe199817e7313b6f772a6802" 775 | dependencies: 776 | builtin-status-codes "^2.0.0" 777 | inherits "^2.0.1" 778 | readable-stream "^2.1.0" 779 | to-arraybuffer "^1.0.0" 780 | xtend "^4.0.0" 781 | 782 | stream-splicer@^2.0.0: 783 | version "2.0.0" 784 | resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83" 785 | dependencies: 786 | inherits "^2.0.1" 787 | readable-stream "^2.0.2" 788 | 789 | string_decoder@~0.10.0, string_decoder@~0.10.x: 790 | version "0.10.31" 791 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 792 | 793 | stylus@^0.54.5: 794 | version "0.54.5" 795 | resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.5.tgz#42b9560931ca7090ce8515a798ba9e6aa3d6dc79" 796 | dependencies: 797 | css-parse "1.7.x" 798 | debug "*" 799 | glob "7.0.x" 800 | mkdirp "0.5.x" 801 | sax "0.5.x" 802 | source-map "0.1.x" 803 | 804 | subarg@^1.0.0: 805 | version "1.0.0" 806 | resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" 807 | dependencies: 808 | minimist "^1.1.0" 809 | 810 | symbol-observable@^1.0.1: 811 | version "1.0.4" 812 | resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" 813 | 814 | syntax-error@^1.1.1: 815 | version "1.1.6" 816 | resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.1.6.tgz#b4549706d386cc1c1dc7c2423f18579b6cade710" 817 | dependencies: 818 | acorn "^2.7.0" 819 | 820 | through2@^2.0.0: 821 | version "2.0.3" 822 | resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" 823 | dependencies: 824 | readable-stream "^2.1.5" 825 | xtend "~4.0.1" 826 | 827 | "through@>=2.2.7 <3": 828 | version "2.3.8" 829 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 830 | 831 | timers-browserify@^1.0.1: 832 | version "1.4.2" 833 | resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" 834 | dependencies: 835 | process "~0.11.0" 836 | 837 | to-arraybuffer@^1.0.0: 838 | version "1.0.1" 839 | resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" 840 | 841 | tty-browserify@~0.0.0: 842 | version "0.0.0" 843 | resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" 844 | 845 | typedarray@~0.0.5: 846 | version "0.0.6" 847 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 848 | 849 | typescript@^2.1.4: 850 | version "2.1.4" 851 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.1.4.tgz#b53b69fb841126acb1dd4b397d21daba87572251" 852 | 853 | umd@^3.0.0: 854 | version "3.0.1" 855 | resolved "http://registry.npmjs.org/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" 856 | 857 | url@~0.11.0: 858 | version "0.11.0" 859 | resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" 860 | dependencies: 861 | punycode "1.3.2" 862 | querystring "0.2.0" 863 | 864 | util-deprecate@~1.0.1: 865 | version "1.0.2" 866 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 867 | 868 | util@0.10.3, util@~0.10.1: 869 | version "0.10.3" 870 | resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" 871 | dependencies: 872 | inherits "2.0.1" 873 | 874 | vm-browserify@~0.0.1: 875 | version "0.0.4" 876 | resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" 877 | dependencies: 878 | indexof "0.0.1" 879 | 880 | wrappy@1: 881 | version "1.0.2" 882 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 883 | 884 | xtend@^4.0.0, xtend@~4.0.1: 885 | version "4.0.1" 886 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 887 | 888 | zone.js@^0.7.4: 889 | version "0.7.4" 890 | resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.4.tgz#0e624fe5b724450ee433495deff15c02b3908ee0" 891 | --------------------------------------------------------------------------------